IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【Java面向对象】代码块、静态代码块(类的执行顺序) -> 正文阅读

[Java知识库]【Java面向对象】代码块、静态代码块(类的执行顺序)

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

一、代码块入门

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

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

基本语法:

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

? 修饰符可选,如果写的话只能写static
? 代码块分为两类,没有static修饰的叫普通代码块、非静态代码块;有static修饰的叫静态代码块
? 代码块中可以写任意业务逻辑语句
? ;可以省略,也可以写上

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

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

创建一个有代码块的类:

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);
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:22:16  更:2022-01-17 11:22:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:59:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码