美文网首页
类加载机制的初始化

类加载机制的初始化

作者: 别拿爱情当饭吃 | 来源:发表于2018-12-20 15:39 被阅读7次

类初始化阶段是类加载过程的最后一步,这一步操作是由虚拟机主导和控制的。

你看《深入理解java虚拟机》书的时候,会看到这样子的一个东西:<clinit()>。

<clinit()>是什么?

<clinit()>方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,收集的顺序由源文件中出现的语句顺序决定的。
提取关键知识点:<clinit()>就是由类变量和静态语句块组成的。

拓展:静态语句块中能访问在静态语句块前定义的变量和对变量赋值;
静态语句块中不能访问在静态语句块后定义的变量,但能对变量赋值。

/**
 * 静态语句块只能访问到定义在静态语句块前的变量;
 * 定义在静态语句块后的变量,只能赋值,不能访问。
 * @author aaron
 *
 */
public class Test {
    static {
        i = 0;
        //System.out.println(i);//定义在静态语句块后的变量,只能赋值,不能访问
    }
    static int i = 1;
    
    static int j = 2;
    static {
        j = 3;
        System.out.println(j);//定义在静态语句块前的变量,可以赋值,可以访问。
    }
}

不离题了,说回<clinit()>方法
知识点1:<clinit()>方法与类的构造函数不一样,她不需要显示的调用父类的构造器,虚拟机会保证在执行子类的<clinit()>方法前,会先执行父类的<clinit()>方法。因此虚拟机中,执行第一个<clinit()>方法的类肯定是java.lang.Object。

由于父类的<clinit()>方法先执行,这也解释了为什么父类中定义的静态语句块一定会优先于子类中定义的静态语句块。看下面的例子1:
例子1:

/**
 * output:
 * A=2
 * 2
 * @author aaron
 *
 */
public class Parent {
    static int A = 1;
    static {
        A = 2;
        System.out.println("A="+A);
    }
    public static void main(String[] args) {
        System.out.println(Sub.B);
    }
}
class Sub extends Parent{
    public static int B = A;
}

继续谈<clinit()>方法:
知识点2:<clinit()>方法对于类或接口不是必需的,如果这个类没有类变量,也没有静态语句块。
那么编译器是不会为这个类生成<clinit()>方法的。

知识点3:接口中虽然不能使用静态语句块,但可以有类变量,所以接口也可以有<clinit()>方法的。
但是接口和类稍微有点不同,接口执行<clinit()>方法时,不需要先执行它的父接口的<clinit()>方法。只有当父接口中的定义的变量被使用时,才会执行<clinit()>方法。

知识点4:虚拟机会保证一个类<clinit()>方法在多线程环境下被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit()>方法,其他线程都需要阻塞等待,直到线程执行<clinit()>完毕。(看例子2)
例子2:

/**
 * 有死循环的情况下:
 * output:
 * Thread[Thread-0,5,main]start
Thread[Thread-1,5,main]start
Thread[Thread-0,5,main]DeadLoopClass init!!!
一个线程在阻塞等待,另一个线程在死循环模拟长时间操作

没死循环的情况下:
output:
Thread[Thread-0,5,main]start
Thread[Thread-1,5,main]start
Thread[Thread-0,5,main]DeadLoopClass init!!!
Thread[Thread-0,5,main]over
Thread[Thread-1,5,main]over
虚拟机会保证一个类的<clinit()>方法在多线程环境中被正确地加锁,同步,如果多个线程同时去初始化一个类,
那么只会有一个类能执行<clinit()>方法,其他线程都需要阻塞等待,直到活动线程执行<clinit()>方法完毕,
才会继续后续操作。
 * @author aaron
 *
 */
public class TestDeadLoopClass {
    public static void main(String[] args) {
        Runnable script = new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println(Thread.currentThread()+"start");
                DeadLoopClass deadLoopClass = new DeadLoopClass();
                System.out.println(Thread.currentThread()+"over");
            }
        };
        Thread thread1 = new Thread(script);
        Thread thread2 = new Thread(script);
        thread1.start();
        thread2.start();
    }
}
class DeadLoopClass{
    static {
        if(true) {
            
            System.out.println(Thread.currentThread()+"DeadLoopClass init!!!");
            while(true){
                
            }
        }
    }
}

相关文章

  • 【Java基础】类加载过程

    要点:1、类加载机制的原理2、程序初始化的顺序3、类加载的代理模式(双亲委托机制) 一、类加载机制 JVM把cla...

  • JVM学习(二)类加载器

    目录 一、类加载器 还记得类加载机制吗?类加载机制的各阶段是加载、连接(验证、准备、解析)、初始化、使用、卸载。可...

  • 深入理解Java虚拟机之类加载机制

    虚拟机类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,顺序如下 在这五个阶段中,加载、验...

  • 2020最新JAVA核心面试知识整理283页(带详解)

    部分目录预览 部分内容预览 JVM 类加载机制 JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下...

  • 反射,注解,动态代理

    类加载机制 当调用某个类时,系统会通过加载,连接,初始化三个步骤来对该类进行初始化操作。 加载 加载是指将类的字节...

  • Java类加载机制

    Java类加载机制 类的生命周期是:加载->验证->准备->解析->初始化->使用->卸载,只有在准备阶段和初始化...

  • jvm

    类加载机制 类的生命周期:加载,链接,初始化,使用,卸载 类加载器:(待补充) 双亲委派模型:当类加载器试图加在某...

  • 深入理解JVM类加载机制

    1.类加载机制 类加载机制,指虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,...

  • 类加载原理分析&动态加载Jar/Dex

    简书 编程之乐转载请注明原创出处! JVM类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化...

  • JAVA类加载机制

    1.JAVA类加载机制 JAVA类加载机制:虚拟机把Class文件加载到内存中,并对数据进行校验,转换解析和初始化...

网友评论

      本文标题:类加载机制的初始化

      本文链接:https://www.haomeiwen.com/subject/zgvykqtx.html