• 虚拟机类加载--2.类的初始化


    初始化是类加载过程的最后一步,但由于比较重要,故放在前面先讲。

    在前面的连接(准确来说是准备)阶段,类的变量已经被赋予默认值(如int类型为0,布尔类型为false,引用类型为null等)。而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。或者可以从另一个角度来表达:初始化阶段是执行类构造器<clini>()方法的过程。

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

    2. <clinit>()方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此,在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object;

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

     1 package com.khlin.initialization;
     2 
     3 public class App {
     4 
     5     public static void main(String[] args) {
     6         System.out.println(Sub.b);
     7     }
     8 
     9     static class Parent {
    10         public static int a = 1;
    11         static {
    12             a = 2;
    13         }
    14     }
    15 
    16     static class Sub extends Parent {
    17         public static int b = a;
    18     }
    19 }

    4.<clinit>()方法对于类或接口并不是必需的,如果类没有静态语句块,也没有赋值动作,那么编译器可以不为这个类生成<clinit>()方法;

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

    6.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。单例的initialization on demand holder模式就是利用了这一特性。如果在一个类的<clinit>()方法中有耗时很长的操作,就有可能造成多个进程阻塞。需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出<clinit>()之后,其他线程唤醒后不会再次进入<clinit>()方法。同一个类加载器下,一个类型只会初始化一次。

     1 package com.khlin.initialization;
     2 
     3 public class EndlessLoop {
     4 
     5     static {
     6         if (true) {
     7             System.out.println(Thread.currentThread() + " init EndlessLoop");
     8             while (true) {
     9             }
    10         }
    11     }
    12 
    13 }
     1 public static void main(String[] args) {
     2         Runnable runnable = new Runnable() {
     3 
     4             @Override
     5             public void run() {
     6                 System.out.println(Thread.currentThread() + " is running....");
     7                 EndlessLoop endlessLoop = new EndlessLoop();
     8                 System.out.println(Thread.currentThread() + " runs over");
     9             }
    10         };
    11 
    12         Thread threadA = new Thread(runnable);
    13         Thread threadB = new Thread(runnable);
    14         threadA.start();
    15         threadB.start();
    16     }

    结果会显示:

  • 相关阅读:
    jdk1.8安装与配置
    java编译器——idea的安装
    原型设计作业
    案例分析作业
    202103226-1 编程作业
    阅读任务
    准备工作
    java课程总结
    第十四周总结
    第十三周总结
  • 原文地址:https://www.cnblogs.com/kingsleylam/p/6696991.html
Copyright © 2020-2023  润新知