类的加载、连接和初始化

本文将会介绍JAVA类的加载、连接和初始化知识,并且重点介绍Java反射的相关内容。

类的加载、连接和初始化

系统课程在第一次使用某个类的时候加载该类,也可能采取预加载机制来加载某个类。

1JVM和类
注意:当调用Java命令启动运行某个Java程序时,该命令将会启动一个Java虚拟机进程,不管该Java程序有多么复杂,改程序启动了多少个线程,他们都处于该Java虚拟机进程里。
当系统出现以下几种情况时,JVM进程将被终止。
1.程序运行正常结束;
2.程序执行过程中到使用System.exit()或者Runtime().exit()代码处结束程序。
3.程序执行过程中遇到未捕获的异常或错误而结束。
4.程序所在平台强制结束了JVM进程。
从上面的介绍可以看出,当Java程序运行结束时,JVM进程结束,该进程在内存中的状态就会丢失。

举一个简单例子,例如:

public class A{
	public static int a=6;
}
public class ATest1{
	public static void main(String[] args){
		var a =new A();
		a.a++;
		System.out.println(a.a);
	}
}
public class ATest2{
	public static void main(String[] args){
		var b= new A();
		System.out.println(b.a);
	}
}

依次执行ATest1.java ,ATest2.java,请问ATest2打印的值是多少?
结果是6,运行ATest1和ATest2是两次运行JVM进程,第一次运行JVM结束后,它对A类所做的修改将全部丢失,第二次运行JVM时将会再次初始化A类。

2类的加载

当程序主动使用某个类时,如果该类还未被夹在到内存中,则系统会通过加载、连接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将连续完成这三个步骤,所以有事也把这三个步骤统称为类加载或者类初始化。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者也可以通过继承ClassLoader基类来创建自己的类加载器。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有以下几种来源。
1.从本地文件系统加载class文件。
2.从JAR包加载class文件。
3.通过网络加载class文件。
4.把一个Java源文件动态变异,并执行加载。
类加载器通常无需等到"首次使用"该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。

3类的连接

当类被加载之后,系统位置生成一个对应的Class对象,接着就会进入到连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可以分为如下三个阶段。
(1)验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
(2)准备:类准备阶段则负责为类的类变量分配内存,并设置默认初始值。
(3)解析:将类的二进制数据中的符号引用替换成为直接引用。

4类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。在Java类中对类变量指定初始值有两种方式:
(1)声明类变量时制定初始值;
(2)使用静态初始化块为类变量制定初始值。
例如:

public  class Test{
	//声明变量时指定初始值
	static int a=5;
	static int b;
	static int c;
	static {
	//使用静态初始化块为变量b指定初始值
		b=6;
	}
}

对于上面代码,程序为类变量a、b都显示指定了初始值,所以这两个类变量的值分别时5、6,但类变量c则没有制定初始值,他将采取默认初始值0。
声明变量时制定初始值,静态初始化块都将被当成类的初始化语句,JVM会按照这些语句在程序中的排列顺序依次执行他们,例如下面的类。

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

上面代码先在静态初始化块中为b变量赋值,此时类变量b的值为6;接着程序向下执行,执行到1号代码处,这行代码也属于该类的初始化语句,所以程序再次为类变量b赋值。也就是说,当Test类初始化结束后,该类的类变量b的值为9.
JVM初始化一个类包含如下几个步骤。
1.假如这个类还没有被加载和连接,则程序先加载并连接该类。
2.假如该类的直接父类还没有被初始化,则先初始化其直接父类。
3.假如该类中有初始化语句,则系统一次执行这些初始化语句。
当执行第二个步骤时,系统对直接父类的初始化步骤也遵循此步骤1~3;如果该直接父类又有直接父类,则系统再次重复这三个步骤来初始化这个父类…依次类推,所以JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有父类(包括直接父类和间接父类)都会被初始化。

5类初始化的动机

当Java程序首次通过下面6种方式来使用某个类或者接口时,系统就会初始化该类或者接口。
1.创建类的实例。为某个类创建实例的方式包括:使用new操作符来创建实例,通过反射来创建实例,通过反序列化来创建实例。
2.调用某个类的类方法。
3.访问某个类或者接口的类变量,或为该类变量赋值。
4.使用反射方式来强制创建某个类或接口对应java.lang.Class对象。例如代码Class.forName(“Person”),如果系统还未初始化Person类,则这行代码将会导致该Person类被初始化,并返回Person类对应java.lang.Class对象。
5.初始化某个类的子类。当初始化某个类的子类时,该子类的所有父类都可以被初始化。
6.直接使用java.exe命令来运行某个主类。当运行某个主类时,程序会先初始化该类。
但是,对于final型类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于"宏变量"。Java编译器会在编译时直接把这个类变量出现的地方替换成为他的值,因此即使程序使用该静态类变量,也不会导致该类的初始化。

发布了26 篇原创文章 · 获赞 5 · 访问量 1925

猜你喜欢

转载自blog.csdn.net/weixin_43840862/article/details/103577794