Java构造器和初始化块
公众号: 菜鸡干Java
构造器是一个特殊的方法,但定义构造器和普通方法没什么太大区别,该有的都有。不过为了区分还是看看不一样的地方。
- 方法名:构造器方法名需要和类名一样
- 返回值:构造器不定义返回值,也不用返回
void
,但是它会返回一个对象(隐式的) - 修饰符:一般设为
public
权限,可以被其他方法调用 - 不是静态的:构造器方法不用
static
修饰
在定义一个类时,没有写构造器,系统将默认提供一个无参构造器。构造器是创建一个对象的途径之一。上面的第四点很少有书提到,但是我们也很少见到用static
修饰的构造器—静态构造器。这里先讲一个知识点:在static
修饰的方法中如果使用this
关键字,则关键字无法指向对象。而访问非静态成员是隐式访问,在没有重名下,省略了this
关键字,所以静态成员是无法访问非静态成员。回到static
修饰,构造器如果用静态的,则不能访问非静态成员,而构造器是用来初始化成员变量的,那怎么初始化,怎么创建对象?最后还要返回对象。其次,创建对象会比较麻烦,new
类名加构造器是不可想象的!
既然构造器不用类名加构造器访问,开始又没有实例对象,那如何访问呢?因此,Java提供关键字new
来调用构造器,所以在构造器中关键字this
表示构造器正在初始化的对象,当然大多数情况下可以省略,除了构造器中有一个重名的局部变量的情况下。
public MyConstructor(){
int son = 0;
this.son = 6;//构造器会把所有对象的son属性初始化为6
}
构造器的作用
主要作用还是为了初始化
,默认初始化把所有数字基本类型实例变量设为0,布尔类型为false
,引用类型为null
,如果想改变一下,可以在构造器中定义。
public MyConstructor(String name,int age){
this.name = name;//传入两参数
this.age = age;
}
需要注意的是构造器不是全权负责创建对象,在执行构造器之前,系统已为对象分配了内存,结束后构造器返回对象,让引用指向该对象。
构造器重载
如果想保留无参构造器,可以提供多个构造器,形成构造器重载。重载后,构造器的参数列表是不一样的,这样能利用不同的构造器创建不同的对象。如果包含多个构造器,其中一个构造器代码包含另一个,如下。这种情况是可以有简洁的代码代替的,但构造器不能直接被调用,用new
关键字又会创建一个对象,则使用this
关键字很好解决,this
调用另一个重载的构造器。此种方法仅限在构造器中使用
public class dog{
public String name;
public int age;
public int weight;
public dog(){}
public dog(String name,int age){
this.name = name;
this.age = age;
}
public dog(String name,int age,int weight){
this(name,age);//调用另一个构造器的初始化代码
//其实还可以用以下代码代替,但是不建议
//this.name = name;
//this.age = age;
this.weight = weight;
}
}
调用父类构造器
子类继承父类不会获得父类的构造器,但子类构造器可以调用父类构造器的初始化代码,类似上面的调用另一个重载构造器。在一个构造器中调用另一个构造器用this
完成,在子类构造器中调用父类构造器则用super
完成。super
调用必须出现在子类构造器的第一行,因为Java设计子类执行构造器必须先调用父类构造器,所以它是第一行,然后,然后就没this
啥事了,因为刚才讲了,再this
调用又要执行父类构造器。this
与super
不能同时使用!
子类继承了父类的属性和方法,所以在先初始化父类的属性和方法,这样子类才可以初始化自己特有的,因为java中不允许调用没有初始化的成员。 this
就是调用本类的其他构造函数,在其他构造函数中也有默认的super(),或者自定义了带参的super
,这样就初始化了父类的成员了,所以写了this
的构造函数不能再写super
了,因为实例化一个对象运行两次super
是不安全的。this
放在第一行,也是因为要先初始化父类和this代表的构造函数先,因为当前构造函数可能用到那些成员,所以那些成员得要先初始化
不管是否使用super
调用父类构造器,子类总会调用父类构造器一次,有以下情况:
- 子类构造器使用
super
显式调用父类构造器 - 子类构造器使用
this
调用重载构造器,执行前重载构造器先会隐式调用父类构造器 - 子类构造器既无
super
调用,也没有this
调用,则系统默认调用父类无参构造器
创建任何对象时,总是从该类所在继承树最顶层类的构造器开始执行,然后往下执行到本类构造器。如果父类构造器还调用了重载构造器,那就会依次执行多个构造器。
初始化块
跟构造器作用差不多,初始化块也可以进行对象初始化,它比构造器先执行。它也是类的一种成员,修饰符只能用static
修饰,被修饰称为静态初始化块,也可以不修饰。
{
a = 6;
}
int a = 9;
public test(){
System.out.println(new MyInstance().a);
}//输出9,实例变量值为9
类和对象无法调用初始化块,初始化块只在创建对象时隐式执行,而且在构造器之前执行。此外,普通初始化块、声明实例变量指定默认值,都可认为是对象的初始化块,执行顺序和排列顺序一样。
当创建一个对象时,系统先为对象的所有实例变量分配内存,接着程序开始对实例变量初始化,初始化顺序是:先执行初始化块或声明实例变量时指定初始值,再执行构造器中的初始值。
初始化块和构造器
与构造器不同,初始化块是一段固定的代码,不接收参数,因此初始化块对同一个类的所有对象的初始化处理是一样的。所以就这种特点,我们可以利用来初始化相同一段值。构造器中相同一段代码可以提炼出相同初始化块,简化了程序,提高了复用性和可维护性。
实际上在编译Java块后,初始化块会消失,初始化块的代码会被“还原”到构造器中,且位于构造器代码前面!
与构造器类似,创建一个Java对象,不仅会执行该类的普通初始化块和构造器,而且系统会从Object
类开始执行,往下执行父类,然后才到该类的初始化块和构造器。如果希望类加载后对整个类进行初始化操作,例如把类变量初始化一下,则需要用静态初始化块。
静态初始化块
也叫类初始化块,属于类的静态成员,同样需要遵循静态成员不能访问非静态成员的规则
。普通初始化块负责对对象执行初始化,类初始化块则负责对类进行初始化。类初始化时,系统执行静态初始化块。静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是创建对象时才执行,因此静态初始化块总是比普通初始化块先执行。静态初始化块不能对实例变量进行初始化处理。
与普通初始化块类似,系统执行类的静态初始化块不仅会执行本类的静态初始化块,还会从源头类Object
类开始执行,道理一样。类初始化后,才能使用这个类,然后才能创建对象。一旦类初始化成功,该类在JVM中一直存在,所以创建实例时,无须对类进行初始化。创建实例时,都需要先执行顶层父类的初始化块、构造器,然后执行父类的初始化块、构造器,然后执行本类的初始化块和构造器。静态初始化块->普通初始化块->构造器
Java系统加载并初始化某个类时,总是保证该类的所有父类全部加载和初始化!静态初始化块和声明静态变量时所指定的初始值都是该类的初始化代码。JVM第一次主动调用某个类,系统会在类准备阶段为该类所有静态成员分配内存,初始化阶段负责初始化。
public class Test{
static { //(1)
a = 6;
}
static int a = 9;//(2)
public static void main(String[] args){
System.out.println(Test.a);
}//输出9,如果调换(1)(2)则为6
}