最近在阅读Thinking in Java
,谈到Java类成员加载顺序
一部分,不由想起以前被大学老师支配的恐惧,但是翻看此书之后,有点豁然开朗之感,遂起心思,想要写一篇文章,详细分析Java类成员加载顺序
,希望能够帮助新人朋友们,如有失误之处,劳请指出,感激不尽。
在开始长篇大论之前,我想先写一点简单但需要注意的知识:
咱先举一个🌰,我喜欢🐕,咱们先写一个阿拉斯加吧。
import java.util.*;
/**
* @author LKQ
* @date 2022/7/28 19:07
* @description
*/
public class Alaska {
private static int i = print("大白");
private static int j = print("小黄");
static int print(String s) {
System.out.println(s);
return 0;
}
public static void main(String[] args) {
}
}
Output:
大白
小黄
public class Alaska {
private static int j = print("小黄");
private static int i = print("大白");
static int print(String s) {
System.out.println(s);
return 0;
}
public static void main(String[] args) {
}
}
Output:
小黄
大白
从上面可以看出,大白i
和小黄j
两个变量,定义的顺序不同,输出的结果顺序也不同。
请注意,虽然main()
啥也没干,但是会有输出。
public class Alaska {
private static boolean isSick;
private static int age;
private static String name;
private static double weight;
public static void main(String[] args) {
System.out.println("Data type: Initial Value");
System.out.println("boolean: " + isSick);
System.out.println("int: " + age);
System.out.println("String: " + name);
System.out.println("double: " + weight);
}
}
Output:
Data type: Initial Value
boolean: false
int: 0
String: null
double: 0.0
可以看到,我们并没有给这个变量赋值,但依然有其对应的默认值。
public class Alaska {
private static int i = 10;
static {
System.out.println("Alaska static block initialized");
}
static class AlaskaInner {
AlaskaInner() {
System.out.println("static class AlaskaInner initialized");
}
static {
System.out.println("static class AlaskaInner static initialized");
}
}
private static String name = getName();
{
System.out.println("Alaska non-static block initialized");
}
private static String getName() {
System.out.println("alaska - 阿拉斯加");
return "alaska";
}
Alaska() {
System.out.println("Alaska constructor");
}
public static void main(String[] args) {
System.out.println("create a new Alaska");
Alaska a = new Alaska();
}
}
Output:
Alaska static block initialized
alaska - 阿拉斯加
create a new Alaska
Alaska non-static block initialized
Alaska constructor
假设我们养两只阿拉,会发生什么呢?
public class Alaska {
private static int i = 10;
static {
System.out.println("Alaska static block initialized");
}
static class AlaskaInner {
AlaskaInner() {
System.out.println("static class AlaskaInner initialized");
}
static {
System.out.println("static class AlaskaInner static initialized");
}
}
private static String name = getName();
{
System.out.println("Alaska non-static block initialized");
}
private static String getName() {
System.out.println("alaska - 阿拉斯加");
return "alaska";
}
Alaska() {
System.out.println("Alaska constructor");
}
public static void main(String[] args) {
System.out.println("create a new Alaska");
Alaska a = new Alaska();
Alaska a1 = new Alaska();
}
}
Output:
Alaska static block initialized
alaska - 阿拉斯加
create a new Alaska
Alaska non-static block initialized
Alaska constructor
Alaska non-static block initialized
Alaska constructor
可以看到,静态的初始化,不管生成多少只阿拉,它只会执行一次
。
而非静态初始化
以及构造器
,每 new Alaska()
都执行了一次。
注意:静态内部类
并没有初始化。只用在调用了静态内部类时,才会进行初始化。
public static void main(String[] args) {
System.out.println("create a new Alaska");
Alaska a = new Alaska();
Alaska a1 = new Alaska();
AlaskaInner alaskaInner = new AlaskaInner();
}
Output:
Alaska static block initialized
alaska - 阿拉斯加
create a new Dog
Alaska non-static block initialized
Alaska constructor
Alaska non-static block initialized
Alaska constructor
static class AlaskaInner static initialized
static class AlaskaInner initialized
如果一只不养但是看到别人养了呢?
public class Alaska {
private static int i = 10;
static {
System.out.println("Alaska static block initialized");
}
static class AlaskaInner {
AlaskaInner() {
System.out.println("static class AlaskaInner initialized");
}
static {
System.out.println("static class AlaskaInner static initialized");
}
}
private static String name = getName();
{
System.out.println("Alaska non-static block initialized");
}
private static String getName() {
System.out.println("alaska - 阿拉斯加");
return "alaska";
}
Alaska() {
System.out.println("Alaska constructor");
}
}
class Dog {
public static void main(String[] args) {
System.out.println("create a new dog");
Alaska.getName();
}
}
Output:
create a new dog
Alaska static block initialized
alaska - 阿拉斯加
alaska - 阿拉斯加
如果不喜欢🐶,也没看到别人家的狗。
class Dog {
public static void main(String[] args) {
// Alaska a = new Alaska();
// Alaska a1 = new Alaska();
System.out.println("我不喜欢阿拉");
}
}
Output:
我不喜欢阿拉
那么,是否可以得出这样的结论:静态初始化,只执行一次:当首次生成这个类的对象,或者首次访问属于那个类的静态数据成员时(即使从未生成过那个类的对象)。请好好琢磨
还是以上面的阿拉为例,现在我们清除不必要的东西,只保留部分内容,并进行适当的扩展。一步一步来分析。
import java.util.*;
/**
* @author LKQ
* @date 2022/7/28 19:07
* @description
*/
public class Alaska extends Dog{
static {
System.out.println("Alaska static initialized");
}
public static void main(String[] args) {
System.out.println("create a new Alaska");
}
}
class Dog extends Animal{
static {
System.out.println("Dog static initialized");
}
}
class Animal {
static {
System.out.println("Animal static initialized");
}
}
Output:
Animal static initialized
Dog static initialized
Alaska static initialized
create a new Alaska
如果不让 Alaska extends Dog
Output:
Alaska static initialized
create a new Alaska
在Alaska
运行Java时,所发生的第一件事情就是试图访问Alaska.main()(一个静态方法)
。于是加载器开始启动,并找出Alaska
类的编译代码(Alaska.class文件中
)。加载过程中,编译器注意到它有一个基类Dog
(由extends
得知),于是它继续加载Dog
,不管有没有产生一个该基类的对象,这都要发生。
如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。 自底向上找到根基类后Animal
,然后自顶向下执行static
初始化。这其实就是Java类加载时的双亲委派模型机制。
完成类加载后,对象就可以被创建了。2.2说过,对象中的所有基本类型都被设为默认值,对象引用被设为null。这是通过将对象内存设为二进制零值而一举生成的。
那么实例变量和构造器谁先加载呢?
import java.util.*;
/**
* @author LKQ
* @date 2022/7/28 19:07
* @description
*/
public class Alaska extends Dog{
private static int i = 10;
public int d = print();
static {
System.out.println("Alaska static initialized");
}
Alaska() {
System.out.println("Alaska constructor");
}
public static void main(String[] args) {
System.out.println("create a new Alaska");
Alaska alaska = new Alaska();
}
}
class Dog extends Animal{
private static int j = 5;
public int d = print();
static {
System.out.println("Dog static initialized");
}
Dog() {
System.out.println("Dog constructor ");
}
}
class Animal {
private static int k = 0;
private int a = print();
static {
System.out.println("Animal static initialized");
}
int print() {
System.out.println("Animal instance variable initialized " + k + " time");
k++;
return k;
}
Animal() {
System.out.println("Animal constructor");
}
}
Animal static initialized
Dog static initialized
Alaska static initialized
create a new Alaska
Animal instance variable initialized 0 time
Animal constructor
Animal instance variable initialized 1 time
Dog constructor
Animal instance variable initialized 2 time
Alaska constructor
// 如果这里修改为如下代码
public static void main(String[] args) {
System.out.println("create a new Dog");
Dog dog = new Dog();
}
Output:
Animal static initialized
Dog static initialized
Alaska static initialized
create a new Dog
Animal instance variable initialized 0 time
Animal constructor
Animal instance variable initialized 1 time
Dog constructor
可以看到,首先是static
初始化,从父类到子类输出。然后new Alaska()
时,根父类实例变量 a 先初始化,然后调用其构造器完成初始化,顺序也是从根父类到子类。
得出结论:实例变量 > 构造器
那加入非静态初始化块呢?它应该出现在哪?
import java.util.*;
/**
* @author LKQ
* @date 2022/7/28 19:07
* @description
*/
public class Alaska extends Dog{
private static int i = 10;
public int d = print();
static {
System.out.println("Alaska static block initialized");
}
{
System.out.println("Alaska non-static block initialized");
}
Alaska() {
System.out.println("Alaska constructor");
}
public static void main(String[] args) {
System.out.println("create a new Alaska");
Alaska a = new Alaska();
}
}
class Dog extends Animal{
private static int j = 5;
public int d = print();
static {
System.out.println("Dog static initialized");
}
{
System.out.println("Dog non-static block initialized");
}
Dog() {
System.out.println("Dog constructor ");
}
}
class Animal {
private static int k = 0;
private int a = print();
static {
System.out.println("Animal static initialized");
}
{
System.out.println("Animal non-static block initialized");
}
int print() {
System.out.println("Animal instance variable initialized " + k + " time");
k++;
return k;
}
Animal() {
System.out.println("Animal constructor");
}
}
Output:
Animal static initialized
Dog static initialized
Alaska static block initialized
create a new Alaska
Animal instance variable initialized 0 time
Animal non-static block initialized
Animal constructor
Animal instance variable initialized 1 time
Dog non-static block initialized
Dog constructor
Animal instance variable initialized 2 time
Alaska non-static block initialized
Alaska constructor
可以看到,非静态初始块顺序 出现在 实例变量
和 构造器
之间。
得出结论:实例变量 > 非静态初始块 > 构造器
借鉴Thinking in Java
来下个结论吧。假设有一个Dog
类:
Dog
的对象时(构造器可以看成静态方法),或者Dog
类的静态方法/静态域首次被访问时,Java解释器
必须查找类路径,以定位Dog.class
文件。如果有基类存在,那么继续往上定位。Dog.class,这将创建一个Class对象
,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载
的时候进行一次。new Dog()
创建对象时,首先在堆上为Dog
对象分配足够的存储空间。Dog
对象中的所有基本类型数据都设置成了默认值,而引用类型则被设置为null
。实例变量
、非静态初始块
、构造器
,然后到子类。