[读书笔记] java类初始化

  以下内容来自周志明的《深入理解java虚拟机》:

  

  类初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。

  到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码)。
  在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。

  我们在下文会讲解<clinit>()方法是怎么生成的,在这里,我们先看一下<clinit>()方法执行过程中一些可能会影响程序运行行为的特点和细节,这部分相对更贴近于普通的程序开发人员?。
?  1. <clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。
  2. ?<clinit>()方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。
?  3. 由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作。

public class SuperClass {
    public static int value = 123;

    static {
        System.out.println("父类初始化。");
    }

    public SuperClass() {
        System.out.println("父类构造函数。");
    }
}
public class SubClass extends SuperClass {
    static {
        System.out.println("子类初始化。");
    }

    public SubClass() {
        System.out.println("子类构造函数。");
    }
}
public static void main(String[] args) {
    SubClass subClass = new SubClass();
}
//结果 父类优先于子类初始化,父类的构造函数同样优先于子类的构造函数,但是初始化优先于构造函数。
父类初始化。
子类初始化。
父类构造函数。
子类构造函数。

  4. ?<clinit>()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。
?  5. 接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法。但接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。
?  6. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞?,在实际应用中这种阻塞往往是很隐蔽的。

  --所以可以使用静态内部类来实现单利模式。

时间: 2024-10-06 00:38:47

[读书笔记] java类初始化的相关文章

[读书笔记]Java类载入过程

一. 类的生命周期 类从被载入到虚拟机内存中開始,到卸载出内存为止,有下面(如图)的生命周期: 以上"载入->验证->准备->解析->初始化"称为类的载入过程. Java虚拟机规范中没有对什么时候须要開始类载入的第一阶段进行强制约束,而是交给了虚拟机依据详细实现来自由把握. 可是对于初始化阶段,虚拟机有下面5种必须对类立即进行"初始化"的情况: (1)遇到new.getstatic.putstatic或invokestatic这4条字节码指令

读书笔记-----Java并发编程实战(一)线程安全性

线程安全类:在线程安全类中封装了必要的同步机制,客户端无须进一步采取同步措施 示例:一个无状态的Servlet 1 @ThreadSafe 2 public class StatelessFactorizer implements Servlet{ 3 public void service(ServletRequest req,ServletResponse resp){ 4 BigInteger i = extractFromRequest(req); 5 BigInteger[] fact

java类初始化

class SuperClass{ static{ System.out.println("super class init!"); } static int superInt = 1; static String superString = "supString"; } class SubClass extends SuperClass{ static{ System.out.println("sub class init!"); } stat

java类初始化顺序

java类初始化顺序 执行顺序如下: 没有继承其他类时: 静态变量 静态初始化块 变量 初始化块 构造器 继承其他类时: 父类--静态变量 父类--静态初始化块 子类--静态变量 子类--静态初始化块 父类--变量 父类--初始化块 父类--构造器 子类--变量 子类--初始化块 子类--构造器 执行顺序图:

从Java类初始化,来看代码优化

Java类初始化顺序可能引起的bug 最近编程中遇到的问题, 类的成员初始化过程大家都很了解,都是基础知识,但是有些地方很微妙,重新学习 下,来提高代码质量. 先描述下遇到的场景: 子类构造器中调用super(),然后在父类构造器中调用子类有@overwrite的方法,子类在overwrite的方法中对自己成 员赋值,log输出成功赋值,在子类new完,log打印发现部分成员变量值丢失了. 打印log发现list数据丢失了,int值还在,如下: 看了半天感觉还是很奇怪,有点不相信代码的感觉,最后

静态代码块、构造代码块、构造函数以及Java类初始化顺序

静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public HelloA(){//构造函数 } 关于构造函数,以下几点要注意:1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的.2.构造函数的作用是用于给对象进行初始化.3.一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次. 构造代码块 {//构造代码块 }

Android(java)学习笔记136:Java类初始化顺序

Java类中初试化的顺序: 由此得出Java普通类初始化顺序结论: 静态变量 静态初始化块 变量 初始化块 构造器 由此得出Java继承类初始化顺序结论: 1 .继承体系的所有静态成员初始化(先父类,后子类) 2 .父类初始化完成(普通成员的初始化-->构造函数的调用) 3 .子类初始化(普通成员-->构造函数) Java初始化顺序如图: 代码演示: class Sample { Sample(String s) { System.out.println(s); } Sample() { Sy

[读书笔记]Java类加载器

一.类与类加载器 类加载器除了在类加载阶段的作用外,还确定了对于一个类,都需要由加载它的类加载器和这个类本身一同确定其在Java虚拟机中的唯一性.通俗一点来讲,要判断两个类是否"相等",前提是这两个类必须被同一个类加载器加载,否则这个两个类不"相等". 这里指的"相等",包括类的Class对象的equals()方法.isAssignableFrom()方法.isInstance()方法.instanceof关键字等判断出来的结果. 示例:不同的类

转!!关于java类初始化顺序

原文地址:http://www.cnblogs.com/luckygxf/p/4796955.html 1.没有继承 静态变量->静态初始化块->变量->变量初始化块->构造方法 2.有继承的情况 父类静态变量->父类静态初始化块->子类静态变量->子类静态变量初始化块->父类变量初始化->父类变量初始化块->父类构造方法->子类变量初始化->子类变量初始化块->子类构造方法 --------------------------