Java静态代码块、普通代码块、构造方法的优先级

本文详细探讨了Java中静态代码块、普通代码块和构造方法的调用顺序。在同一个类中,静态代码块优先级最高,其次为普通代码块,最后是构造方法。在父子类中,父类的静态代码块先于子类的执行,接着是父类的构造方法(包括其内的super()、普通代码块),然后是子类的普通代码块,最后是子类的构造方法。

0. 引言

一、代码块:

  • 只有方法体,没有方法名、没有参数、没有返回
  • 基本语法:[修饰符] {方法体}
  • 静态代码块:修饰符为static,只能调用静态成员
  • 普通代码块:无修饰符,可以调用类所有成员

二、构造方法:

  • 方法名与类名相同,没有返回值,但不能写void
  • 基本语法:`[修饰符] 类名(形参列表) {方法体}

1. 在同一个类中的调用顺序

A) 新建一个类取名PriorityTest,依次定义构造方法、普通代码块、静态代码块

class PriorityTest {
    // 定义构造方法
    public PriorityTest() {
        System.out.println("构造方法被调用");
    }
    
    // 定义普通代码块
    {
        System.out.println("普通代码块被调用");
    }
    
    // 定义静态代码块
    static {
        System.out.println("静态代码块被调用");
    }
}

B) 定义main方法,运行

public static void main(String[] args) {
    PriorityTest priorityTest = new PriorityTest();
}

C) 执行结果

静态代码块被调用
普通代码块被调用
构造方法被调用

D) 分析原因

  • 静态代码块随着类加载而执行,优先级最高
  • 构造方法内最开始隐含了super()和调用普通代码块:先执行super(),再调用普通代码块,最后执行构造方法

2. 在父子类中的调用顺序

前言:从上述分析可以看出,构造方法内部先执行super(),再调用普通代码块,最后执行构造方法。因此,如果存在父子类的继承关系,优先级顺序将更为复杂。下文将进一步分析父类与子类中静态代码块、普通代码块、构造方法的调用顺序。

A) 父类取名PriorityTestFather,依次定义构造方法、普通代码块、静态代码块

class PriorityTestFather {
    // 定义构造方法
    public PriorityTestFather() {
        System.out.println("父类 构造方法被调用");
    }
    
    // 定义普通代码块
    {
        System.out.println("父类 普通代码块被调用");
    }
    
    // 定义静态代码块
    static {
        System.out.println("父类 静态代码块被调用");
    }
}

B) 子类取名PriorityTestSon,依次定义构造方法、普通代码块、静态代码块

class PriorityTestSon extends PriorityTestFather {
    // 定义构造方法
    public PriorityTestSon() {
        System.out.println("子类 构造方法被调用");
    }
    
    // 定义普通代码块
    {
        System.out.println("子类 普通代码块被调用");
    }
    
    // 定义静态代码块
    static {
        System.out.println("子类 静态代码块被调用");
    }
}

C) 定义main方法,运行

public static void main(String[] args) {
    PriorityTestSon priorityTestSon = new PriorityTestSon();
}

D) 执行结果

父类 静态代码块被调用
子类 静态代码块被调用
父类 普通代码块被调用
父类 构造方法被调用
子类 普通代码块被调用
子类 构造方法被调用

E) 分析原因

  • 执行main方法,先加载父类.class进内存,后加载子类.class进内存,然后开始初始化对象。静态代码块随着类加载而执行,因此先后分别调用父类静态代码块、子类静态代码块。
  • main方法初始化对象时,正如上文所述,构造方法内部会先执行super(),再调用普通代码块,最后执行构造方法。
    • 第一步,执行子类的super(),运行父类构造方法。
    • 第二步,父类构造方法内依旧按super()、普通代码块、构造方法的顺序依次执行。但是因为父类再往上super()就是Object类,所以没有打印内容。接下来,按父类普通代码块、父类构造方法的顺序依次执行。
    • 第三步,执行子类的普通代码块
    • 第四步,执行子类的构造方法

3. 总结

  • 创建对象时,在一个类中的调用顺序
    • ⑴调用静态代码块
    • ⑵调用普通代码块
    • ⑶调用构造方法
  • 创建对象时,在父子类的调用顺序
    • 静态代码块随类加载而执行;构造方法内先执行super(),后调用普通代码块,最后执行构造方法
    • ⑴父类静态代码块
    • ⑵子类静态代码块
    • ⑶父类普通代码块
    • ⑷父类构造方法
    • ⑸子类普通代码块
    • ⑹子类构造方法
<think> 首先,用户的问题是基于提供的引用材料,询问Java构造方法构造代码块的区别。我需要根据引用[1]、[2][3]来回答这个问题。 关键点从引用中提取: - 引用[1]:展示了静态代码块、构造代码块构造方法的位置。构造代码块用花括号{}定义。 - 引用[2]:解释了区别与联系。构造方法用于初始化,每次新对象创建时执行一次,但只能执行一个有参或无参的。构造代码块也是每次新对象创建时执行,但不能重载。静态代码块在类加载时执行一次。 - 引用[3]:静态代码块在JVM加载类时执行一次,构造方法每次创建新对象时执行。区别在于执行频率:静态代码块只执行一次,构造方法每次实例化时执行。 用户的问题是:“请问Java构造方法构造代码块的区别”。我需要专注于这两个概念。 核心区别: 1. **定义方式**: - 构造方法:使用类名相同的方法名,可以有参数。例如:`public Person() {...}`。 - 构造代码块:用花括号{}定义在类中,但没有方法名。引用[1]中示例:`{ System.out.println("这是构造代码块"); }`。 2. **执行时机**: - 构造方法:每次创建对象(使用`new`关键字)时执行一次。 - 构造代码块:也是每次创建对象时执行,但在构造方法之前执行(引用[2]暗示)。 从引用[2]:"构造代码块,也是每次产生新类的时候就执行一次",意思是每次新对象创建时。引用[3]强调了执行频率。 3. **执行顺序**: 引用[2]提到:"一般来说。构造方法是用来初始化的...每次产生新类的时候就执行一次",但没有明确顺序细节。 引用[3]:"静态优先执行,父类优先执行",指的是静态代码块优先。 对于构造代码块构造方法,标准顺序是:静态代码块(如果适用) > 父类构造 > 子类构造代码块 > 子类构造方法。构造代码块构造方法之前执行。 4. **重载能力**: - 构造方法:可以有多个重载版本(有参、无参)。 - 构造代码块:只有一个,不能重载(引用[2]:"不能进行重载")。 5. **可选性**: - 构造方法:如果类中没有定义构造方法Java会提供一个默认的无参构造方法。 - 构造代码块:是可选的,不是必须的(引用[2]:"静态代码块构造代码块在程序中是可以省略的")。 6. **目的**: - 两者都用于初始化对象。 - 构造代码块常用于共享初始化逻辑,避免在每个构造方法中重复代码。 回答结构: - 清晰、结构化。 - 使用中文。 - 符合系统指令:行内数学表达式用$...$,但我这里不需要数学表达式;独立公式用$$...$$;LaTeX语法正确;生成相关问题。 - 末尾自然地添加引用标识。 生成相关问题: - 系统指令要求:"生成相关问题",放在回答末尾。 - 基于主题,问题如:静态代码块的作用、执行顺序示例等。 最终回答框架: 1. 介绍构造方法构造代码块的定义。 2. 列举区别点(定义、执行时机、顺序、重载、可选性)。 3. 总结。 4. 添加相关问题。 5. 末尾引用标识:[^1], [^2], [^3],但引用是用户提供的,我需要引用它们。 引用标识:用户提供了引用[^1], [^2], [^3],我需要在回答中引用它们。指令说:"回答中引用的段落末尾自然地添加引用标识",所以当我使用引用内容时,在末尾加[^n]。 例如:"构造代码块用花括号{}定义 [^1]。" </think> 基于提供的引用材料,我将详细解释Java构造方法构造代码块的区别。以下是结构化分析,重点参考了引用[1]、[^2][^3]的内容。 ### 1. **定义方式** - **构造方法**: 使用与类名相同的名称定义,可以有参数(支持重载)。例如: ```java public Student(String name) { this.name = name; // 初始化逻辑 } ``` 如果类中未显式定义构造方法Java会默认提供一个无参数的构造方法 [^1][^2]。 - **构造代码块**: 使用花括号 `{}` 直接定义在类体中,没有方法名或参数。例如引用[1]中的示例: ```java { System.out.println("这是构造代码块"); // 共享初始化逻辑 } ``` 构造代码块不能重载,每个类只能有一个(或通过多个块组合) [^2]。 ### 2. **执行时机** - **构造方法**: 每次使用 `new` 关键字创建对象时执行一次。例如,`Student s = new Student();` 会触发构造方法执行。 - 关键点:执行频率高,每个新对象实例化时都调用 [^2][^3]。 - **构造代码块**: 同样在每次创建对象时执行,但执行发生在构造方法之前(顺序:构造代码块构造方法)。 - 关键点:每次对象创建都执行,不会跳过 [^2][^3]。 ### 3. **执行顺序(在对象创建过程中的优先级)** 当创建对象时,执行顺序固定: ``` 静态代码块(类加载时执行一次) → 父类构造 → 子类构造代码块 → 子类构造方法 ``` - 示例:如果类中包含构造代码块构造方法,构造代码块优先于构造方法执行 [^2][^3]。 ### 4. **重载能力** - **构造方法**: 支持重载(例如,可以定义多个构造方法:无参、有参等)。 - 例如:`public Student()` `public Student(String name)` 可以共存 [^1][^2]。 - **构造代码块**: 不支持重载,因为其没有方法签名。每个类中只能有一个构造代码块(或通过多个 `{}` 块组合,但逻辑上视为一个整体) [^2]。 ### 5. **可选性与默认行为** - **构造方法**: 如果类中未显式定义,Java编译器会自动添加一个默认的无参数构造方法。这意味着构造方法在类中是可选的,但对象创建时必须有一个 [^2]。 - **构造代码块**: 完全可选。如果类中未定义构造代码块,则不会执行任何额外初始化逻辑。程序可以省略构造代码块而不影响编译 [^2]。 ### 6. **主要用途** - **构造方法**: 用于对象初始化,允许通过参数定制初始化逻辑。例如,设置对象属性值 [^1][^2]。 - **构造代码块**: 用于共享初始化代码,避免在每个构造方法中重复相同逻辑。例如,记录日志或设置默认状态。 - 示例:如果多个构造方法都需要某个初始化步骤,可以统一放在构造代码块中 [^2][^3]。 ### 总结 | 特性 | 构造方法 | 构造代码块 | |--------------------|----------------------------|----------------------------| | 定义方式 | 类名同名的方法,支持参数重载 | 花括号 `{}`,无方法名,不支持重载 | | 执行时机 | 每个新对象创建时 | 每个新对象创建时,在构造方法前执行 | | 执行频率 | 每次实例化对象执行 | 每次实例化对象执行 | | 重载能力 | 支持(多个版本) | 不支持 | | 可选性 | 可选(有默认无参构造) | 可选(可完全省略) | | 主要用途 | 参数化初始化 | 共享初始化逻辑 | 简言之,构造方法允许灵活的参数化初始化,而构造代码块提供了在对象创建前执行共享代码的机制 [^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值