0
点赞
收藏
分享

微信扫一扫

【Java面向对象】代码块、静态代码块

何以至千里 2022-01-16 阅读 103

理论知识看目录,代码详解看内容

文章目录

一、代码块入门

代码块又称为初始化块,属于类中的成员(类的一部分),类似于方法,可以将逻辑语句封装在方法体中,使用{}包围。

和方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或者类显式调用,而是加载类或创建对象时隐式调用。

基本语法:

[修饰符] {
	// 代码
};

代码块的作用:
相当于是另一种形式的构造器,可以做初始化操作。例如:如果多个构造器中有重复的语句,可以抽取到代码块中,提高代码的重用性。

代码块的调用顺序优先于构造器

创建一个有代码块的类:

public class Cook {
	// 代码块
    {
        System.out.println("买菜");
        System.out.println("洗菜");
        System.out.println("切菜");
    }
    
    public Cook() {
        System.out.println("炒白菜");
    }

    public Cook(String name) {
        System.out.println("炒" + name);
    }
}

执行测试方法可以看到以下结果:
在这里插入图片描述
可见,代码块的执行顺序是在构造方法之前

二、代码块详解(超祥)

1. static代码块也叫静态代码块,作用就是对类进行初始化,而它随着类的加载而执行,并且只会执行一次,如果是普通的代码块,每创建一个对象就执行。

创建一个类

class AAA {
    // 静态代码块
    static {
        System.out.println("AAA的静态代码块执行了...");
    }

    // 普通代码块
    {
        System.out.println("AAA的普通代码块执行了...");
    }
}

执行以下代码:

public static void main(String[] args) {
     AA aa = new AA();
     AA aa2 = new AA();
}

可以看到,类只会被加载一次,所以静态代码块也只会执行一次,而普通代码块会随着类实例的创建而执行
在这里插入图片描述

2. 类是么时候被加载?

① 创建对象实例时(new)

class AA {
    // 静态代码块
    static {
        System.out.println("AA的静态代码块执行了...");
    }
}

执行下面代码,可以看到如下结果,说明创建对象时类有被加载

public static void main(String[] args) {
    // 创建对象实例时(new)
    AA aa = new AA();
}

在这里插入图片描述

② 创建子类对象实例时,父类也会被加载

创建一个子类BB类,是AA类的子类

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

执行下面代码,可以看到如下结果

public static void main(String[] args) {
    // 创建子类对象实例时,父类也会被加载
    BB bb = new BB();
}

可以看到父类会先被加载,子类后被加载,继承时有说到过
在这里插入图片描述

③ 使用类的静态成员时(静态属性、静态方法)

创建一个带有静态成员的CC类

class CC {
    public static int n1 = 100;
    
    // 静态代码块
    static {
        System.out.println("CC的静态代码块执行了...");
    }
}

执行以下代码:

public static void main(String[] args) {
    // 使用类的静态成员时(静态属性、静态方法)
    System.out.println(CC.n1);
}

可以看到如下结果:
在这里插入图片描述

因为在main方法中调用了CC类的静态属性,所以类会被加载

3. 创建一个对象时,在一个类中的调用顺序:

① 静态属性初始化和静态代码块

定义一个A类:

class A {
    private static int n1 = getVal();

    static {
        System.out.println("A类的静态代码块执行了");
    }

    public static int getVal() {
        System.out.println("getVal() 方法执行了");
        return 10;
    }
}

执行以下代码:

public static void main(String[] args) {
    A a = new A();
}

可以看到,静态变量初始化先被执行,因为静态变量初始化调用了getVal方法,所以该方法会先执行,然后将值赋给n1,然后代码继续向下执行,会继续执行静态代码块
在这里插入图片描述

② 普通属性初始化和普通代码块

增加A的代码:

class A {
    {
        System.out.println("A类的普通代码块执行了");
    }
    private static int n1 = getVal();
    private int n2 = getVal2();
    static {
        System.out.println("A类的静态代码块执行了");
    }
    public static int getVal() {
        System.out.println("getVal() 方法执行了");
        return 10;
    }
    public int getVal2() {
        System.out.println("getVal2() 方法执行了");
        return 20;
    }
}

执行以下代码:

public static void main(String[] args) {
    A a = new A();
}

因为静态时随着类加载的,所以静态的会先执行,而非静态的也是根据顺序执行的。
在这里插入图片描述

③ 构造器

在A类上增加一个无参构造器:

class A {
    {
        System.out.println("A类的普通代码块执行了");
    }
    private static int n1 = getVal();
    private int n2 = getVal2();
    static {
        System.out.println("A类的静态代码块执行了");
    }
    public A() {
        System.out.println("A类的无参构造器执行了");
    }
    public static int getVal() {
        System.out.println("getVal() 方法执行了");
        return 10;
    }
    public int getVal2() {
        System.out.println("getVal2() 方法执行了");
        return 20;
    }
}

执行以下代码:

public static void main(String[] args) {
    A a = new A();
}

可以看到构造器时最后执行的
在这里插入图片描述

4. 构造器最前面隐含了super()和调用普通代码块

class 类名 extends 父类名 {
	public 类名() {	// 构造器
		// 1. 这里隐藏了调用父类构造的super();
		super();	// 继承有提到
		
		// 2. 这里隐藏了调用普通代码块的语句

		// 3. 我们自己写的构造器逻辑
		// 如果有手动调用父类构造,则前面的super()方法将会被替代
		...
	}
}

所以到目前为止,一个类的执行顺序应该是:

因为在构造器中先有super再执行普通代码块的,所以会一直追溯到Object类,到Object类时没有父类了,就开始执行Object的普通代码块(虽然没有),然后执行Object类的构造方法,执行后会往下找下一级的普通代码块,然后是构造方法,以此类推,所以普通代码块的执行顺序是优先于构造方法的

5. 有继承关系的静态代码块、静态属性初始化、普通代码块、普通代码初始化、构造方法的调用顺序:

① 父类静态代码块和静态属性初始化(优先级按代码顺序执行)

② 子类静态代码块和静态属性初始化(优先级按代码顺序执行)

③ 父类普通代码块和普通属性初始化(优先级按代码顺序执行)

如果父类属性初始化时调用了方法,并且子类重写了该方法,执行的是子类的方法

④ 父类构造方法

⑤ 子类普通代码块和普通属性初始化(优先级按代码顺序执行)

⑥ 子类构造方法

创建测试方法,看下执行什么:

package object05;

public class Test001 {
    public static void main(String[] args) {
        new Zi();
    }
}

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

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

    private static int n1 = getInt1();
    private int n2 = getInt2();

    public Fu() {
        System.out.println("Fu类的构造方法");
    }

    public static int getInt1() {
        System.out.println("Fu类的初始化静态成员的方法");
        return 10;
    }

    public int getInt2() {
        System.out.println("Fu类的初始化普通成员的方法");
        return 10;
    }
}

class Zi extends Fu {
    private static int n3 = getInt3();
    private int n4 = getInt4();

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

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

    public Zi() {
        System.out.println("Zi类的构造方法");
    }

    public static int getInt3() {
        System.out.println("Zi类的初始化静态成员的方法");
        return 10;
    }

    public int getInt4() {
        System.out.println("Zi类的初始化普通成员的方法");
        return 10;
    }
}

在这里插入图片描述
因为父类会先被加载并且父类的静态代码块在上面,所以先执行父类的静态代码块再执行父类的初始化静态成员的方法。
然后加载子类,子类的静态成员在静态代码块的上面,所以会先被初始化,然后执行子类的静态代码块。
当类加载完后,会先执行父类的普通代码块和普通成员初始化,也是根据代码顺序执行的。
再然后执行父类的构造方法。
父类执行完毕后,就轮到子类了,也是根据顺序执行普通代码块和普通成员初始化,最后执行子类构造。

6. 静态代码块只能对用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

class C {
    private int n1 = 10;
    private static int n2 = 20;

    {
        System.out.println(n1);
        System.out.println(n2);
    }
    
    static {
        // System.out.println(n1);  // 错误
        System.out.println(n2);
    }
}
举报

相关推荐

0 条评论