Java中自由块的执行顺序

本文详细解释了Java中静态块与非静态块的执行时机,包括类加载、对象实例化过程中的执行顺序。通过具体代码示例,清晰展示了静态块与非静态块的执行细节。
  • java中的自由块分为静态的自由块和非静态的自由块。
  • 非静态自由块的执行时间是:在执行构造函数之前。
  • 静态自由块的执行时间是:class文件加载时执行。
  • 非静态自由块可以多次执行,只要初始化一个对象就会执行,但是静态自由块只会在类装载的时候执行一次,一般用来初始化类的静态变量的值。
  • 每次初始化一个对象,都会导致一次非静态块的执行。
  • 如果涉及到继承,则是:首先执行父类的非静态块,然后是父类的构造函数,接着是自己的自由块,最后是自己的构造函数。
  • 静态块的执行时机是在class文件装载的时候,class文件只会装载一次,因此静态块只会执行一次,后面再使用这个类时,不会再执行静态块。 
  • 静态块的执行时机是在class装载后的初始化阶段。如果采用ClassLoader的loadclass来仅仅装 载类而不初始化,是不会触发静态块的执行的。采用Class的forname(String)是采用了默认的initialize为true的情况,也就 是初始化了。如果使用forname(String name,boolean initialize, ClassLoader loader),设置initialize为false,则不会执行静态块。
  • 在执行class装载后的初始化阶段包括:运行<clinit>方法,这个方法中就是类变量的初始化语句和静态自由块语句。这个方法是由java的编译器收集信息后生成的,不能显示的调用。

下面通过例子来说明:

父类

father.java

public class father {

    static{//静态块
       System.out.println("father'sSTATIC free block running");
    }

    {//非静态块
	System.out.println("father'sfree block running");
    }

    public father(){

       System.out.println("father'sconstructor running");

    }
}


子类

son.java
public class son extends father{
    static{//静态块
       System.out.println("son'sSTATIC free block running");
    }

    {//非静态块
       System.out.println("son's freeblock running");
    }

    public son() {
       // TODO Auto-generated constructor stub
       System.out.println("son'sconstructor running");
    }
}

主函数所在类

test.java

public class test{

      public static void main(String[] args) {

       Class f;

       try {

           System.out.println("--------beforeload father--------");

           f=Class.forName("freeblock.father");

           System.out.println("--------afterload father---------");         

           System.out.println("--------beforeinitial father object--------");

           f.newInstance();

           System.out.println("--------afterinitial father object--------");

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       } catch (InstantiationException e) {
           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();
       }      
       Class s;

       try {

           System.out.println("-------beforeload son--------");

           s=Class.forName("freeblock.son");

           System.out.println("--------afterload son-------");

           System.out.println("--------beforeinitial son object----------");

           s.newInstance();

           System.out.println("--------afterinitial son object-----------");

       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       }
    }
}


执行结果:

--------before loadfather--------

father's STATIC free blockrunning

--------after loadfather---------

--------before initial fatherobject--------

father's free block running

father's constructor running

--------after initial fatherobject--------

-------before load son--------

son's STATIC free block running

--------after load son-------

--------before initial sonobject----------

father's free block running

father's constructor running

son's free block running

son's constructor running

--------after initial son object-----------



<think>我们被要求回答关于Java类中属性写法的顺序问题。根据之前对话历史,用户询问了Jackson序列化规则,现在转向Java类内部的属性顺序问题。 参考引用: 引用[4]提到:通过添加注解实现按顺序输出类属性。很失望,打印的属性顺序和代码属性的顺序并不一致,因为getDeclaredFields()方法返回的字段数组并不保证按照代码顺序排序。字段的顺序可能受到多种因素的影响,例如编译器、JVM实现以及其他因素。根据Java文档,getDeclaredFields()方法返回的字段数组是以任意顺序返回的,没有特定的排序保证。 因此,我们需要明确:在Java类中,属性(成员变量)的书写顺序在语法上是自由的,但存在一些约定俗成的规范和潜在影响。 回答结构: 1. 语法规则:Java语言规范对属性顺序没有强制要求。 2. 约定俗成的顺序:通常遵循的代码风格(如Java编码规范)。 3. 顺序的影响:在反射、序列化等场景下,顺序可能不确定。 4. 如何控制顺序:如果需要特定顺序(如序列化时),如何实现。 5. 示例代码。 6. 相关问题。 详细回答: ### 1. **Java类属性书写顺序的语法规则** Java语言规范**没有强制规定**类中属性的书写顺序。以下写法都是合法的: ```java public class Person { private String name; // 先写name private int age; // 后写age } ``` 或 ```java public class Person { private int age; // 先写age private String name; // 后写name } ``` 编译器不会因为属性顺序而报错,因此顺序完全由开发者决定[^2]。 ### 2. **约定俗成的属性顺序** 虽然语法自由,但为了提高代码可读性,主流编码规范(如Google Java Style、Oracle规范)建议按以下顺序排列: 1. **静态常量**(`public static final`) 2. **静态变量**(`private static`) 3. **实例常量**(`private final`) 4. **实例变量**(`private`) 5. **构造方法** 6. **其他方法** 示例: ```java public class Employee { // 1. 静态常量 public static final String COMPANY = "Tech Corp"; // 2. 静态变量 private static int nextId = 1; // 3. 实例常量 private final int id; // 4. 实例变量 private String name; private double salary; // 5. 构造方法 public Employee(String name, double salary) { this.id = nextId++; this.name = name; this.salary = salary; } // 6. 方法 public void raiseSalary(double percent) { salary *= (1 + percent / 100); } } ``` ### 3. **属性顺序的实际影响** - **编译与运行**:顺序不影响程序逻辑(除非属性初始化有依赖关系)。 - **反射获取字段**:通过`Class.getDeclaredFields()`返回的字段数组**不保证顺序**(与源码顺序可能不同)[^4]。 - **序列化**: - Jackson等库默认按字母序序列化字段(可通过`@JsonPropertyOrder`注解自定义顺序)[^4]。 - 例如: ```java @JsonPropertyOrder({"name", "age"}) // 强制先序列化name,再age public class Person { private int age; private String name; } ``` ### 4. **如何控制属性的输出顺序** 若需在反射或序列化中固定顺序,可: - **使用注解**(推荐): ```java @JsonPropertyOrder(alphabetic = false, value = {"id", "name"}) public class Product { private String name; private int id; } ``` - **手动排序字段**:通过反射获取字段后按名称或注解排序(如`@Order`)[^4]。 - **依赖初始化顺序**:避免属性间存在初始化依赖(如`int a = b + 1;` 且`b`在`a`后声明),否则会导致编译错误。 ### 5. **初始化依赖的注意事项** 属性初始化按代码顺序执行,以下写法会报错: ```java public class Test { private int x = y + 1; // 错误!y尚未初始化 private int y = 10; } ``` 正确写法: ```java public class Test { private int y = 10; private int x = y + 1; // 正确:y已初始化 } ``` --- ### 示例:属性顺序与序列化 ```java import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.ObjectMapper; @JsonPropertyOrder({"title", "author"}) // 显式指定序列化顺序 public class Book { private String author; private String title; // 构造方法、getter/setter省略 } public class Demo { public static void main(String[] args) throws Exception { Book book = new Book("Author Name", "Book Title"); String json = new ObjectMapper().writeValueAsString(book); System.out.println(json); // 输出:{"title":"Book Title","author":"Author Name"} } } ``` --- §§相关问题§§ 1. Java类中方法的书写顺序是否有规范? 2. 如何通过反射按源码顺序获取类的字段? 3. 在Spring Boot中如何全局配置Jackson的字段序列化顺序? </think>### Java类中属性写法的顺序规范 在Java类中,属性(成员变量)的书写顺序**没有严格的语法强制要求**,但存在**约定俗成的行业规范和潜在影响**。以下是详细说明: #### 1. **语法自由性** - Java编译器**不强制**属性声明顺序,以下写法均合法: ```java public class User { // 顺序1:先常量后变量 private static final int MAX_AGE = 100; // 静态常量 private String name; // 实例变量 private int age; // 实例变量 } ``` ```java public class User { // 顺序2:先变量后常量 private int age; private String name; private static final int MAX_AGE = 100; } ``` #### 2. **推荐规范(主流编码风格)** 主流规范(如Google Java Style、Oracle规范)建议按以下优先级排序: ```java public class Example { // 1. 静态常量 (public > protected > private) public static final String DEFAULT_NAME = "Unknown"; // 2. 静态变量 private static int instanceCount; // 3. 实例常量 (final) private final int id; // 4. 实例变量 (非final) private String name; private int age; // 5. 构造方法 public Example(int id) { ... } // 6. 其他方法 public void setName(String name) { ... } } ``` **优势**: - 高可见性元素(如常量)优先,提升可读性[^2] - 避免初始化依赖错误(如后声明的属性被先声明的属性引用) #### 3. **顺序的关键影响** - **初始化依赖风险**: ```java public class ErrorExample { private int a = b + 1; // 编译错误!b未定义 private int b = 10; } ``` 需确保被引用的属性先声明: ```java public class CorrectExample { private int b = 10; private int a = b + 1; // 正确 } ``` - **反射获取顺序不确定**: 通过`Class.getDeclaredFields()`获取的属性数组**不保证源码顺序**(可能乱序返回)[^4] - **序列化顺序问题**: - Jackson等库默认按**字母序**序列化属性 - 需用`@JsonPropertyOrder`注解显式控制顺序: ```java @JsonPropertyOrder({"id", "name", "age"}) // 强制指定序列化顺序 public class User { private String name; private int age; private int id; } ``` #### 4. **特殊场景处理** - **继承场景的属性顺序**: 父子类属性按类加载顺序初始化(父类属性先于子类)[^3],但声明位置不影响: ```java class Parent { protected String familyName = "Smith"; // 父类属性 } class Child extends Parent { private String firstName; // 子类属性 } // 初始化顺序:Parent属性 → Child属性 ``` - **Lombok注解顺序**: 工具生成的属性通常集中在类顶部(如`@Data`生成的属性在源码位置上方) --- ### 最佳实践建议 1. **遵循团队规范**:采用统一的声明顺序(如静态→常量→实例变量) 2. **避免交叉依赖**:相互引用的属性就近声明 3. **序列化控制**:用`@JsonPropertyOrder`解决输出顺序问题[^4] 4. **工具辅助**:使用IDE格式化工具(如IntelliJ的`Rearrange Code`)自动排序 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值