静态块、静态属性、构造块、构造方法执行顺序

本文详细解析了Java中类的加载与初始化过程,通过两个具体示例展示了类加载、静态成员初始化、实例化时的构造顺序等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


转载:http://www.cnblogs.com/aligege/p/7600477.html

总结:

1、类加载时,执行静态代码块和初始化静态变量。执行顺序跟代码顺序一致。

2、类实例化时,初始化变量、执行代码块、执行构造。其中初始化变量和代码块的顺序跟代码顺序一致。执行构造是在最后。

3、实例化子类时,会先调用父类的构造方法。调用构造方法之前,会先执行该类的代码块。

4、类只会加载一次。

5、静态变量或静态方法中如果调用构造,可以把构造当做一个普通方法来看。但会先执行一遍代码块。

 

下面是在网上找到的比较典型的例子,以此来说明

例一:

复制代码
复制代码
class A {
    static {
        System.out.println("A的静态块");
    }
    private static String staticStr = getStaticStr();
    private String str = getStr();

    {
        System.out.println("A的实例块");
    }
    
    public A() {
        System.out.println("A的构造方法");
    }

    private static String getStaticStr() {
        System.out.println("A的静态属性初始化");
        return null;
    }
    private String getStr() {
        System.out.println("A的实例属性初始化");
        return null;
    }
    public static void main(String[] args) {
        new B();
        new B();
    }

}

class B extends A{
    private static String staticStr = getStaticStr();
    static {
        System.out.println("B的静态块");
    }
    {
        System.out.println("B的实例块");
    }
    public B() {
        System.out.println("B的构造方法");
    }
   private String str = getStr(); private static String getStaticStr() { System.out.println("B的静态属性初始化"); return null; } private String getStr() { System.out.println("B的实例属性初始化"); return null; } }
复制代码
复制代码

该段代码的执行结果为:

A的静态块
A的静态属性初始化
B的静态属性初始化
B的静态块
A的实例属性初始化
A的实例块
A的构造方法
B的实例块
B的实例属性初始化
B的构造方法
A的实例属性初始化
A的实例块
A的构造方法
B的实例块
B的实例属性初始化
B的构造方法

由此可见,实例化子类的时候,若此类未被加载过,首先加载是父类的类对象,然后加载子类的类对象,接着实例化父类,最后实例化子类,若此类被加载过,不再加载父类和子类的类对象。

接下来是加载顺序,当加载类对象时,首先初始化静态属性,然后执行静态块;当实例化对象时,首先执行构造块(直接写在类中的代码块),然后执行构造方法。至于各静态块和静态属性初始化哪个些执行,是按代码的先后顺序。属性、构造块(也就是上面的实例块)、构造方法之间的执行顺序(但构造块一定会在构造方法前执行),也是按代码的先后顺序。


 

例二:

复制代码
复制代码
public class EXA {
    private static EXA a = new EXA();  
    static {  
        System.out.println("父类--静态代码块");  
    }  
  
    public EXA() {  
        System.out.println("父类--构造函数");  
    }  
  
    {  
        System.out.println("父类--非静态代码块");  
    }  
  
    public static void main(String[] args) {
        new EXC();
        new EXC();
    }
}

class EXC extends EXA {  
    private static EXC b = new EXC();  
    static {  
        System.out.println("子类--静态代码块");  
    }  
    {  
        System.out.println("子类--非静态代码块");  
    }  
  
    public EXC() {  
        System.out.println("子类--构造函数");  
    }
}
复制代码
复制代码

该段代码的执行结果为:

父类--非静态代码块
父类--构造函数
父类--静态代码块
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数
子类--静态代码块
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数

分析(非静态代码块即构造块):

首先要加载父类EXA,由于A的静态属性在静态块的前面,先初始化静态属性(new EXA())(父类--非静态代码块;父类--构造函数),然后是静态块(父类--静态代码块);

加载子类EXC,由于EXC的静态属性也在静态块的前面,先初始化静态属性(new EXC()),实例化子类对象的时候会先实例化父类,所以执行的顺序为(父类--非静态代码块;父类--构造函数;子类--非静态代码块;子类--构造函数),然后是静态块(子类--静态代码块)

实例化父类EXA对象:父类--非静态代码块;父类--构造函数;

实例化子类EXC对象:子类--非静态代码块;子类--构造函数;

第二个new EXC():因为父类和子类都已加载,只需依次实例化父类对象和子类对象(父类--非静态代码块;父类--构造函数;子类--非静态代码块;子类--构造函数;)

推测:若将EXA的静态块和静态属性的初始化换位置,执行结果应该为:

父类--静态代码块
父类--非静态代码块
父类--构造函数
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数
子类--静态代码块
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数
父类--非静态代码块
父类--构造函数
子类--非静态代码块
子类--构造函数

复制代码
复制代码
public class EXA {
      
    static {  
        System.out.println("父类--静态代码块");  
    } 
    private static EXA a = new EXA();
  
    public EXA() {  
        System.out.println("父类--构造函数");  
    }  
  
    {  
        System.out.println("父类--非静态代码块");  
    }  
  
    public static void main(String[] args) {
        new EXC();
        new EXC();
    }
}

class EXC extends EXA {  
    private static EXC b = new EXC();  
    static {  
        System.out.println("子类--静态代码块");  
    }  
    {  
        System.out.println("子类--非静态代码块");  
    }  
  
    public EXC() {  
        System.out.println("子类--构造函数");  
    }
}
复制代码
复制代码
<think>好的,用户问的是构造块的概念或用法,以及在编程中的应用定义。首先,我需要明确“构造块”这个术语在编程中的具体含义。根据提供的引用资料,引用[1]提到了构造块构造方法静态代码块,并指出Java中每个类至少有一个构造方法,如果没有显式定义,系统会自动生成一个默认的无参构造方法构造方法需要用public修饰,否则在其他类中无法实例化。而引用[2]讨论了C#中的构造方法定义调用,说明当用户自定义构造方法后,系统不再提供默认的无参构造方法。引用[4]则提到Python中尝试定义多个构造器时的问题,指出通过多个__init__方法不可行,可能需要其他方式实现。 不过用户的问题是关于“构造块”,而引用中并没有直接提到“构造块”的定义,可能是用户将“构造方法”或“代码块”混淆了。在Java中,存在实例初始化块(instance initializer block)静态初始化块(static initializer block),有时也被称为构造块。例如,实例初始化块在每次创建对象时执行,无论调用哪个构造方法,它都会在构造方法之前运行。静态初始化块则在类加载时执行一次。 所以,用户可能指的是这种初始化块。需要确认构造块是否指的是Java中的实例初始化块或静态初始化块。根据引用[1]中的“构造块构造方法静态代码块”,这里的构造块可能指的是实例初始化块,而静态代码块就是静态初始化块。因此,构造块(实例初始化块)用于对象初始化,而静态代码块用于类初始化。 接下来,需要解释构造块的定义、用法应用场景。例如,在Java中,构造块用大括号包围,直接写在类中,每次创建对象时都会执行,且在构造方法之前执行。它可以用来初始化实例变量,特别是当多个构造方法有共同的初始化代码时,可以提取到构造块中,避免代码重复。 另外,需要比较构造块构造方法的区别:构造块构造方法之前执行,且无论调用哪个构造方法构造块都会执行。而构造方法可以有多个,根据参数不同进行重载。构造块适合执行所有构造方法共有的初始化步骤。 在应用方面,构造块可以用于日志记录、资源初始化、设置默认值等场景。例如,每次创建对象时记录日志,或者在构造块中初始化某些资源,确保对象创建时资源已准备就绪。 还需要注意,用户可能在问题中将“构造块”与“构造方法”混淆,因此需要明确两者的不同。同时,根据引用[4],Python中无法通过多个__init__方法实现构造器重载,但可以通过类方法或默认参数来实现类似功能。不过这可能与构造块无关,但需要确认用户是否可能混淆了这些概念。 最后,生成相关问题时要围绕构造块构造方法静态代码块的区别,使用场景,以及在不同语言中的实现差异等方面提问,以帮助用户深入理解。</think>### 构造块的定义与编程应用 **构造块**(Instance Initializer Block)是面向对象编程中用于对象初始化的特殊代码块,在Java语言中具有典型代表性。以下是其核心要点: --- #### 1. **构造块的定义** - **语法形式**:直接使用大括号`{}`包裹代码,置于类内部、方法外部 示例: ```java public class MyClass { // 构造块 { System.out.println("构造块执行"); } } ``` - **执行时机**:**每次创建对象时**,构造块会**优先于构造方法**执行[^1] - **用途**:集中处理**多个构造方法的公共初始化逻辑**,例如设置默认值或验证参数 --- #### 2. **与构造方法的区别** | 特性 | 构造块 | 构造方法 | |---------------------|--------------------------|-----------------------------| | **执行顺序** | 先于构造方法执行 | 在构造块之后执行 | | **代码复用** | 多个构造方法共享代码 | 需通过`this()`调用其他构造方法 | | **参数依赖** | 无法接收参数 | 可定义参数实现灵活初始化 | --- #### 3. **典型应用场景** 1. **资源预加载** ```java public class DatabaseConnector { private Connection conn; // 构造块初始化数据库连接 { try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb"); } catch (SQLException e) { e.printStackTrace(); } } } ``` 2. **日志记录** 在构造块中添加日志输出,追踪对象创建过程 3. **字段默认值设置** 为复杂对象的非空字段提供默认值,避免构造方法遗漏 --- #### 4. **静态构造块静态初始化块)** - **语法**:使用`static {}`定义 - **作用**:在类加载时执行**一次**,用于初始化静态资源 示例: ```java public class ConfigLoader { static Map<String, String> config; // 静态构造块加载配置文件 static { config = loadConfigFromFile("app.properties"); } } ``` --- #### 5. **与其他语言的对比** - **C#**:通过构造函数重载实现类似功能,无直接等价语法[^2] - **Python**:通过`__init__`方法类方法(如`@classmethod`)模拟构造块逻辑[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值