JAVA静态代码块会在类被加载时自动执行?
一、先看Java静态方法,静态变量
静态代码块
在类中,可以将某一块代码声明为静态的,这样的程序块叫静态初始化段。静态代码块的一般形式如下:
static {
语句序列
}
public class staticBlock{
//定义一个普通的main()方法
public static void main(String args[]){
System.out.println("This is main method.");
}
//定义一个静态代码块
static{
System.out.println("This is static block.");
int stVar = 0; //这是一个局部变量,只在本块内有效
}
}
编译通过后,用java命令加载本程序,会得到如下输出:
This is static block.
This is main method.
从以上输出结果中可以看出,静态代码块甚至在main方法之前就被执行。在main()方法中可以完成的任务在静态代码块中都可以完成。但是二者在执行上仍然有一些区别,main方法是整个程序启动的入口,而静态代码块是存在于某个类中的一个过程。
那是不是只要类被加载了就一定会执行静态代码块?
二、再看JVM类加载的过程
- 装载
- 连接
- 初始化
其中装载阶段又三个基本动作组成:
- 通过类型的完全限定名,产生一个代表该类型的二进制数据流
- 解析这个二进制数据流为方法区内的内部数据结
- 构创建一个表示该类型的java.lang.Class类的实例
另外如果一个类装载器在预先装载的时遇到缺失或错误的class文件,它需要等到程序首次主动使用该类时才报告错误。
连接阶段又分为三部分:
- 验证,确认类型符合Java语言的语义,检查各个类之间的二进制兼容性(比如final的类不用拥有子类等),另外还需要进行符号引用的验证。
- 准备,Java虚拟机为类变量分配内存,设置默认初始值。
- 解析(可选的) ,在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程。
当一个类被主动使用时,Java虚拟就会对其初始化,如下五种情况为主动使用:
1. 遇到new, getstatic, putstatic 或者 invokestatic 4条字节码指令时,如果类没有进行过初始化,则需要先进行初始化。这些场景包括:使用new关键字实例化对象,读取或者设置一个类的静态字段以及调用一个类的静态方法的时候。
2. 使用java.lang.reflect包的方法进行反射调用的时候,如果类没有初始化,需要进行初始化。
3. 当初始化一个类的时候发现其父类还没有初始化,需要对父类进行初始化。
4. JVM启动时,用户指定的包含main方法的那个类,需要首先进行初始化。
5. JDK1.7中动态语言的支持,解析java.lang.invoke.MethodHandle的结果为REF_getStatic, REF_putStatic, REF_invokeStatic方法的句柄时,对应的类没有初始化的时候。
实际上,static块的执行发生在“初始化”的阶段。初始化阶段,jvm主要完成对静态变量的初始化,静态块执行等工作。
下面我们看看执行static块的几种情况:
1、第一次new A()的过程会打印"";因为这个过程包括了初始化
2、第一次Class.forName("A")的过程会打印"";因为这个过程相当于Class.forName("A",true,this.getClass().getClassLoader());
3、第一次Class.forName("A",false,this.getClass().getClassLoader())的过程则不会打印""。因为false指明了装载类的过程中,不进行初始化。不初始化则不会执行static块。
再看这个例子
class MyClass1 {
static {//静态块
System.out.println("static block ");
}
}
public class Main {
Class[] classArray = {
MyClass1.class//这样引用该类,必然需要将该类加载到虚拟机中,(反射机制)
};
public static void main(String[] args){
System.out.println("hello word");
}
}
结果:
hello world
所以结论是不初始化则不会执行static块。
参考:《深入理解java虚拟机》