深入理解java:类加载机制及反射

     2022-03-26     734

关键词:

 

 说明:本文乃学习整理参考而来.

一、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属性)

 

 参考:

《深入理解JVM虚拟机》

Java 下高效的反射工具包 ReflectASM 使用例解 

ReflectUitls类的编写和对反射机制的解析

 Cglib源码

java:类加载机制及反射

一、Java类加载机制1.概述       Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这... 查看详情

类加载机制及反射

http://www.cnblogs.com/ITtangtang/p/3978102.html 一、Java类加载机制1.概述       Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造... 查看详情

深入理解java中的反射机制(代码片段)

一.什么是反射在理解反射这个概念之前,我们先来理解Java中的“正射”。在编写代码时,如果需要使用某一个类,必定先会去了解这是一个什么类,用来做什么,有怎么样的功能。之后才会对这个类进行实例... 查看详情

深入理解java虚拟机——类加载机制(代码片段)

文章目录类加载机制类的生命周期类的加载过程1、加载2、验证3、准备4、解析5、初始化类的初始化时机类加载器类与类加载器类加载器分类双亲委派模型工作过程源码分析双亲委派机制的好处类加载机制类的生命周期一个类型... 查看详情

深入理解jvmjvm类加载机制

7.1JVM类加载机制  虚拟机把数据从Class文件加载到内存,并且校验、转换解析和初始化最终形成可以被虚拟机使用的Java类型,这就是虚拟机的类加载机制。7.2类加载的时机  1.类加载的步骤开始的顺序: 加载(Loading) -... 查看详情

虚拟机类加载机制——类加载时机

...于道行不够深,所以此篇类加载机制的讲解主要来自于《深入理解Java虚拟机——JVM高级特性与最佳实践》的第7章虚拟机类加载机制。在前面《初识Java反射》中我们在开头提到要了解Java反射,就得要了解虚拟机的类加载机... 查看详情

深入理解java虚拟机——类加载机制概述

一、类加载机制概述虚拟机把描述类的数据从Class文件加载加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。在Java语言里面,类型的加... 查看详情

深入理解java虚拟机——类加载机制概述

一、类加载机制概述虚拟机把描述类的数据从Class文件加载加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。在Java语言里面,类型的加... 查看详情

深入剖析java并发之阻塞队列linkedblockingqueue与arrayblockingqueue

关联文章:深入理解Java类型信息(Class对象)与反射机制深入理解Java枚举类型(enum)深入理解Java注解类型(@Annotation)深入理解Java类加载器(ClassLoader)深入理解Java并发之synchronized实现原理Java并发编程-无锁CAS与Unsafe类及其并发包Atomic深... 查看详情

深入理解jvm:类加载机制

概述虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。与那些在编译时需要进行链接工作的语言不同,在Java语言里... 查看详情

java基础-反射(代码片段)

...s对象Java的内存分析了解:类的加载过程与ClassLoader的理解什么时候会发生类初始化类加载器的作用双亲委派机制获取运行时类的完整结构有了Clas 查看详情

java基础-反射(代码片段)

...s对象Java的内存分析了解:类的加载过程与ClassLoader的理解什么时候会发生类初始化类加载器的作用双亲委派机制获取运行时类的完整结构有了Clas 查看详情

深入理解java的反射机制

今天将从以下4方面来系统的学习一下java的反射机制:java反射是什么java反射(Reflection)底层实现原理java反射的简单演示java反射的应用场景1,java反射是什么首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你... 查看详情

深入理解java反射

  要想理解反射的原理,首先要了解什么是类型信息。Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发... 查看详情

12000+字java反射,一起全面了解java反射机制,为学习框架铺路(代码片段)

文章目录Java反射机制理解Class类获取Class类实例类的加载过程类加载器ClassLoader创建运行时类的对象获取运行时类的结构调用运行时类的指定结构动态代理Java反射机制Reflection是被视为动态语言的关键,反射机制允许程序在执... 查看详情

深入理解jvm读书笔记三:虚拟机类加载机制

Java虚拟机类加载机制是把Class类文件加载到内存,并对Class文件中的数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程。7.1概述与那些在编译时需要进行链接工作的语言不同,在Java语言里面,... 查看详情

深入理解java虚拟机(类文件结构+类加载机制+字节码执行引擎)

周志明的《深入理解Java虚拟机》很好很强大,阅读起来颇有点费劲,尤其是当你跟随作者的思路一直探究下去,开始会让你弄不清方向,难免有些你说的啥子的感觉。但知识不得不学,于是天天看,反复看,就慢慢的理解了。... 查看详情

深入理解java虚拟机---4虚拟机类加载机制

类加载的整个生命周期:   加载、连接(验证、准备、解析)、初始化、使用、卸载。加载:    class文件中的二进制数据读取到内存中,然后将该字节流所代表的静态数据结构转化为方法区中运行的数据结构,... 查看详情