• JVM类加载(3)—初始化


    3、初始化

    在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量(静态变量)和其他资源,或者从另外一个角度表达:初始化过程是执行类构造器<client>()方法的过程。<client>()方法:

    • <client>()方法是由编译器自动收集类中的所有类变量(静态变量)的赋值动作和静态语句块(static{})中的语句合并吃产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的,静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,静态语句块能进行赋值操作,但是不能进行访问。
    1 public class Test {
    2     static{
    3         i = 0;                 //这句能编译通过
    4         System.out.println(i); //这里编译会报错,提示“非法向前引用”
    5     }
    6     static int i;
    7 }
      • <client>()方法与类的构造函数不同,它不需要显式的调用父类构造器,虚拟机会保证在<client>()方法执行之前,父类的<client>()方法已经执行完毕。因此在虚拟机中第一个被执行的<client>()方法肯定是java.lang.Object的<client>()方法。
      • 由于父类的<client>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,如下代码,字段B的值会是2而不会是1
         1 public class Test {
         2     static class Parent{
         3         public static int A = 1;
         4         static{
         5             A = 2;
         6         }
         7     }
         8 
         9     static class Sub extends Parent{
        10         public static int B = A;
        11     }
        12     
        13     public static void main(String[] args) {
        14         System.out.println(Sub.B);
        15     }
        16 }
    • <client>()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有静态变量的赋值操作,那么编译器可以不为这个类生成<client>()方法。
    • 接口中不能使用静态语句块,但仍然有变量赋值初始化的操作,因此接口和类一样都会生成<client>()方法。但接口与类不同的是,执行接口的<client>()方法不需要执行父接口的<client>()方法,只有当父接口定义的变量被使用时父接口才会初始化。另外接口的实现进行初始化时,也不会执行接口的<client>()方法,同理除非访问了接口中定义的静态变量才会初始化接口。如果通过子类访问定义在父类中的静态变量时,只有父类会被初始化,子类则不会被初始化,如下代码清单:
       1 class Parent {
       2     public static int A = 1;
       3     static {
       4         System.out.println("this is parent");
       5     }
       6 }
       7 
       8 class Sub extends Parent {
       9     public static int B = 2;
      10     static {
      11         System.out.println("this is Sub");
      12     }
      13 }
      14 
      15 public class Test {
      16     public static void main(String[] args) {
      17         int i = Sub.A;
      18     }
      19 }
      20 // output:
      21 // this is parent
    • 虚拟机会保证一个类的<client>()方法在多线程环境中被正确的加锁、同步,如果多个线程去初始化一个类,那么只会有一个线程去执行这个类的<client>()方法,其他线程都需要阻塞等待,直到活动线程的<client>()方法执行完毕。如果一个类的<client>()方法有耗时很长的操作,就能造成多个进程阻塞,在实际应用中,这种阻塞往往是很隐蔽的。同一个虚拟机上类的<client>()方法只会执行一次。
    • 编译时的常量访问不会对该类进行初始化,如果只有在运行时才能确定,则会执行类的初始化动作,如下代码:
    •  1 import java.util.Random;
       2 
       3 class Init{
       4     static final int x = 6 / 3;
       5     static final int y = new Random().nextInt(100);
       6     static{
       7         System.out.println("init----");
       8     }
       9 }
      10 
      11 public class Test {
      12     public static void main(String[] args) {
      13         System.out.println(Init.x); //此处Init类不会进行初始化
      14         System.out.println(Init.y); //执行这条语句时,Init类才进行初始化
      15     }
      16 }
      17 // output:
      18 //2
      19 //init----
      20 //91

    类只有在被首次主动使用时,才进行初始化,类的主动使用方式:

    (1)、实例化一个类,new一个类的实例对象

    (2)、访问类的静态变量

    (3)、调用类的静态方法

    (4)、通过反射调用类

    (5)、实例化类的子类

    (6)、被标位启动类的类

    Java虚拟机执行类的初始化语句为类赋予初始值,在程序中静态变量初始化有两种方式

    (1)、在变量声明处初始化

    (2)、在静态代码块中进行初始化

    1 private static int param1 = 1;//变量声明时初始化
    2 private static int param2;
    3 static{
    4    param2 = 2; //静态代码块中进行初始化
    5 }

    针对private static int param1 = 1;在连接阶段的准备阶段时,param1变量被赋予int变量初始值0,在初始化阶段执行赋值操作,赋值为1。

  • 相关阅读:
    Android开发环境搭建全程演示(jdk+eclipse+android sdk)
    这样在web service 中的android调用soap
    lotus数据库已坏,不能分配空间
    android 的sdk在线安装失败问题处理
    Android环境变量的设置
    App列表之圆角ListView源码
    安装Android SDK出现Failed to fetch URL http://dl
    BW Delta (增量)更新方法 . 沧海
    SAP BW系统日常维护日常工作及常见的Infopackage错误 沧海
    传输一个特定的InfoObject 沧海
  • 原文地址:https://www.cnblogs.com/qiyexue/p/6822240.html
Copyright © 2020-2023  润新知