Java类装载机制指的是JVM如何加载和查找类的过程。在Java程序运行过程中,JVM需要定位并加载需要使用的类文件,Java类装载机制便是完成这个过程的。
Java 类装载有五个过程:加载、验证、准备、解析和初始化。以下是Java类装载的详细使用攻略。
1. 加载
加载是指将类的字节码数据加载到内存中,并为之创建一个 java.lang.Class 对象。加载阶段需要执行如下三个过程:
- 通过类的全名获取定义此类的二进制字节流。
- 将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。
2. 校验
校验是指对加载的类文件进行验证,确保符合JVM规范和应用规范。校验阶段的主要任务就是进行以下四个检查:
- 文件格式的验证:检查class字节码的格式是否合法。
- 元数据的验证:对类自身信息进行语义分析,保证不存在不符合Java语言规范的元数据信息。
- 字节码验证:通过数据流检查和控制流检查等方式,确定代码语义是合法的、符合逻辑的。
- 符号引用验证:保证解析动作能正确执行。
3. 准备
准备是指为类的静态变量分配内存并赋予初始值的过程。这些变量所使用的内存都将在方法区中进行分配。注意,这时候进行默认初始化,所以不会对类的静态变量进行显示赋值。
4. 解析
解析是Java将类的二进制数据中的符号引用转换为直接引用的过程。换而言之,就是虚拟机将常量池中的符号引用替换为直接引用的过程。解析一般发生在以下情况:
- 调用类的静态方法或属性时,解析类获得程序要加载的对象。
- 常量池中的符号引用转变为直接引用。
5. 初始化
初始化是类装载的最后一个阶段,也是真正意义上类准备开始执行的阶段。初始化阶段是执行类构造器
至此,Java类装载机制的所有过程都分析完毕。下面通过两个示例来详细说明Java类装载机制的实现过程。
示例一:动态代理类装载和执行
我们通过动态代理示例看看一个类的初始化过程。在Java中,动态代理技术是很常用的,可以通过动态代理实现对象的远程调用等。下面是一个简单的动态代理示例代码。
public interface HelloService {
void sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
public class HelloProxy implements InvocationHandler {
Object target;
public HelloProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before Method Invoke");
Object result = method.invoke(target, args);
System.out.println("After Method Invoke");
return result;
}
}
public class Main {
public static void main(String[] args) {
// 创建被代理对象
HelloService helloService = new HelloServiceImpl();
// 创建代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
HelloService.class.getClassLoader(),
new Class[]{HelloService.class},
new HelloProxy(helloService)
);
// 调用方法
proxy.sayHello("world");
}
}
在运行该示例过程中,Java类装载机制的过程如下:
- 首先加载
HelloService
接口。由于该接口并没有任何实现,所以Java只将其字节码加载到内存中,不会进行进一步的校验、准备和初始化操作。 - 加载
HelloServiceImpl
类,由于该类实现了HelloService
接口,所以在进行加载后,Java会进行校验、准备、初始化等操作并生成HelloServiceImpl
类的对象。 - 加载
HelloProxy
类,由于该类实现了InvocationHandler
接口,所以在加载后,Java会进行校验、准备、初始化等操作,并生成HelloProxy
类的对象。 - 在
Main
类中,先创建了HelloServiceImpl
的一个实例,通过JVM方法Proxy.newProxyInstance()
获取HelloService
的一个代理对象。代理对象的实例化过程完全依赖于动态实现,所以需动态生成字节码。Java会先加载HelloProxy
类,由于HelloProxy
实现了InvocationHandler
接口,这时 Java会将该对象转化为字节码,在运行时动态生成代理类。这个过程比较常见,前几个步骤详见前面的分析内容。代理类实现了HelloService
接口,并将HelloProxy
实例作为目标对象传递进去,代理对象转化为了默认实现的HelloService
接口,以此完成了代理关系的建立。
整个过程中,Java类装载机制主要用到了对 HelloServiceImpl
、HelloProxy
和代理类的加载、校验、准备和初始化等过程,我们可以清晰地在代码执行过程中看到加载类(使用 ClassLoader
等类),校验类(字节码格式是否正确),运行静态代码块(初始化类),以及动态生成代理类等过程。
示例二:静态变量初始化
下面通过一个静态变量初始化的示例来看看在Java类装载机制中的实现过程。
public class StaticTest {
private static int num = 1;
private static final String s = "Hello, world";
static {
System.out.println("静态代码块。。。");
num = 2;
}
public static void main(String[] args) {
System.out.println(Parent.num);
System.out.println(Parent.s);
}
}
在运行该示例过程中,Java类装载机制的过程如下:
- 首先加载
StaticTest
类文件。 - 在加载静态变量
num
时,执行赋值操作,并将赋值结果2存储到内存中。当第二次访问时,直接从内存中取值输出。 - 在加载静态变量
s
时,会因为s
为final
,这时候赋值操作会直接跳过。在静态代码块中将num
的值设置为2。
整个过程中,Java类装载机制主要用到了对 StaticTest
类的加载、静态变量num
和 s
的赋值操作,静态代码块最后对 num
的赋值操作。我们根据代码实际执行过程,可以看到Java类装载机制对于静态变量赋值等操作的处理过程。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:什么是Java类装载机制? - Python技术站