类加载,初始化

java虚拟机与程序的生命周期

1.java虚拟机结束生命周期的原因

     1.执行system.exit()方法。

     2.程序正常执行结束。

     3. 程序在执行过程中遇到了异常或错误终止。

     4.由于操作系统出现错误而导致java虚拟机进程终止。

2.类的加载、连接与初始化

     加载:查找并加载类的二进制数据。类的加载时将类的.class文件中的二进制数据读入到内存中,将其存放到数据区的方法区中,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。加载.class文件的方式:从本地系统中直接加载;通过网络下载.class文件;从zip.jar等归档文件中加载.class文件;从专用数据库中提取.class文件;将java源文件动态编译成.class文件

    类的加载的最终产品是位于堆区中的Class对象;class对象封装了类在方法区内的数据结构,并且向java程序员提供了访问数据结构的接口。

     连接:验证:确保被加载的类的正确性。

              准备: 为类的静态变量分配内存,并将其初始化为默认值。

              解析:把类中的符号引用转换为直接引用。

     初始化:为类的静态变量赋予正确的初始值。

3. java程序对类的使用方式可分为两种,主动使用和被动使用;所有的java虚拟机实现必须在每个类或者接口被java程序首次主动使用时才初始化他们。java的主动使用主要分为6中方式:

     创建类的实例;

     访问某个类或接口的静态变量,或者对该静态变量赋值;

     调用类的静态方法;

     反射(class.forName("com.test"))

     初始化一个类的子类

     java虚拟机启动时被标明为启动类的类

    除了以上6中情况,其他情况下都属于被动使用,都不会引起类的初始化。

4.类的加载

  有两种类型的类加载器

     java虚拟机自带的加载器:根类加载器(Bootstrap)、扩展类加载器(Extension)、系统类加载器(System)

     用户自定义的类加载器 :java.lang.ClassLoader的子类;用户可以定制类的加载方式

     JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中就遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类才报告错误(LinkageError错误);如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。


class Singleton{

public static int counter1;
public static int counter2=0;
private static Singleton singleton = new Singleton();
private Singleton(){
counter1++;
counter2++;
}
public static Singleton getInstance(){
return singleton;
}
}
class Test{

}
public class JvmTest1 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(Singleton.class.getClassLoader());

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

执行main方法中,调用了singleton 的静态方法,为静态变量分配分配空间并且赋给默认值,其中 singleton 为null,counter1 为0,counter2为0,然后进行初始化,调用singleton 的构造方法,导致counter1 =1,counter2=1;然后counter1没有初始化还是1,counter2初始化为0,所以最终结果是

sun.misc.Launcher$AppClassLoader@157c2bd
counter1 = 1
counter2 = 0

class Singleton{

public static int counter1;
public static int counter2=0;
private static Singleton singleton = new Singleton();
private Singleton(){
counter1++;
counter2++;
}
public static Singleton getInstance(){
return singleton;
}
}
class Test{

}
public class JvmTest1 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(Singleton.class.getClassLoader());

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

同理可分析

sun.misc.Launcher$AppClassLoader@157c2bd
counter1 = 1
counter2 = 1

5.类的初始化

类的初始化步骤:

  1.加入这个类没有被加载和连接,那就先进行加载和连接

  2.加入类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类。

  3. 假如类中存在初始化语句,那就依次执行这些初始化语句。

   注意:1.final类型的编译常量不会到时类的初始化。

            2.当java虚拟机初始化一个类时,要求他的所有父类都已经被初始化,但是这条规则并不适用于接口。

                    在初始化一个类时,并不会先初始化它所实现的接口。

                    在初始化一个接口时,并不会先初始化它的父接口。

    因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才回导致该接口的初始化。

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

原文地址:https://www.cnblogs.com/kaikailele/p/3916594.html