Java核心技术卷梳理补充-类中字段初始化与类的加载顺序问题

发布于:2024-02-27 ⋅ 阅读:(93) ⋅ 点赞:(0)

Java核心技术卷梳理补充-类中字段初始化与类的加载顺序问题

默认字段初始化原则

如果写一个类时没有编写构造器,就会为你提供一个无参数构造器。这个构造器将所有的实例字段设置为默认值。于是,实例字段中的数值型数据设置为0,布尔型数据设置为false,所有对象变量将设置为null

示例类:

public class JavaInitTest {
    private int id;
    private String name;
    private LocalDate constructTime;
}

使用无参构造器进行初始化

public JavaInitTest(){
        id = 0;
        name = "";
        constructTime = null;
}

在类中进行显式字段初始化

public class JavaInitTest {
    private int id = 0;
    private String name = "";
    private LocalDate constructTime = LocalDate.now();
}

使用字段初始化块(代码块)

在代码块中的内容将会在类实例化的时候先于构造器执行,并且使用类的静态字段时,此代码块并不会被执行

public class JavaInitTest {
    private int id;
    private String name;
    private LocalDate constructTime;

    {
        id  = 0;
        name = "Kun's FeFan";
        constructTime = LocalDate.now();
    }
}

静态字段的三种初始化方式

显式字段初始化

public class JavaInitTestOfStatic {
    public static int count = 0;   
}

注意:Java中静态字段同样满足默认初始化原则

静态方法初始化

public class JavaInitTestOfStatic {
    public static int count;

    static void setStaticField(){
        count = 233;
    }

    public static void main(String[] args){
        JavaInitTestOfStatic.setStaticField();
        System.out.println(JavaInitTestOfStatic.count);
    }
}

也可以这样写:

package ClassVar;

public class JavaInitTestOfStatic {
    public static int count = getVal();

    public static int getVal(){
        return 233;
    }

    public static void main(String[] args){
        System.out.println(JavaInitTestOfStatic.count);
    }
}

静态初始化块

在静态初始化代码块中的内容将会在类加载的时候被执行

public class JavaInitTestOfStatic {
    public static int count;
    
    static {
        count = 233;
    }

    public static void main(String[] args){
        System.out.println(JavaInitTestOfStatic.count);
    }
}

Java中类会在哪些情况下被加载

调用类的静态方法时加载

package ClassVar;

public class MainClass {
    public static int id;
    static {
        id = 233;
        System.out.println("The field ID in the class is set to 233");
    }

    public static void main(String[] args) {
        System.out.println("id = " + id);
    }
}

类进行实例化时被加载

package ClassVar;

class MyTestClass{
    public static int id;
    static {
        id = 233;
        System.out.println("The field ID in the MyTestClass is set to 233");
    }
}

public class MainClass {
    public static void main(String[] args) {
        MyTestClass test_obj = new MyTestClass();
        System.out.println("id = " + test_obj.id);
    }
}

父类在子类实例化时被加载

注意:父类会优先于子类加载

package ClassVar;

class MyTestClass{
    public static int id;
    static {
        id = 233;
        System.out.println("The field ID in the MyTestClass is set to 233");
    }
}

class MySubClass extends MyTestClass{
    static {
        System.out.println("The class MySubClass has been loaded");
    }
}

public class MainClass {
    public static void main(String[] args) {
        MyTestClass test_obj = new MySubClass();
        System.out.println("id = " + test_obj.id);
    }
}

创建对象时类中字段的加载顺序

  1. 首先被加载的是静态字段与静态初始化块,该两者处于同等地位,并且加载顺序取决于定义顺序
  2. 第二个被加载的是普通字段与普通初始化块,该两者也处于同等地位,并且加载顺序取决于定义顺序
  3. 其次被加载的是该类的构造器
package LoadTest;

class StaticContentLoad{
    public static int num1 = getStaticVar();

    static {
        System.out.println("Static-Code-Block has been executed");
    }

    {
        System.out.println("Normal-Code-Block has been executed");
    }
    public int num3 = getNormalVar();

    public static int num2 = getStaticVar();

    public static int getStaticVar(){
        System.out.println("Method getStaticVar has been executed");
        return 233;
    }

    public static int getNormalVar(){
        System.out.println("Method getNormalVar has been executed");
        return 2333;
    }

    public StaticContentLoad(){
        System.out.println("Constructor has been executed");
    }
}

public class EnterMain {
    public static void main(String[] args) {
        new StaticContentLoad();
    }
}

得到以下运行结果:

Method getStaticVar has been executed
Static-Code-Block has been executed
Method getStaticVar has been executed
Normal-Code-Block has been executed
Method getNormalVar has been executed
Constructor has been executed

进程已结束,退出代码为 0

执行构造器前进行了哪些操作

从上一节的内容不难看出,在执行当前类的构造器中的逻辑前,实质上还进行了其他操作,已知Java中的所有类均隐式继承自Object超类,则实际上在构造器中的代码前面其实**隐式执行了父类的无参构造器(super)**和当前类的普通初始化块,如果有继承自的父类将会依次执行:

package ClassOrderTest;

class MyClassA{
    public MyClassA(){
        System.out.println("Constructor of MyClassA has been executed");
    }
}

class MyClassB extends MyClassA{
    {
        System.out.println("Normal-Code-Block of MyClassB has been executed");
    }
    public MyClassB(){
        System.out.println("Constructor of MyClassB has been executed");
    }
}

public class EnterMain {
    public static void main(String[] args) {
        new MyClassB();
    }
}

在进行继承的情况下的加载顺序

  1. 先加载父类的静态代码块与静态字段,然后加载子类的静态代码块与静态字段
  2. 第二步加载父类的普通字段与普通代码块
  3. 第三步执行父类的构造器
  4. 最后加载子类的普通字段与普通代码块,然后执行子类的构造器
package ClassOrderTest;

class MyClassA{
    static {
        System.out.println("Static-Code-Block of MyClassA has been executed");
    }

    {
        System.out.println("Normal-Code-Block of MyClassA has been executed");
    }
    public MyClassA(){
        System.out.println("Constructor of MyClassA has been executed");
    }
}

class MyClassB extends MyClassA{
    static {
        System.out.println("Static-Code-Block of MyClassB has been executed");
    }

    {
        System.out.println("Normal-Code-Block of MyClassB has been executed");
    }

    public MyClassB(){
        System.out.println("Constructor of MyClassB has been executed");
    }
}

public class EnterMain {
    public static void main(String[] args) {
        new MyClassB();
    }
}

运行结果如下:

Static-Code-Block of MyClassA has been executed
Static-Code-Block of MyClassB has been executed
Normal-Code-Block of MyClassA has been executed
Constructor of MyClassA has been executed
Normal-Code-Block of MyClassB has been executed
Constructor of MyClassB has been executed

进程已结束,退出代码为 0
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到