java中的构造方法

时间:2022-07-05 19:31:44

在多数情况下,初始化一个对象的最终步骤是去调用这个对象的构造方法。构造方法负责对象的初始化工作,为实例变量赋予合适的初始值。构造方法必须满足一下语法规则:

1.方法名必须和类名相同

2.不要声明返回的类型

3.不能被static final.abstract和native修饰,构造方法不能被子类继承,所以用final和abstract修饰没有意义,构造方法用于初始化一个新建的对象,所以用static修饰没有意义,多个线程不会同时创建内存地址相同的同一个对象,因此用synchronized修饰没有必要.此外,java语言不支持native类型的构造方法

这里加点题外话一:static :1.静态方法

                                     通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。声明为static的方法有以下限制

                                     一.它们仅能调用其他的 static方法

                                     二.它们只能访问static数据

                                     三.它们不能以任何方式引用this或super

          class Simple{

      static void go(){

     System.out.println("welcome");

}

}

public class Cal{

public static void main(String[] args){

Simple.go();

}

        }      

调用一个静态方法就是“类名.方法名”,静态方法的使用很简单如上所示。一般来说,静态方法常常为应用程序中的其它类提供一些实用工具所用,在java的类库中大量的静态方法正是出于此目的而定义的.

  2.静态变量

     声明为static的变量实质上就是全局变量,当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量,静态变量与静态方法类似,所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操作此块存储空间,当然对于final则另当别论了

 class Value{

 static int c=0; 

 static void inc(){

c++;}

}

public class Count2{

public static vois main(String[] args){

Value v1,v2;

v1=new Value();

v2=new Value();

System.out.print(v1.c+v2.c);

v1.inc();

System.out.print(v1.c+v2.c);

}

}

结果是v1.c=0 v2.c=0   v1.c=1 v2.c=1

由此可以证明的就是它们共享一块存储区

值得探讨的是静态变量的初始化问题

   如果你需要通过计算初始化你的static变量,你可以声明一个static块,static块仅在该类加载时执行一次,下面的例子显示有一个static方法,一些static变量,以及一个static初始块:

class Value3{

 static int c=0;

 Value3(){

c=15;

}

Value3(int i){

c=i;

}

static void inc(){

c++;

}

}

public class Count{

public static void prt(String s){

System.out.println(s);

}

}

Value3 v=new Value3(10);

static Value3 v1,v2;

static{即为static 块

prt(v1.c+v2.c);

v1=new Value3(27);

prt(v1.c+v2.c);

v2=new Value3(15);'

prt(v1.c+v2.c);

}

public static void main(String[] args){

Count ct=new Count();

prt(ct.v.c);

prt(v1.c+v2.c);

v1.inc();

prt(v1.c+v2.c);

prt(ct.v.c);

}

结果是:

v1.c=0  v2.c=0

v1.c=27  v2.c=27

v1.c=15  v2.c=15

ct.c=10

v1.c=10  v2.c=10

v1.c=11  v2.c=11

ct.c=11

这个程序展示了静态初始化的各种特性。如果你初次接触Java,结果可能令你吃惊,可能会对static后加大括号感到困惑。首先我要告诉你的是,static定义的变量会优先任何的其他非static变量,不论顺序如何,正如在程序中看到的一样,虽然v在v1与v2的前面,但是结果是v1和v2的初始化在v的前面,在static后面的代码,这是用来进行显示静态变量的初始化,这段代码只会初始化一次,并且在类被第一次装载时,如果你读懂了并理解这段代码,会帮你对static关键字的认识,在涉及到继承的时候,会先初始化父类的static变量,然后在子类的,依次类推

   二,final关键字

   在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。

   1.修饰类

    当用final修饰类时,表明这个类不能被继承,也就是说如果一个类你永远你不会让它继承,就可以用 final修饰,final类中的成员变量可以根据需要设为final,但是要注意的是final类中的所有成员方法都会被隐式地指定为final方法

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

2.修饰方法

 “使用final方法的原因有2个,第一个原因是把这个方法锁定,防止任何继承类修改它的含义,第二个是效率,

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

3.修饰变量

修饰变量是final用的最多的地方

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象

class man{

private final int i=0;

public Man(){

i=1;

final Object obj=new Object();

obj=new Object();

}

}

上面这段代码,对于i与obj的重新赋值都会报错

深入理解final关键字

  1.当用 final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前进行赋值即可),而且final变量一旦被初始化赋值之后就不能在被赋值了

public class Test {
public static void main(String[] args) throws Exception { 
 String a="hello2";
 final String b="hello";
 String d="hello";
 String c=b+2;
 String e=d+2;
 System.out.println(c);
 System.out.println(e);

}

}

大家可以先想一下这道题的输出结果。为什么第一个比较结果为true,而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的  值。而对于变量d的访问却需要在运行时通过链接来进行,不过要注意的是,只有当编译期间确切知道final变量值的情况时,编译器才会进行优化

public class Test {
     public static void main(String[] args)  {
         String a = "hello2"
         final String b = getHello();
         String c = b + 2
         System.out.println((a == c));
 
     }
     
     public static String getHello() {
         return "hello" ;
     }
}
这段代码输出是false

2.被修饰的final修饰的引用变量指向的对象内容可变

3.final与static

很多时候我们容易 把static与final混淆,static只保存一份副本,而final的作用是保存变量不可变,看下面的例子

public class Test {
     public static void main(String[] args)  {
         MyClass myClass1 = new MyClass();
         MyClass myClass2 = new MyClass();
         System.out.println(myClass1.i);
         System.out.println(myClass1.j);
         System.out.println(myClass2.i);
         System.out.println(myClass2.j);
 
     }
}
 
class MyClass {
     public final double i = Math.random();
     public static double j = Math.random();
}

运行这段代码,每次打印的j的值都是一样的,而i的值却不相同,

废话不多了,还是言归正传把

在以下的Sample类中,具有int返回类型的Sample(int x)方法只是个普通的方法,不能作为构造方法

public class Sample{

private int x;

public Sample(){不带参数的构造方法

this(1);

}

public Sample(int x){带参数的构造方法

this.x=x;

}

public int Sample(int x){不是构造方法

return x++;

}

}

以上例子尽管能编译通过,但是使用实例方法与构造方法同名,不是好的编程习惯,容易造成混淆,例如以下Mystery()类的Mystery()方法有void返回类型,由此不是构造方法

public class Mystery {

private String s;

public void Mystery() { //不是构造方法

s = "constructor";

}

void go() {

System.out.println(s);

}

public static void main(String[] args) {

Mystery m = new Mystery();

m.go();

}

}

以上程序输出的是null,因为new语句创建Mystery实例时,调用的是Mystery类的默认的方法,而不是以上有void返回类型的Mystery()方法

11.2.1 重载构造方法

当通过new语句创建一个对象时,在不同的条件下,对象可能会有不同的初始化行为,例如对于公司新来的一个雇员,在一开始的时候,有可能不知道他的姓名和年龄是未知的,也有可能仅仅是他的姓名是已知的,也有可能姓名和年龄是已知的,如果姓名是未知的,那就叫无名氏,如果年龄是未知的,那就暂且把年龄设为-1。

可通过重载方法来表达对象的多种初始化行为,java中允许一个构造方法中,用this用英语来调用另外一个构造方法

例程11-2 Employee.java

public class Employee {

private String name;

private int age;

/** 当雇员的姓名和年龄都已知,就调用此构造方法 */

public Employee(String name, int age) {

this.name = name;

this.age=age;

}

/** 当雇员的姓名已知而年龄未知,就调用此构造方法 */

public Employee(String name) {

this(name, -1);

}

/** 当雇员的姓名和年龄都未知,就调用此构造方法 */

public Employee() {

this( "无名氏" );

}

public void setName(String name){this.name=name; }

public String getName(){return name; }

public void setAge(int age){this.age=age;}

public int getAge(){return age;}

}

例子中,this(name,-1)语句用来调用Employee(String name,int age)构造方法,在Employee()构造方法中,this(无名氏)语句用来调用Employee(String name)构造方法

用this语句来调用其他构造方法时,必须遵守以下语法规则。

l 假如在一个构造方法中使用了this语句,那么它必须作为构造方法的第一条语句(不考虑注释语句)。以下构造方法是非法的:

public Employee(){

String name="无名氏";

this(name); //编译错误,this语句必须作为第一条语句

}

l 只能在一个构造方法中用this语句来调用类的其他构造方法,而不能在实例方法中用this语句来调用类的其他构造方法。

l 只能用this语句来调用其他构造方法,而不能通过方法名来直接调用构造方法。以下对构造方法的调用方式是非法的:

public Employee() {

String name= "无名氏";

Employee(name); //编译错误,不能通过方法名来直接调用构造方法

}

11.2.2默认的构造方法

默认构造方法是没有参数的构造方法1.隐含默认的构造方法 2.程序显示定义的默认构造方法

在java语言中,每个类至少有一个构造方法,为了保证这一点,如果用户定义的类中没有提供任何构造方法,那么java语言将自动提供一个隐含的默认构造方法,该构造方法没有参数,用public修饰,而且方法体为空,格式如下

public ClassName(){}//隐含的默认构造方法

在程序中也可以显式地定义默认构造方法,它可以是任意的访问级别。例如:

protected Employee() { //程序显式定义的默认构造方法

this("无名氏");

}

如果类中显式定义了一个或多个构造方法,并且所有的构造方法都有参数,那么这个类就失去了默认构造方法,在一下程序中,Sample1类中隐含一个默认的构造方法,SamPle2类没有默认的构造方法,Sample3有一个显式默认构造方法

public class Sample1{}

public Class Sample2{

public class Sample2(int a){

system,.out.println("aa");

}

public class Sample3{

public class Sample3(){

System.out.print("aa");

}

}

}

可以调用Sample1类的默认构造方法来创建Sample1对象

Sample1 s=new Sample1();//合法

由于Sample2 没有默认的构造方法

Sample2 s=new Sample2();//编译出错

Sample3有显式定义了默认的构造方法,因此语句合法

Sample3 s=new Sample3();