代码块的使用细节

介绍代码块的基本使用,对类的加载时机,及子类对象实例化过程中,静态/普通属性初始化、静态/普通代码块、构造方法的执行顺序的分析。

Author: Msuenb

Date: 2023-02-11


代码块基本介绍

代码块又称为初始化块,属于类的成员,类似于方法,将逻辑语句封装在方法体中。但和方法不同的是,代码块没有方法名、返回值、参数,只有方法体,而且不通过对象或类显示调用,而是加载类,或创建对象时隐式调用。

基本语法:

【修饰符】 {
    代码...
}

使用说明:

  • 修饰符可选,要写的话,只能是static

  • 代码块分为两类:静态代码块(有static修饰)和普通代码块(没static修饰)

  • 逻辑语句可以为输入、输出、方法调用、循环、判断等语句

示例代码:

public class CodeBlack01 {
    public static void main(String[] args) {
        new Movie("1919");
    }
}

class Movie {
    private String name;

    {
        System.out.println("电影开始...");	// 将相同的语句放到代码块中
    }

    public Movie() {
        // System.out.println("电影开始...");	
        System.out.println("Movie() 被调用...");
    }

    public Movie(String name) {
        this.name = name;
        // System.out.println("电影开始...");
    }
}

注意:代码块的调用优先于构造器

普通代码块可以看作是对构造器的补充机制,如果多个构造器中有重复的语句,可以抽取到代码块中,提高代码的重用性。

代码块使用细节

  • 静态代码块的作用是对类进行初始化,它随着类的加载而执行,并且只会执行一次
  • 普通代码块,在创建对象实例时被隐式调用,每创建一个实例对象,就会执行一次
  • 静态代码块只能调用静态成员;普通代码块可以调用任意成员

类的加载时机

  1. 创建对象实例时(new)
  2. 创建子类对象实例,父类也会被加载
  3. 使用类的静态成员时(静态属性, 静态方法)

注意:使用静态成员常量时,类不会被加载!!!

示例代码:

public class CodeBlockDetail01 {
    public static void main(String[] args) {
        // 1. 创建对象实例时 该类被加载
        // new AA();
        // new AA();   // 静态代码块只会执行一次 普通代码块会再次执行

        // 2. 创建子类对象时 父类也被加载(父类先 子类后)
        // new BB();

        // 3. 调用静态成员时 该类被加载
        System.out.println(AA.n1);   // 普通代码块不会执行
    }
}

class AA {
    public static int n1 = 996;

    {
        System.out.println("AA 的普通代码块...");
    }

    static {
        System.out.println("AA 的静态代码块...");
    }
}

class BB extends AA {
    static {
        System.out.println("BB 的静态代码块...");
    }
}

子类对象实例化过程

在创建一个子类对象时,它们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序如下:

  1. 父类的静态代码块和父类的静态属性初始化(优先级一样,按定义的顺序执行)
  2. 子类的静态代码块和子类的静态属性初始化(优先级一样,按定义的顺序执行)
  3. 父类的普通代码块和父类的普通属性初始化(优先级一样,按定义的顺序执行)
  4. 父类的构造方法
  5. 子类的普通代码块和子类的普通属性初始化(优先级一样,按定义的顺序执行)
  6. 子类的构造方法

示例代码:

class Father {
    private int a = getValA();         	// 5
    private static int b = getValB();  	// 1

    {                               	// 6
        System.out.println("父类普通代码块");
    }
    
    static {                        	// 2
        System.out.println("父类静态代码块");
    }

    public Father() {               	// 7
        System.out.println("父类构造器");
    }

    public int getValA() {
        System.out.println("父类普通属性初始化");
        return 1;
    }

    public static int getValB() {
        System.out.println("父类静态属性初始化");
        return 2;
    }
}

class Son extends Father {

    private int c = getValC();         	// 8
    private static int d = getValD();  	// 3

    {                               	// 9
        System.out.println("子类普通代码块");
    }

    static {                        	// 4
        System.out.println("子类静态代码块");
    }

    public Son() {                  	// 10
        // 隐含 super();
        System.out.println("子类构造器");
    }

    public int getValC() {
        System.out.println("子类普通属性初始化");
        return 1;
    }

    public static int getValD() {
        System.out.println("子类静态属性初始化");
        return 2;
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        new Son();
    }
}

注意:在创建子类实例对象时,会先调用父类的构造方法,初始化父类空间。

posted @ 2023-02-12 19:08  msuenb  阅读(23)  评论(0编辑  收藏  举报