类初始化与类对象初始化

来源:互联网 发布:淘宝花草茶 那么便宜 编辑:程序博客网 时间:2024/05/16 15:11

类的生命周期:

加载——> 验证、准备、解析——>初始化——>使用——>卸载

类初始化进行的5种情况:

1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象的时候、读取或者设置一个类的静态字段(被final修饰的除外),以及调用一个类的静态方法

2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有初始化过,则需要先触发其初始化

3.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化

4.当虚拟机启动时,虚拟机初始化一个main方法的类

5.当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandler实例后的解析结果REF_getStatic、REF_putstaic、REF_involeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先触发其初始化


类初始化阶段是执行类构造器<clinit>()方法的过程。

<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。

<clinit>方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显示得调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。

由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作。

<clinit>()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有类变量,那么编译器可以不为这个类生产<clinit>()方法

接口中不鞥使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法,但接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。

虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,哪儿只会有一个线程去执行这个类的<clinit>方法,其他线程都需要阻塞等待,知道活动线程执行clinit()方法完毕。如果一个类的<clinit>()方法有耗时很长的操作,就可能造成多一个进程阻塞。

0 0
原创粉丝点击