Java类加载与反射

Java类加载与反射

类加载

转载https://blog.csdn.net/xu768840497/article/details/79175335

类的生命周期

1、加载 loading:
通过“类全名”来获取定义此类的二进制字节流(更有可能是十六进制);
将字节流所代表的静态存储结构转换为方法区的运行时数据结构;
JVM调用类加载器中的defineClass方法在java堆中构造一个代表这个类的java.lang.Class对象,1.8中Class对象包含静态成员变量,作为方法区这些数据的访问入口。

2、验证 verification:确保Class文件的字节流中包含信息符合当前虚拟机要求
文件格式验证:基于字节流验证,验证字节流是否符合Class文件格式的规范,并且能被当前虚拟机处理;
元数据验证:基于方法区的存储结构验证,对字节码描述信息进行语义验证;
字节码验证:基于方法区的存储结构验证,进行数据流和控制流的验证;
符号引用验证:基于方法区的存储结构验证,发生在解析中,是否可以将符号引用成功解析为直接引用。

3、准备 preparation:为static变量分配内存并且设置初始值即零值,不包含用final修饰因在编译时就已分配。类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。

4、解析 resolution:将常量池中的符号引用替换为直接引用,有类或接口的解析,字段解析,类方法解析,接口方法解析。

5、初始化 initialization:执行类构造器方法的过程,将static字段和静态语句赋值。
6、使用 using
7、卸载 unloading

img

举例:
转载https://www.jianshu.com/p/6e039f83a24bclass Lava {

class Lava {
	private int speed = 5; 
	void flow() {}
}

class Volcano {
	public static void main(String[] args) {
		Lava lava = new Lava();
		lava.flow();
    }
}

​ “Volcano”传到JVM时,JVM找到Volcano.class并读入,它从类文件提取了类型信息并放在了方法区中;再解析存在方法区中的字节码,JVM激活了main()方法,在执行时,JVM保持了一个指向当前类(Volcano)常量池的指针;
​ main()的第一条指令告知JVM为常量池第一项的类分配内存,JVM使用指向Volcano常量池的指针找到第一项,是一个对Lava类的符号引用(类lava的完整有效名”lava“),然后检查方法区看lava是否已被加载;
​ 若未加载,开始查找并加载类文件”Lava.class”,同样从类文件中抽取类型信息并放在了方法区中;JVM再以一个直接指向方法区lava类的指针替换了常量池第一项的符号引用,以后就可以用这个指针快速的找到lava类了,而这个替换过程称为常量池解析(constant pool resolution);
​ jvm使用指向volcano常量池第一项的指针,从存储在方法区中的类型信息知道Lava对象需要的空间,堆上为之分配内存,并把这个实例的变量speed初始化为缺省值0。假如lava的父对象也有实例变量,则也会初始化;
​ 当把新生成的lava对象的引用压到栈中,第一条指令也结束了。下面的指令利用这个引用激活java代码把speed变量设为初始值5,另外一条指令会用这个引用激活Lava对象的flow()方法。
创建对象,先检查类是否加载,寻找类对应的class对象,通过使用Class对象执行其RTTI(运行时类型识别,Run-Time Type Identification)创建实例对象。

类加载器

img

Bootstrap:负责将 Java_Home/lib下面的类库加载到内存中(比如rt.jar)。本地代码实现,不向java程序提供引用。
Extension:是由 Sun 的 ExtClassLoader
(sun.misc.Launcher E x t C l a s s L o a d e r ) 实 现 的 。 它 负 责 将 J a v a H o m e / l i b / e x t 或 者 由 系 统 变 量 j a v a . e x t . d i r 指 定 位 置 中 的 类 库 加 载 到 内 存 中 。 开 发 者 可 以 直 接 使 用 标 准 扩 展 类 加 载 器 。 A p p l i c a t i o n : 是 由 S u n 的 A p p C l a s s L o a d e r ( s u n . m i s c . L a u n c h e r ExtClassLoader)实现的。它负责将Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。 Application:是由 Sun 的 AppClassLoader(sun.misc.Launcher ExtClassLoaderJavaHome/lib/extjava.ext.dir使ApplicationSunAppClassLoadersun.misc.LauncherAppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器。开发者可以直接使用系统类加载器。

双亲委派模型过程

​ 某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。保证了方法区中类的唯一性,并只有一个Class对象
​ 实现:在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

反射

​ 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
​ 程序运行时, 所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载),使用反射,实时使用到的类描述参数写到配置文件,动态加载时避免修改源代码并重新编译,增加程序灵活性。在Web上配置的通用框架,保证框架的可扩展性。

​ 例:方法的反射调用Method.invoke,委派给 MethodAccessor来处理。MethodAccessor是一个接口,它有两个具体实现:本地方法、委派模式。每个Method实例的第一次反射调用都会生成一个委派实现,它所委派的具体实现是一个本地实现。本地实现是反射调用将传入的参数准备好,然后通过Method实例所指向方法的具体地址,调用进入目标方法。(猜测Class对象有指向类的原型信息的指针,所以可以使用方法具体地址);Java 的反射调用机制还设立了另一种动态生成字节码的实现,直接使用 invoke 指令来调用目标方法,在调用超过 15 次之后,委派实现便会将委派对象切换至动态实现(Inflation)。

Class类
//代表类的实体,在运行的Java应用程序中表示类和接口
Reflection r = new Reflection();
//通过反射获取类的对象
Class c1 = r.getClass();//需要对象实例
Class c2 = Reflection.class;//需要导入类的包
Class c3 = Class.forName("javaBasics.Reflection");
//参数是类的全路径名。不需要为了获得对象实际类型的Class引用而持有该类的对象。
System.out.println("c1:"+c1.getName()+" c2:"+c2.getName()+" c3:"+c3.getName());

输出:

c1:javaBasics.Reflection c2:javaBasics.Reflection c3:javaBasics.Reflection
Constructor类
//代表类的构造方法
//import java.lang.reflect.Constructor;
//公有
Constructor[] constructor1 = c3.getConstructors();
//所有
Constructor[] constructor2 = c3.getDeclaredConstructors();
//公有无参
Constructor constructor3 = c3.getConstructor(null);//throws NoSuchMethodException
//公有有参
Constructor constructor4 = c3.getConstructor(new Class[]{int.class});//throws NoSuchMethodException
//所有有参
Constructor constructor5 = c3.getDeclaredConstructor(new Class[]{String.class,String.class});//throws NoSuchMethodException

//测试
for(Constructor c : constructor1){
    System.out.println("公有:"+c);
}
for(Constructor c : constructor2){
    System.out.println("所有:"+c);
}
System.out.println("公有无参:"+constructor3);
System.out.println("公有有参:"+constructor4);
System.out.println("所有有参:"+constructor5);
        

输出:

公有:public javaBasics.Reflection(int)
公有:public javaBasics.Reflection()
所有:private javaBasics.Reflection(java.lang.String,java.lang.String)
所有:public javaBasics.Reflection(int)
所有:public javaBasics.Reflection()
公有无参:public javaBasics.Reflection()
公有有参:public javaBasics.Reflection(int)
所有有参:private javaBasics.Reflection(java.lang.String,java.lang.String)
Field类
//代表类的的属性
//import java.lang.reflect.Field;
//import java.lang.reflect.InvocationTargetException;
//公有
Field[] fields1 = c3.getFields();
//所有
Field[] fields2 = c3.getDeclaredFields();
//获取指定公有
Field field3 = c3.getField("i");//throws NoSuchFieldException
//获取指定私有
Field field4 = c3.getDeclaredField("i1");//throws NoSuchFieldException

for(Field f: fields1){
    System.out.println("公有:"+f);}
for(Field f: fields2){
    System.out.println("所有:"+f);}

//使用
Object obj = constructor3.newInstance();//throws IllegalAccessException, InvocationTargetException, InstantiationException
//为属性设置值
field3.set(obj,5);//为私有属性设置指
field4.setAccessible(true);//不然throw IllegalAccessException,我注释掉没报错
field4.set(obj,10);

//测试
Reflection r1 = (Reflection)obj;
System.out.println("i:"+r1.getI()+" i1:"+r1.getI1());

输出:

公有:public int javaBasics.Reflection.i
公有:public java.lang.String javaBasics.Reflection.s
所有:public int javaBasics.Reflection.i
所有:public java.lang.String javaBasics.Reflection.s
所有:private int javaBasics.Reflection.i1
所有:private java.lang.String javaBasics.Reflection.s1
i:5 i1:10
Method类
//代表类的方法	
//import java.lang.reflect.Method;
Method methods1 = c3.getMethod("getI");//无参方法
Method methods2 = c3.getMethod("setI",int.class);//带参方法
methods2.invoke(obj,1);
System.out.println(methods1.invoke(obj,null));

输出:1

测试类:

public class Reflection {
    public int i;
    public String s;
    private int i1;
    private String s1;
    public Reflection(){

    }
    public Reflection(int i){

    }
    private Reflection(String s1, String s2){

    }

    public int getI() {
        return i;
    }

    public int getI1() {
        return i1;
    }

    public void setI(int i) {
        this.i = i;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值