目录
1.子类一定会调用父类的构造器
2.如果不调用父类的构造器,会发生什么?
3.构造器的作用
4.什么是初始化
5.属性赋值的方法
6.构造器赋值和 初始化区别
1.子类一定会调用父类的构造器
来看下面这个图,创建一个子类ChildExample 继承父类ParentExample,什么都不做,然后我编译,看到编译后的字节码中出现初始化父类的命令。说明子类中有一个默认的构造器super()调用了父类的构造器。
2.如果不调用父类的构造器,会发生什么?
我们来看下面一段代码,很简单的一段代码,子类调用父类的属性,分别输出,10,20。那么假设没有调用父类的构造器,会发生什么?或者说输出的值是什么?输出的结果应该是0,0。这是为什么呢?我们接着往下看。
public class ChildExample extends ParentExample{
public static void main(String[] args) {
ChildExample childExample = new ChildExample();
System.out.println(childExample.a);
System.out.println(childExample.b);
}
}
public class ParentExample {
int a = 10;
int b = 20;
}
在回答上个输出的问题之前,我们先要明确几点,构造器的作用,什么是初始化。
3.构造器的作用
简单来说就是一句话,构造器有两个作用,创建对象和初始化对象的属性。
4.什么是初始化
回答初始化之前我们先看看对象的创建过程,相信看完之后就会很清楚了。这里拿我之前写的
面试官的一道简单的单例模式问题给我问懵了,详解单例模式双重检查加锁为什么要加volatile关键字!https://blog.csdn.net/benbenniaono1/article/details/105763029 这篇文章的对象的创建过程的例子来说一下:
对象的创建过程主要分成三步,如下图展示的汇编码所示,主要是0,4,7这三步。
0 这步是为新创建的对象申请内存,但是此时对象中的成员变量的值是默认的值(半初始化),即下图a 的值此时是0;
4 初始化对象,在这步才把10赋给成员变量a
7 建立关联,把testDemo引用和new 出来的TestDemo对象建立关联
从这里我们可以看出初始化其实就是一个显示赋值的过程,把10赋给原先半初始化的a,此时的a=0,赋值之后a=10。这里也稍微说以下我对new关键字的一些个人理解,我认为new 关键字是申请了一块内存地址,至于这块地址放的谁,就是对应类的构造器来决定的了。
到这里我们已经可以回答之气的输出问题和子类为什么要调用父类的构造器了,初始化对象的属性,准确的说是为对象的属性显示赋值。
public class TestDemo {
private int a = 10;
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
// 0 new #2 <company/syncronized/TestDemo> 申请内存,半初始化,此时a的值是0(当对象刚new出来的时候会给里面的成员变量设置默认初始值,int类型的初始值是0)
// 3 dup 复制
// 4 invokespecial #3 <company/syncronized/TestDemo.<init>> 初始化,在这步把10赋给a,此时a的值是10
// 7 astore_1 testDemo和new TestDemo()建立关联
// 8 return
}
}
5.6两点是一个补充,问题到第四点已经是解决了,为了得到父类显示赋值之后的属性值。
5.属性赋值的方法
上面谈到显示赋值,那么有几种赋值的方式呢?有四种,分别是隐式赋值,显式赋值,构造器赋值和普通方法赋值。
它们的执行顺序:隐式赋值>显式赋值>构造器赋值>方式属性赋值
隐式赋值:没有=赋值的情况下会默认赋上对应类型的默认值,int 是0,Object 是 null 等等。
显式赋值:=号直接赋值
构造器赋值:通过给有参构造器传值赋值
普通方法赋值:通过给普通方式传值赋值
public class ChildExample extends ParentExample{
//2.显示赋值
int a = 10;
public ChildExample(int a) {
//3.构造器赋值
this.a = a;
}
//普通方法赋值
public void setValue(int value){
this.a = value;
}
public static void main(String[] args) {
ChildExample childExample = new ChildExample(1);
}
}
6.构造器赋值和 初始化区别
刚开始的时候我老是把初始化和构造器赋值搞混,认为初始化就是给有参构造器传个值,然后属性值就变成我传的值,其实不是这样的。初始化可以认为是显示赋值,其实是将对应类型的隐式赋值用显示赋值覆盖的过程。而构造器赋值,需要调用有参构造器,然后通过 this.a = a;这句赋值语句来完成赋值,构造器赋值只限于有参构造器。但是初始化无论无参,有参构造器都是存在这样一个过程。