一、Java类加载机制
1.概述
Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
2.工作机制
类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示的对象组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
(1) 装载:查找和导入Class文件;
(2) 链接:把类的二进制数据合并到JRE中;
(a)校验:检查载入Class文件数据的正确性;
(b)准备:给类的静态变量分配存储空间;
(c)解析:将符号引用转成直接引用;
(3) 初始化:对类的静态变量,静态代码块执行初始化操作
Java程序可以动态扩展是由运行期动态加载和动态链接实现的;比如:如果编写一个使用接口的应用程序,可以等到运行时再指定其实际的实现(多态),解析过程有时候还可以在初始化之后执行;比如:动态绑定(多态);
【类初始化】
(1) 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
(2) 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
(3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
(4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
只有上述四种情况会触发初始化,也称为对一个类进行主动引用,除此以外,所有其他方式都不会触发初始化,称为被动引用
代码清单1
上述代码运行后,只会输出【---SuperClass init】, 而不会输出【SubClass init】,对于静态字段,只有直接定义这个字段的类才会被初始化,因此,通过子类来调用父类的静态字段,只会触发父类的初始化,但是这是要看不同的虚拟机的不同实现。
代码清单2
此处不会引起SuperClass的初始化,但是却触发了【[Ltest.SuperClass】的初始化,通过arr.toString()可以看出,对于用户代码来说,这不是一个合法的类名称,它是由虚拟机自动生成的,直接继承于Object的子类,创建动作由字节码指令newarray触发,此时数组越界检查也会伴随数组对象的所有调用过程,越界检查并不是封装在数组元素访问的类中,而是封装在数组访问的xaload,xastore字节码指令中.
代码清单3
对常量ConstClass.value 的引用实际都被转化为NotInitialization类对自身常量池的引用,这两个类被编译成class后不存在任何联系。
【装载】
在装载阶段,虚拟机需要完成以下3件事情
(1) 通过一个类的全限定名来获取定义此类的二进制字节流
(2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3) 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
虚拟机规范中并没有准确说明二进制字节流应该从哪里获取以及怎样获取,这里可以通过定义自己的类加载器去控制字节流的获取方式。
【验证】
虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有害的字节流而导致系统奔溃。
【准备】
准备阶段是正式为类变量分配并设置类变量初始值的阶段,这些内存都将在方法区中进行分配,需要说明的是:
这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中;这里所说的初始值“通常情况”是数据类型的零值,假如:
public static int value = 123;
value在准备阶段过后的初始值为0而不是123,而把value赋值的putstatic指令将在初始化阶段才会被执行
二、类加载器与双亲委派模型
类加载器
(1) Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar 名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用
(2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库加载。开发者可以直接使用扩展类加载器。
(3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
双亲委派模型
工作过程:如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。
java.lang.ClassLoader中几个最重要的方法:
//加载指定名称(包括包名)的二进制类型,供用户调用的接口
public Class<?> loadClass(String name);
//加载指定名称(包括包名)的二进制类型,同时指定是否解析(但是,这里的resolve参数不一定真正能达到解析的效果),供继承用
protected synchronized Class<?> loadClass(String name, boolean resolve);
protected Class<?> findClass(String name)
//定义类型,一般在findClass方法中读取到对应字节码后调用,可以看出不可继承(说明:JVM已经实现了对应的具体功能,解析对应的字节码,产生对应的内部数据结构放置到方法区,所以无需覆写,直接调用就可以了)
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{}
如下是实现双亲委派模型的主要代码:
三、反射
Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的内部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在执行的过程中,动态生成instances、变更fields内容或唤起methods。
1、获取构造方法
Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor getConstructor(Class[] params)
根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors()
返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params)
根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors()
返回该类中所有的构造函数数组(不分public和非public属性)
2、获取类的成员方法
与获取构造方法的方式相同,存在四种获取成员方法的方式。
Method getMethod(String name, Class[] params)
根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods()
返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params)
根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods()
返回该类中的所有的方法数组(不分public和非public属性)
3、获取类的成员变量(成员属性)
存在四种获取成员属性的方法
Field getField(String name)
根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields()
返回具有public属性的成员变量的数组
Field getDeclaredField(String name)
根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredFields()
返回所有成员变量组成的数组(不分public和非public属性)
相关推荐
Java的类加载机制:加载,连接,初始化。JAVA类加载器: Bootstrap ClassLoader : 根类加载器, Extension ClassLoader: 扩展类加载器, System ClassLoader : 系统类加载器, Java反射
反射提供了强大的运行时元数据操作能力,而类加载机制则确保了类的安全加载和动态管理。深入理解这两个机制对于Java开发者来说至关重要,尤其对于那些从事框架开发和系统级编程的开发者而言更是如此。
Java注解、反射、字节码和类加载机制是Java编程中的核心概念,它们在实际开发中扮演着重要角色。让我们深入探讨这些知识点。 **Java注解(Annotation)**: Java注解是一种元数据,它提供了在编译时或运行时处理代码的...
类加载机制不仅涉及到类的加载、验证、准备、解析和初始化等步骤,还包括类加载时机的选择及类加载器的具体工作原理等内容。 #### 一、类加载时机 Java类的生命周期主要包括以下几个阶段:加载、验证、准备、解析...
Java 动态类加载机制研究及应用 Java 动态类加载机制研究及应用是基于 Java 虚拟机(JV M)机制的,旨在实现 Java 应用程序中动态加载类文件,而不影响其他功能模块的正常运行。为了实现这个目标,需要对 Java 类...
本篇文章将深入探讨Android热修复框架的工作原理,特别是基于类加载机制的代码修复。 Android热修复的核心在于替换或修补运行时应用中的问题代码,而实现这一目标的关键是类加载器(ClassLoader)。在Java和Android...
在反射过程中,如果没有加载目标类,那么会触发ClassLoader的加载机制。 总结,理解ClassLoader的加载机制和原理对于优化程序性能、设计可扩展的系统至关重要。通过自定义ClassLoader,开发者可以实现许多高级功能...
JAVA 类加载机制是Java平台核心特性之一,它关乎到程序的运行时环境和代码的动态加载。理解这一机制有助于开发者解决与对象创建、配置问题、应用程序发布等相关的问题。以下是关于JAVA 类加载机制的详细分析: 首先...
类加载机制是Java虚拟机(JVM)运行时的核心机制之一,它负责将类的.class文件从磁盘或网络中加载到内存,并转化为可以执行的Java对象。这一过程包括加载、验证、准备、解析和初始化五个阶段,下面我们来详细探讨...
Java 类加载机制是Java运行时环境的一个核心特性,它...理解类加载机制和反射对于优化性能、实现动态加载、增强代码灵活性等具有重要意义。开发者应谨慎使用反射,因为它可能导致安全风险、性能损失和难以调试的问题。
1.1 类加载机制 类加载是指将.class文件读入内存,并为之创建一个java.lang.Class对象。任何类被使用时,系统都会为之建立一个java.lang.Class对象。类加载机制分为三个步骤:加载、连接和初始化。 * 加载:将....
Java动态类加载机制是Java平台一个非常重要的特性,它允许程序在运行时动态地加载类,增强了软件的灵活性和可扩展性。动态类加载对于理解和优化Java应用程序的性能、实现插件系统、以及处理复杂的模块化系统至关重要...
Java虚拟机(JVM)是Java程序运行的基础,它的类加载机制是理解Java应用程序如何启动和执行的关键部分。本文将深入JDK源码,详细解析JVM类加载机制的各个环节,帮助开发者更好地理解和优化自己的代码。 类加载机制...
### JAVA类加载机制与动态代理 #### 一、类加载机制 ##### 1.1 类加载的时机 类加载机制负责将描述类的数据从`.class`文件加载到内存,并进行必要的校验、转换解析和初始化,使之成为可以被Java虚拟机直接使用的...
Java类加载机制是Java运行时环境中的核心组成部分,它负责将编译后的字节码(.class文件)加载到Java虚拟机(JVM)中,使得程序能够执行。这一过程涉及多个步骤,包括加载、验证、准备、解析和初始化。理解类加载...
在Java开发中,JVM(Java虚拟机)的类加载机制是至关重要的,因为它负责将类的字节码转换为运行时的实例。本专题"性能调优专题-jvm类加载机制-performance-jvmclassloader"深入探讨了如何通过理解并优化类加载过程来...
Java的类加载机制遵循“双亲委派模型”,这意味着当一个类加载器收到加载类的请求时,它会先委托给父类加载器,只有当父类加载器无法找到该类时,子类加载器才会尝试自己去加载。这种设计保证了核心库的稳定性和安全...
### Java反射机制与动态加载实例类 在Java中,反射是一种强大的工具,允许程序在运行时检查和修改其结构和行为。通过反射,我们可以动态地加载类、创建对象、访问和修改字段、调用方法等。本文将深入探讨Java反射...