为什么构造函数中对this()或super()的调用必须放在第一行呢?
---------------
有定义如下:
构造方法可以调用类中其他 重载的构造方法。如果构造方法调用了另一种构造方法,对this()的调用必须出现在第一条语句中。
---------------
有定义如下:
构造方法可以调用类中其他 重载的构造方法。如果构造方法调用了另一种构造方法,对this()的调用必须出现在第一条语句中。
调试欢乐多
实例化一个对象,同时会加载它所有的父类.
super()放在第一行的目的是为了完全初始化父类构造方法后再初始化子类构造方法,这样做是有原因的,假如子类构造方法里用到了父类的一个成员变量,这就保证了子类用到的是经过父类完全初始化后的变量!
this()也是同样的原因,只不过是发生在同一个类中而已!
int age = 0; public Person() {
System.out.println("2、然后执行了super");
} Person(int aged){
System.out.println("******");
} public void fun() {
System.out.println("super.age=" + age);
}
} public class Test4 extends Person {
static {
System.out.println("1、先执行了static区域");
} int age = 10; public Test4() {
super(3);
super.fun();
System.out.println("3、然后执行了this() age=" + age);
} public Test4(int age) {
this(100,100); //当Test(int a,int b) 没有super(),和有super() 的时候都会调用父类的构造函数
// 当有Test(int a int b)方法中有super(参数) 的时候就不会调用父类无参的构造函数了。
// 而去调用父类带参的构造函数了。
//this () // 用到了子类的无参的构造函数。
// super(2,3);
// this(); // this(),调用本类的构造函数,super(),调用父类的构造函数都必须是第一条语句,理所当然,
//二者不能同时出现,也不能调用两个构造函数this(),this(参数),super(),super(参数).
super.fun();
System.out.println("4、然后执行了this(int age) 年龄是:" + age);
}
Test4(int a,int b){
super(); // 要不要这句都可以,都会自动执行父类无参的构造函数,
// 由于父类中还定义了带参的构造函数,此处用到无参的构造方法,无参的构造函数必须显示定义,
//super(2); //这句不能和super() 同时出现,当定义这个构造函数时,将会调用带参的构造函数。
System.out.println("a= "+a+" b="+b);
} public static void main(String[] args) {
Test4 test = new Test4(20);
Test4 test2=new Test4(1,2);
}
}
/*
* 程序分析:
* 当类存在继承关系的时候,当调用子类构造函数的时候,当子类构造方法体没有super()
* 或者super()没有参数的时候 ,就会自动调用父类的无参构造函数,当父类没有定义构造函数的时候,系统默认无参的构造函数。
* 当子类有super(参数),就会匹配父类带参数的构造方法,而不再去执行没有带有参数的构造函数了。
* 当子类调用父类带参的构造函数时候,必须显示的说明,super(参数),否则,编译器报错:
* 未定义隐式超级构造函数 XXXX ,必须显式调用另一个构造函数 */
个人的理解如下:为什么要先调super呢
我们知道子类会继承基类的非private的方法和属性,在基类的构造函数中有可能包含初始化基类属性的代码,我们构造子类的时候,我们更关心的是子类的属性初始化,其中包括被子类继承的基类的属性,假设我们先调用子类构造函数的其它代码,然后再调用基类的构造函数,有可能会造成继承基类的属性值被基类构造函数重设,我想这肯定不是大伙的初衷,所以为了避免程序员误操作出现这样的事情,JAVA设定只能先调用基类的构造函数。那为什么要 先在第一行调this(参数列表)等其它的构造函数呢
其实原因也类似上面的,如果被调用的其它构造函数已经设了某些属性的值,而不把调用其它构造函数放在第一行,也会导致某些属性值被覆盖,这也不是我们想看到的,因为如果想让别的构造函数来覆盖你设的值,那还不如你事先就不设值。至于有些朋友说的是由于调函数的关系,这应该是不对的,因为类的设计中,本来就不应该在构造函数中调用任何本身或基类的非构造函数方法,如果你调了,那本身就是一种不好的设计。
private String str;
private int value;
public Test(String str) {
this(str, 0);
this.value = 0; // 毫无意义的赋值,测试用
}
public Test(String str, int value) {
this.str = str;
this.value = value;
}
}编译后使用 javap -c Test 查看虚拟机指令:public class Test extends java.lang.Object{
public Test(java.lang.String);
Code:
0: aload_0
1: aload_1
2: iconst_0
3: invokespecial #1; //Method "<init>":(Ljava/lang/String;I)V
6: aload_0
7: iconst_0
8: putfield #2; //Field value:I
11: returnpublic Test(java.lang.String, int);
Code:
0: aload_0
1: invokespecial #3; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #4; //Field str:Ljava/lang/String;
9: aload_0
10: iload_2
11: putfield #2; //Field value:I
14: return
}先看看 Test(String, int) 的构造,默认先调用 Object 的初始化构造生成对象,
再一个一个地设置值,这是普通的构造方法。再看看 Test(String) 的构造,这里并傻乎乎地调用 Object 的初始化构造,而是
直接调用 Test(String, int),因为编译器知道在 Test(String, int) 中已经初始
化过一次了,没有必要再进行初始化了。如果改成:
public Test(String str) {
this.value = 0;
this(str, 0);
}这样的话,构造一个对象就会产生两个对象了,因为 Test(String str) 会调用父类
Object 的构造一次,而 this(str, 0) 又会再调用一次,这显然是不符合逻辑的。把 super() this() 这些放在第一行的话,编译器会在第一时间进行检查,如果发现
这两个中的一个,就不会再调用超类的 Object 的构造产生对象了,而是直接调用这
些构造了,如果没有看到这些,才会自己去调用 Object 的构造来产生对象。