代码
class A {
public static String a1 = "静态属性";
String a2 = "实例变量";
static {
System.out.println("A---》" + a1);
System.out.println("A---》静态代码块");
}
{
System.out.println("A---》" + a2);
System.out.println("A---》构造代码块");
}
public A() {
System.out.println("A---》构造方法");
}
}
class B extends A {
public static String b1 = "静态属性";
String b2 = "实例变量";
static {
System.out.println("B---》" + b1);
System.out.println("B---》静态代码块");
}
{
System.out.println("B---》" + b2);
System.out.println("B---》构造代码块");
}
public B() {
System.out.println("B---》构造方法");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("第一次:");
new B();
System.out.println("--------------隔离横线-----------------");
System.out.println("第二次:");
new B();
}
}
结果
第一次:
A---》静态属性
A---》静态代码块
B---》静态属性
B---》静态代码块
A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法
--------------隔离横线-----------------
第二次:
A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法
解释
我们来看Test类
中的main方法
,里面有一行代码是:new B()
,此时B类
还没有加载,那我们需要先来加载B类
,这个过程在类加载子系统
中完成,主要过程如下:
其中加载阶段没有什么重要内容,主要看链接阶段,具体内容如下:
根据上面可以知道在本阶段类静态变量会进行初始化、类静态常量会进行显示赋值,所以B类
中的b1变量
就会初始化为""
,这就是链接阶段的主要内容
现在来看初始化阶段
,具体内容如下:
可以看出该阶段会执行<client>()方法
,该方法主要作用是进行类变量的显示赋值
以及执行static静态代码块中的内容
,这两个过程的执行顺序取决于他们的代码执行顺序
,另外该方法执行之前需要先加载父类,由于B类的父类
是A类
,所以A类
需要先完成加载、链接、初始化之后才会轮到B类的初始化方法执行
A类的加载过程
不再说了,链接过程会将类静态变量a1
初始化为""
,初始化阶段会执行<client>()方法
,所以类静态变量a1
会被显示赋值为"静态属性"
,然后执行static静态代码块
中的内容,因此会先执行如下代码:
System.out.println("A---》" + a1);
System.out.println("A---》静态代码块");
因此会先输出:
A---》静态属性
A---》静态代码块
到此为止A类已经加载完了
,现在来执行B类
初始化中的<client>()方法
,所以类静态变量b1
会被显示赋值为"静态属性"
,然后执行static静态代码块
中的内容,因此会先执行如下代码:
System.out.println("B---》" + b1);
System.out.println("B---》静态代码块");
因此会先输出:
B---》静态属性
B---》静态代码块
现在B类和A类都已经加载完毕
了,那么就需要创建对象了,我们来看一下Test类
的字节码文件:
然后看B类的<init>方法
,如下:
现在我们知道了在子类对象初始化之前需要先初始化其父类对象
,<init>方法
中执行如下操作:
- 默认初始化
- 显式初始化 / 代码块中初始化(这两个的执行顺序取决于代码顺序)
- 构造器中初始化
所以A类对象
中的a2变量
将会默认初始化为""
,然后显示初始化为"实例变量"
,之后执行代码块,代码如下:
{
System.out.println("A---》" + a2);
System.out.println("A---》构造代码块");
}
因此会输出:
A---》实例变量
A---》构造代码块
当A类对象
执行<init>方法
之后,就该执行B类对象
的<init>方法
了,那么B类对象
中的b2变量
将会默认初始化为""
,然后显示初始化为"实例变量"
,之后执行代码块,代码如下:
{
System.out.println("B---》" + b2);
System.out.println("B---》构造代码块");
}
因此会输出:
B---》实例变量
B---》构造代码块
现在已经执行了Test类
中main()方法
中的第一行new B()
代码
现在来看Test类
中main()方法
,里面的其他代码不在看了,我们直接来看该方法中的第一行new B()
代码
由于B类
和B类的父类A类
都已经加载过了,所以不用在经过类加载子系统中的加载、链接、初始化阶段
,所以只会执行B类的<init>方法
,然后会先执行A类中的<init>方法
,具体过程上面已经描述的很清晰了,这里不在赘述,所以会输出如下内容:
A---》实例变量
A---》构造代码块
A---》构造方法
B---》实例变量
B---》构造代码块
B---》构造方法
到此就得出了所有结果