Java中静态代码块、构造代码块、构造方法的执行顺序及静态代码块的执行次数

本文详细解释了Java中静态代码块的概念、执行顺序及其特点,包括与非静态代码块及静态方法的区别,帮助读者理解静态代码块的工作原理。

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

先引入一份代码:

public class A{
 	public A(){
	    System.out.println("构造方法中a=30");
 	    a=30;
	 }
	 
	 {
 	    System.out.println("构造代码块1中a=40");
 	    a=40;
	 }
	 int a=init();
 
	 {
	     System.out.println("构造代码块2中a=60");
	     a=60;
	 }
	 
	 public int init(){
 	     System.out.println("初始化中a=90");
 	     return 90;
	 }
	 
 	static{
  	     System.out.println("静态代码块1中B=100");
	     B=100;
	 }
	 static int B=staticInit();
 
 	static{ 
  	     System.out.println("静态代码块2中B=400");
 	     B=400;
	 }

	 public static int staticInit(){   
     	     System.out.println("初始化中B=500");
 	     return 500;
	 }
	 
	 public static void main(String[] args){
 	     A p=new A();
     	     A q=new A();
 	 }
  }

输出结果为:

   静态代码块1中B=100
   初始化中B=500
   静态代码块2中B=400  
   构造代码块1中a=40        
   初始化中a=90     
   构造代码块2中a=60    
  构造方法中a=30     
   构造代码块1中a=40    
   初始化中a=90     
  构造方法中a=30

根据以上的运行结果我们会不由自主的产生几个问题,例如:

1.什么是静态代码块?

所谓的代码块就是一段独立的代码空间,那什么是静态代码块呢?说白了,静态代码块就是用static修饰的代码块。

2.它们的执行顺序是什么?

执行顺序:(优先级从高到低)静态代码块>mian方法>构造代码块>构造方法。

3.为什么在显示结果中静态代码块的内容只显示了一次?

    在类加载的init阶段,类的类构造器中会收集所有的static块和字段并执行,static块只执行一次,由JVM保证其只执行一次。或者可以这样认为:static代码块只在类加载时执行,类是用类加载器来读取的,类加载器是带有一个缓存区的,它会把读取到的类缓存起来,所以在一次虚拟机运行期间,一个类只会被加载一次,这样的话静态代码块只会运行一次。

4.静态代码块的显著特点是什么?

随着类的加载而执行,而且只执行一次

5.static{ }(静态代码块)与{ }(非静态代码块)的异同点

相同点:都是在JVM加载类时且在构造方法执行之前执行,在类中都可以定义多个。

不同点:静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。
    静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new
    一次就执行一次。
    
总结:静态代码块,在虚拟机加载类的时候就会加载执行,而且只执行一次;  
     非静态代码块,在创建对象的时候(即new一个对象的时候)执行,每次创建对象都会执行一次

6.静态代码块和静态方法的区别?

1)静态代码块是自动执行的;	静态方法是被调用的时候才执行的。
2)静态代码块可用来初始化一些项目最常用的变量或对象;静态方法可用作不创建对象也可能需要执行的代码。

7.关于静态代码块的认识?

1)静态代码块只能定义在类里面,它独立于任何方法,不能定义在方法里面。
2)静态代码块里面的变量都是局部变量,只在本块内有效。
3)静态代码块会在类被加载时自动执行,而无论加载者是JVM还是其他的类。
4)一个类中允许定义多个静态代码块,执行的顺序根据定义的顺序进行。
5)静态代码块只能访问类的静态成员,而不允许访问实例成员。实例方法可以访问静态和实例成员。

8.不允许静态方法访问实例成员变量的原因?

因为实例成员变量是属于某个对象的,而静态方法在执行时,并不一定存在对象。
同样,因为实例方法可以访问实例成员变量,如果允许静态方法调用实例方法,将间接地允许它使用实例成员变量,所以静态方法也不能调用实例方法。基于同样的道理,静态方法中也不能使用关键字this。
  1. 静态代码优先于非静态的代码的原因?
    静态代码优先于非静态的代码,是因为被static修饰的成员都是类成员,会随着JVM加载类的时候加载而执行,而没有被static修饰的成员也被称为实例成员,需要创建对象才会随之加载到堆内存。所以静态的会优先非静态的。 

总结:
静态代码块会随着类的加载而执行,而且只执行一次。当所有的静态代码块都执行结束后会执行main函数中的输出语句,然后会去执行非静态代码块,接着是执行构造方法。

<think>嗯,用户这次是想深入了解Java静态代码块的具体应用场景和初始化过程。从对话历史看,用户之前询问过XPath在Java中的应用,现在转向了Java基础机制,可能是实际开发中遇到了类初始化顺序的问题。用户提供的参考资料很全面,特别是[^4]详细说明了代码块执行顺序和场景。需要重点突出三点:静态代码块的唯一执行性、类加载触发时机、以及与构造代码块的区别。用户似乎更关注实际使用场景而非纯理论,可以多举些真实开发中的例子。注意到用户可能是中级开发者——既需要准确理解机制,又关心如何应用到项目中。比如配置加载、日志初始化这些场景就很实用。另外要强调和SpringBean初始化的差异,这在实际整合框架时容易出错。示例代码要精简但完整,展示多个类继承时的执行顺序特别重要。表格对比三种代码块的形式和执行特征能帮助用户快速区分。最后的问题设计覆盖了常见陷阱:比如静态方法调用是否触发初始化、父子类顺序等。需要提醒潜在误区:静态代码块里不能直接使用非静态成员,这点在开发中容易被忽略。补充的"注意事项"部分专门提了异常处理也很必要,因为初始化错误通常会导致严重的NoClassDefFoundError。</think> ### Java静态代码块详解:使用场景与初始化过程 #### 一、核心功能与特点 1. **唯一性执行**:静态代码块在类首次加载到内存时执行**且仅执行一次** *示例*:数据库驱动注册(如`Class.forName("com.mysql.jdbc.Driver")`) ```java public class DatabaseConnector { static { try { Class.forName("com.mysql.cj.jdbc.Driver"); // 驱动注册[^4] } catch (ClassNotFoundException e) { e.printStackTrace(); } } } ``` 2. **执行时机**(满足任一条件即触发): - ✅ 类首次实例化(`new`操作) - ✅ 访问类的静态成员(属性和方法)[^4] - ✅ 子类初始化时触发父类加载 3. **初始化顺序**(类加载时): ``` 父类静态代码块 → 子类静态代码块 → 父类构造代码块 → 父类构造器 → 子类构造代码块 → 子类构造器 ``` *图示*: ```java class Parent { static { System.out.println("父类静态块"); } // 1 { System.out.println("父类构造块"); } // 3 } class Child extends Parent { static { System.out.println("子类静态块"); } // 2 { System.out.println("子类构造块"); } // 5 Child() { System.out.println("子类构造器"); } //6 } // new Child() 输出顺序:1→2→3→父类构造器→5→6 ``` --- #### 二、典型使用场景 | **场景** | **示例** | **优势** | |--------------------------|------------------------------------------|------------------------------| | 全局配置加载 | 读取配置文件初始化缓存 | 避免重复读取[^4] | | 资源预加载 | 初始化日志系统、连接池 | 提升首次访问性能 | | 注册驱动/服务 | JDBC驱动、Dubbo服务注册 | 符合"只执行一次"需求[^1] | | 复杂静态变量初始化 | 初始化静态Map/集合 | 替代冗长的静态属性声明 | --- #### 三、代码块类型对比 | **类型** | **语法** | **执行时机** | **执行次数** | |----------------|--------------------|----------------------------|-------------| | 静态代码块 | `static {...}` | 类加载时 | 1次 | | 构造代码块 | `{...}` | 每次`new`对象时 | N次 | | 静态属性初始化 | `static int x=5;` | 类加载时(与静态块同优先级)| 1次 | > ⚠️ **重要规则**:静态代码块中**只能访问静态成员**,若尝试调用实例方法将编译失败 --- #### 四、实战示例 ```java public class ConfigLoader { private static Map<String, String> configMap; // 静态块初始化配置 static { configMap = new HashMap<>(); try (InputStream is = getResourceAsStream("config.properties")) { Properties prop = new Properties(); prop.load(is); prop.forEach((k, v) -> configMap.put((String)k, (String)v)); } catch (IOException e) { throw new RuntimeException("配置加载失败", e); } System.out.println("全局配置初始化完成"); // [^4] } // 通过静态方法获取配置 public static String getConfig(String key) { return configMap.get(key); } } // 首次调用触发初始化:ConfigLoader.getConfig("timeout") ``` --- #### 五、注意事项 1. **命名空间隔离**:静态块中定义的变量作用域仅限于该代码块 2. **异常处理**:必须捕获检查型异常,否则导致`ExceptionInInitializerError` 3. **性能影响**:避免在静态块中执行耗时操作(如网络请求) 4. **循环依赖**:类A的静态块调用类B,而类B的静态块又调用类A,会导致栈溢出 5. **与Spring冲突**:Spring管理的Bean中静态执行**早于**`@PostConstruct`方法[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值