在jvm知识总结的时候,看了不少博客,发现自己学习的还不扎实,所以学习一下别人的博客,记录下
参考 Java 类加载机制(阿里面试题)-何时初始化类

类加载机制

类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段

java默认最早日期 java初始化时间_java默认最早日期

其中加载、验证、准备、初始化、和卸载这5个阶段的顺序是确定的。而解析阶段不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java的运行时绑定。

需要明确的是初始化 执行初始化 静态变量,并不会执行 执行类似构造器等方法,根据程序员通过程序制定的主观计划初始化类变量和其他资源。初始化过程其实是执行类构造器()方法的过程

关于初始化:JVM规范明确规定,有且只有5种中情况必须执行对类的初始化(加载、验证、准备自然再此之前要发生):

  1. 遇到new,getstatic,putstatic,invokestatic这失调字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候
  2. 使用java.lang.reflect包的方法对类进行反射调用时,如果类没初始化,则需要初始化
  3. 当初始化一个类时,如果发现父类没有初始化,则需要先触发父类初始化。
  4. 当虚拟机启动时,用户需要制定一个执行的主类(包含main函数的类),虚拟机会先初始化这个类。
  5. 但是用JDK1.7启的动态语言支持时,如果一个MethodHandle实例最后解析的结果是REF_getStatic、REF_putStatic、Ref_invokeStatic的方法句柄时,并且这个方法句柄所对应的类没有进行初始化,则要先触发其初始化。

通过子类来引用父类的静态字段,不会导致子类初始化,而且只是初始化,没有执行任何的构造器语句!

class SSClass
{
    static
    {
        System.out.println("SSClass");
    }
     
    public SSClass() {
    	System.out.println("SSClass init");
    }
}   
 class SuperClass extends SSClass
{
    static
    {
        System.out.println("SuperClass init!");
    }
 
    public static int value = 123;
 
    public SuperClass()
    {
        System.out.println("init SuperClass");
    }
}
 class SubClass extends SuperClass
{
    static
    {
        System.out.println("SubClass init");
    }
 
    static int a;
 
    public SubClass()
    {
        System.out.println("init SubClass");
    }
}
public class NotInitialization
{
    public static void main(String[] args)
    {
        System.out.println(SubClass.value);
    }
}

执行结果:
SSClass
SuperClass init!
123


通过数组定义来引用类,不会触发此类的初始化

public class NotInitialization
{
    public static void main(String[] args)
    {
        SuperClass[] sca = new SuperClass[10];
    }
}

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


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

利用静态内部类实现线程安全的单例模式

虚拟机会保证一个类的 <clinit>( )方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执
行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有
耗时很长的操作,就可能造成多个线程阻塞,在实际应用中这种阻塞往往是隐藏的。
static class DeadLoopClass
    {
        static
        {
            if(true)
            {
                System.out.println(Thread.currentThread()+"init DeadLoopClass");
                while(true)
                {
                }
            }
        }
    }
 
    public static void main(String[] args)
    {
        Runnable script = new Runnable(){
            public void run()
            {
                System.out.println(Thread.currentThread()+" start");
                DeadLoopClass dlc = new DeadLoopClass();
                System.out.println(Thread.currentThread()+" run over");
            }
        };
 
        Thread thread1 = new Thread(script);
        Thread thread2 = new Thread(script);
        thread1.start();
        thread2.start();
    }
}


执行结果:
Thread[Thread-0,5,main] start
Thread[Thread-1,5,main] start
Thread[Thread-0,5,main]init DeadLoopClass
需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>( )方法的那条线程退出<clinit>( )方法后,其他线程唤醒之后
不会再次进入<clinit>( )方法。同一个类加载器下,一个类型只会初始化一次。
public class DealLoopTest
{
    static class DeadLoopClass
    {
        static
        {
            if(true)
            {
            	 System.out.println(Thread.currentThread() + "init DeadLoopClass");
            	    try
            	    {
            	        TimeUnit.SECONDS.sleep(10);
            	    }
            	    catch (InterruptedException e)
            	    {
            	        e.printStackTrace();
            	    }
            }
        }
    }
 
    public static void main(String[] args)
    {
        Runnable script = new Runnable(){
            public void run()
            {
                System.out.println(Thread.currentThread()+" start");
                DeadLoopClass dlc = new DeadLoopClass();
                System.out.println(Thread.currentThread()+" run over");
            }
        };
 
        Thread thread1 = new Thread(script);
        Thread thread2 = new Thread(script);
        thread1.start();
        thread2.start();
    }
}


Thread[Thread-0,5,main] start
Thread[Thread-1,5,main] start
Thread[Thread-1,5,main]init DeadLoopClass (之后sleep 10s)
Thread[Thread-1,5,main] run over
Thread[Thread-0,5,main] run over