首页 > 代码库 > 类的加载机制和反射——一、类的加载、连接和初始化

类的加载机制和反射——一、类的加载、连接和初始化

类的加载、连接和初始化

1.JVM和类

(1)当调用java命令运行某个Java程序时,该命令会启动一个Java虚拟机进程,不管该Java程序有多么复杂,该程序启动了多少线程,他们都处于该Java虚拟机进程里。

(2)同一个JVM的所有线程、所有变量都处于同一个进程里,他们都使用该JVM进程的内存区。

  下面的ATast1和ATest2的输出结果分别是7和6,因为这两个不是位于同一个JVM中的。

public class A {
    //定义该类的类变量
    public static int a = 6;
}
public class ATest1 {
    public static void main(String args[]){
        //创建A的实例
        A a = new A();
        //让a实例的类变量a的值自增
        a.a++;
        System.out.println(a.a);
    }
}
public class ATest2 {
    public static void main(String[] args) {
        A b = new A();
        System.out.println(b.a);
    }
}

(3)JVM进程被终止的情况:

  ①程序运行到最后正常结束;

  ②程序运行到使用System.exit()或者Runtime.getRuntime().exit()代码处结束程序;

  ③程序执行中遇到未捕获的异常错误而结束;

  ④程序所在平台强制结束了JVM进程。

2.类的加载

(1)当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对该类进行初始化。JVM将会连续完成这三个步骤,所以有时也吧这三个步骤统称为类加载或类初始化;

(2)类加载指的是将类的class文件读入内存,并为之创建一个Class对象,当程序使用任何类时,系统都会为之创建一个Class对象;

(3)类的加载由类加载器完成,类加载器通常由JVM提供,同时,开发人员也可以通过继承ClassLoader类来创建自己的类加载器;

(4)通过使用不同的类加载器可以从不同来源加载类的二进制数据,类的二进制数据通常有如下几个来源:

  1)从本地文件系统加载class文件;

  2)从jar包加载class文件;

  3)通过网络加载class文件;

  4)把一个java源文件动态编译,并执行加载。

(5)类加载器无须等到首次使用类时才加载该类,Java虚拟机允许系统预先加载某些类。

3.类的连接

(1)当类被加载后,系统为之生成一个对应的Class对象,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。

(2)类连接的三个阶段:

  1)验证:验证被加载的类是否有正确的内部结构,并和其他类协调一致;

  2)准备:类的准备阶段负责为类的类变量分配内存,并设置默认初始值;

  3)解析:将类的二进制数据中的符号引用替换成直接引用。

4.类的初始化

(1)类初始化阶段,虚拟机负责对类进行初始化,主要是对类变量进行初始化。

(2)对类变量指定初始值的两种方式:

  1)声明类变量时指定初始值;

  2)使用静态代码块为类变量指定初始值。

public class Test {
    //声明变量a时指定初始值;
    static int a =5;
    static int b;
    //没有指定初始值,也没有用静态代码块指定初始值,其默认为0
    static int c;    
    //静态代码块中为b设置初始值
    {
        b = 6;
    }
}    

(3)声明变量时指定初始值,静态初始化块都被当成类的初始化语句,JVM会按这些语句在程序中的排列顺序依次执行。

public class Test {
    static{
        //使用静态初始化块为变量b指定初始值
        b = 6;
        System.out.println("==============");
    }
    //声明a时指定初始值
    static int a = 5;
    static int b = 9;
    static int c;
    public static void main(String args[]){
        System.out.println(Test.b);
    }
}

(4)JVM初始化一个类的步骤:

  1)若该类未被加载和连接,则先加载、连接该类;

  2)若该类的直接父类未被初始化,则先初始化该类的直接父类;

  3)若类中有初始化语句,则依次执行这些初始化语句。

5.类初始化的时机

(1)当Java程序首次通过下面六种方式来使用某个类或者接口时,系统会初始化该类或接口:

  1)创建类的实例(包括使用new关键字、通过反射创建、通过反序列化创建);

  2)调用某个类的静态方法;

  3)访问某个类的类变量,或为该类变量赋值;

  4)初始化某个类的子类;

  5)使用反射强制创建某个类或接口对应的Class对象;

  6)适应java.exe命令来运行某个主类;

(2)对于一个final型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于一个常量。Java编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此程序使用该静态类变量(final)不会导致该类的初始化;

(3)当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。使用Class的forName()静态方法才会导致强制初始化该类。

class Tester{
    static{
        System.out.println("Tester类的静态初始化块。。。。。。");
    }
}
public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException{
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        //下面语句仅仅是加载Tester类
        cl.loadClass("Test4.Tester");
        System.out.println("系统加载Tester类");
        //这里才会初始化Tester类
        Class.forName("Test4.Tester");
    }
}

 

 

 

类的加载机制和反射——一、类的加载、连接和初始化