疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java类加载和初始化


 

   

  Java程序运行由java虚拟机负责。类从加载到虚拟机内存到卸载出内存,包括

  加载-----链接-----初始化-----使用------卸载

  链接具体包括:验证-----准备-----解析

  加载:由类加载器执行,查找字节码并从这些字节码中创建一个Class对象。

  链接:验证类中的字节码;为静态域分配存储内存并赋予默认值;解析这个类创建的对其他类的所有

  引用。

  初始化:该类具有基类,则对其初始化,执行静态初始化和静态初始化块。

  类初始化的时机:程序中首次使用才初始化。

  首次主动使用:

  1. 创建类的实例

  2. 访问类的静态变量

  3. 调用类的静态方法

  4. 反射调用

  5. 初始化类的子类,如果父类为被初始化则先初始化父类

  6. 虚拟机启动时指定的启动类(包含main方法)虚拟机会先初始化该类

  类的初始化步骤:

  1, 没有被加载和链接的话就先进行加载连接。

  2, 有直接父类并且还没有初始化那就先初始化直接父类。

  3, 存在初始化语句,就按顺序依次执行初始化语句。(初始化语句包括静态语句和静态代码块)

  注:

  1. 如果是一个static final的编译器常量(常量池),就不需要对该类进行初始化就可以被读取,

  但是不是编译器常量对其访问将强制进行类的初始化。

  [java]

  class A{

  public static final int b = 10;

  public static final int c = new Random().nextInt(10);

  static {

  System.out.println("A类被初始化");

  }

  }

  public class Test{

  public static void main(String[] args) {

  int i = A.b; // 不会导致初始化,因为b是一个编译时常量

  int ii = A.c; // 回导致初始化,c在编译时不能确定

  }

  }

  2.static域不是final的,那么访问它必须要进行链接和初始化。

  3.只有当访问的静态变量或方法是在当前类或接口中定义时,才认为是对类或接口的主动使用。

  [java]

  interface A{

  int a = 10;

  }

  class AImp implements A{

  static {

  System.out.println("A类被初始化");

  }

  }

  public class Test{

  public static void main(String[] args) {

  int i = A.a; // 不会导致初始化,访问的变量不是当前类中定义的

  }

  }

  4.使用.class创建Class的引用时,不会自动的初始化。初始化被延迟到了对静态方法或者静态域的

  首次调用时才执行。

  [java]

  class A{

  public static int b = 10;

  static {

  System.out.println("A类被初始化");

  }

  }

  public class Test{

  public static void main(String[] args) {

  Class class1 = A.class; // 没有输出

  int aa = A.b; // 这句会导致输出 A类被初始化

  try {

  Class class2 = Class.forName("A"); //注释掉上面两句 output: A类被初始化

  } catch (ClassNotFoundException e) {

  e.printStackTrace();

  }

  }

  }

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

  类中成员的初始化

  1.为类中变量赋初值在Java里面可以在定义类成员的地方为其赋值(c++里不能这样),也可以通过构

  造函数进行初始化,并且构造函无法阻止自动初始化也进行。

  [java] view plaincopy在CODE上查看代码片派生到我的代码片

  class Test{

  int a;

  Test(){a = 10;}// a首先被置为0,

  然后变成7.

  }

  2.变量的初始化顺序取决于变量定义的顺序。他们在任何方法(包括构造函数)调用之前得到初始化

  。

  [java]

  class A{

  public int i = 10;

  public A(){

  System.out.println("构造函数->" + i);

  i = 20;

  System.out.println("构造函数->" + i);

  }

  static {

  System.out.println("A类被初始化");

  }

  }

  public class Test{

  public static void main(String[] args) {

  A a = new A(); // 执行构造函数之前就饿比赋值成10了,执行构造函数后又被置为20

  // output :

  // A类被初始化

  // 构造函数10

  // 构造函数20

  }

  }

  3.静态数据的初始化,只占有一份内存区域。静态初始化只有在必要的时刻才会进行。必要的时刻指

  的是类被主动使用时。

  4.初始化顺序是先静态后非静态。前面讨论初始化过程只说静态语句和静态代码块,其实也会有非静

  态成员属性的初始化,并且是先初始化静态再初始化非静态的。不可能存在非静态成员初始化了而静

  态成员未被初始化的情况。

  一个奇怪的问题

  [java]

  class Singleton{

  // private static Singleton singleton = new Singleton(); // 1

  public static int counter1;

  public static int counter2 = 0;

  private Singleton(){

  counter1++;

  counter2++;

  }

  private static Singleton singleton = new Singleton(); // 2

  public static Singleton getInstance(){

  return singleton;

  }

  }

  public class Test{

  public static void main(String[] args) {

  Singleton singleton = Singleton.getInstance();

  System.out.println("counter1 = " + singleton.counter1);

  System.out.println("counter2 = " + singleton.counter2);

  }

  }

  输出:

  放在位置1

  1 Singleton singleton =Singleton.getInstance();由于是调用该类的静态方法,因为检查还未加载

  ,接下来会依次加载,链接,初始化。

  2 在链接的准备过程中singleton 为null counter1为0 counter2为0

  3 在初始化过程,按顺序初始化

  3.1 初始化 private staticSingleton singleton = new Singleton();会执行构造函数,counter1为

  1,counter2为1

  3.2 初始化public static intcounter1; 其实没有初始化的值,因此还是之前的值为1

  3.3 初始化public static intcounter2 = 0; 将0赋值给counter2为0

  输出为 1 和 0

  counter1 = 1

  counter2 = 0

  放在位置2就很容易理解

  counter1 = 1

  counter2 = 1