Java的反射机制之类加载详解
作者:_雨_
类加载
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
动态加载和静态加载
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
2.动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性
动态加载和静态加载的区别
静态加载是指在编译时期确定要加载的类的类型,即通过 class 关键字和类名来获取对应类的类型,
动态加载则是在运行时期根据需要动态获取需要加载的类的类型,例如使用 Class.forName() 方法
类加载时机
1.当创建对象时 (new) //静态加载
2.当子类被加载时,父类也加载 //静态加载
3调用类中的静态成员时 //静态加载
4.通过反射 //动态加载
类加载的五个阶段
类加载器是负责把类加载到Java虚拟机中的系统模块,Java中的类加载过程可以分为三个阶段:
那么为什么我们有说是三个阶段呢,因为在第二个阶段中链接阶段,又分为了三个部分,分别是验证准备和解析
1.加载阶段:通过类的全限定名获取其二进制字节流,将字节流转换成方法区内部数据结构,在内存中生成一个代表类的 Class 对象并返回。
2.链接阶段:包括验证、准备和解析的过程。
- 验证:检查类的二进制字节流是否符合Java虚拟机规范,并保证不会危害虚拟机自身的安全。
- 准备:为类的静态变量分配内存,并设置默认初始值。
代码演示:
package com.reflection.classload_; /** * 我们说明一个类加载的链接阶段-准备 */ public class ClassLoad02 { public static void main(String[] args) { } } class A { //属性-成员变量-字段 //分析类加载的链接阶段-准备 属性是如何处理 //1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存 //2. n2 是静态变量,分配内存 n2 是默认初始化 0 ,而不是20 //3. n3 是static final 是常量, 他和静态变量不一样, 因为一旦赋值就不变 n3 = 30 public int n1 = 10; public static int n2 = 20; public static final int n3 = 30; }
- 解析:将类中的符号引用转换成直接引用,这个过程包括方法和字段的解析。
3.初始化阶段:
到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行<clinit>() 方法的过程。
<clinit>() 方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
虚拟机会保证一个类的 <clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行 <clinit>() 方法完毕 [debug源码]
代码演示:
package com.reflection.classload_; /** * 演示类加载-初始化阶段 */ public class ClassLoad03 { public static void main(String[] args) throws ClassNotFoundException { //分析 //1. 加载B类,并生成 B的class对象 //2. 链接 num = 0 //3. 初始化阶段 // 依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并 /* clinit() { System.out.println("B 静态代码块被执行"); //num = 300; num = 100; } 合并: num = 100 */ //new B();//类加载 如果是使用的是new B()一个对象,那么还会导致构造器被执行,如果使用的是类的静态属性,那么也会导致类的加载,但是不会导致构造器被执行 //System.out.println(B.num);//100, 如果直接使用类的静态属性,也会导致类的加载 //看看加载类的时候,是有同步机制控制 /* protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //正因为有这个机制,才能保证某个类在内存中, 只有一份Class对象 synchronized (getClassLoadingLock(name)) { //.... } } */ B b = new B(); } } class B { static { System.out.println("B 静态代码块被执行"); num = 300; } static int num = 100; public B() {//构造器 System.out.println("B() 构造器被执行"); } }
获取类的结构信息
Java反射中可以使用以下方法来获取类的结构信息:
- getDeclaredFields() :获取类的所有属性(Field),包括私有属性。
- getFields() :获取类的所有公有属性。
- getDeclaredMethods() :获取类的所有方法(Method),包括私有方法。
- getMethods() :获取类的所有公有方法。
- getDeclaredConstructors() :获取类的所有构造器(Constructor),包括私有构造器。
- getConstructors() :获取类的所有公有构造器。
- getDeclaredClasses() :获取类的所有内部类。
- getClasses() :获取类的所有公有内部类。
- getDeclaredAnnotations() :获取类的所有注解。
- getSuperclass() :获取类的父类。
- getInterfaces() :获取类实现的接口。
除了上述方法之外,还有一些其他方法可以用于获取类的结构信息,如 getDeclaredField() 、 getMethod() 、 getConstructor() 等,这些方法可以用于获取指定名称的属性、方法或者构造器的信息。
代码演示;
package com.reflection; import org.testng.annotations.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 演示如何通过反射获取类的结构信息 */ public class ReflectionUtils { public static void main(String[] args) { } @Test public void api_02() throws ClassNotFoundException, NoSuchMethodException { //得到Class对象 Class<?> personCls = Class.forName("com.reflection.Person"); //getDeclaredFields:获取本类中所有属性 //规定 说明: 默认修饰符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本类中所有属性=" + declaredField.getName() + " 该属性的修饰符值=" + declaredField.getModifiers() + " 该属性的类型=" + declaredField.getType()); } //getDeclaredMethods:获取本类中所有方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本类中所有方法=" + declaredMethod.getName() + " 该方法的访问修饰符值=" + declaredMethod.getModifiers() + " 该方法返回类型" + declaredMethod.getReturnType()); //输出当前这个方法的形参数组情况 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("该方法的形参类型=" + parameterType); } } //getDeclaredConstructors:获取本类中所有构造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("===================="); System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名 Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("该构造器的形参类型=" + parameterType); } } } //第一组方法API @Test public void api_01() throws ClassNotFoundException, NoSuchMethodException { //得到Class对象 Class<?> personCls = Class.forName("com.reflection.Person"); //getName:获取全类名 System.out.println(personCls.getName());//com.reflection.Person //getSimpleName:获取简单类名 System.out.println(personCls.getSimpleName());//Person //getFields:获取所有public修饰的属性,包含本类以及父类的 Field[] fields = personCls.getFields(); for (Field field : fields) {//增强for System.out.println("本类以及父类的属性=" + field.getName()); } //getDeclaredFields:获取本类中所有属性 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("本类中所有属性=" + declaredField.getName()); } //getMethods:获取所有public修饰的方法,包含本类以及父类的 Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println("本类以及父类的方法=" + method.getName()); } //getDeclaredMethods:获取本类中所有方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("本类中所有方法=" + declaredMethod.getName()); } //getConstructors: 获取所有public修饰的构造器,包含本类 Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("本类的构造器=" + constructor.getName()); } //getDeclaredConstructors:获取本类中所有构造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名 } //getPackage:以Package形式返回 包信息 System.out.println(personCls.getPackage());//com.reflection //getSuperClass:以Class形式返回父类信息 Class<?> superclass = personCls.getSuperclass(); System.out.println("父类的class对象=" + superclass);// //getInterfaces:以Class[]形式返回接口信息 Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println("接口信息=" + anInterface); } //getAnnotations:以Annotation[] 形式返回注解信息 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("注解信息=" + annotation);//注解 } } } class A { public String hobby; public void hi() { } public A() { } public A(String name) { } } interface IA { } interface IB { } @Deprecated class Person extends A implements IA, IB { //属性 public String name; protected static int age; // 4 + 8 = 12 String job; private double sal; //构造器 public Person() { } public Person(String name) { } //私有的 private Person(String name, int age) { } //方法 public void m1(String name, int age, double sal) { } protected String m2() { return null; } void m3() { } private void m4() { } }
通过反射创建对象
1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器
3.Class类相关方法
- newlnstance : 调用类中的无参构造器,获取对应类的对象
- getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
- getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象
4.Constructor类相关方法
- setAccessible:暴破
- newlnstance(Object...obj):调用构造器
应用案例1
代码演示:
package com.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 演示通过反射机制创建实例 */ public class ReflecCreateInstance { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //1. 先获取到User类的Class对象 Class<?> userClass = Class.forName("com.reflection.User"); //2. 通过public的无参构造器创建实例 Object o = userClass.newInstance(); System.out.println(o); //3. 通过public的有参构造器创建实例 /* constructor 对象就是 public User(String name) {//public的有参构造器 this.name = name; } */ //3.1 先得到对应构造器 Constructor<?> constructor = userClass.getConstructor(String.class); //3.2 创建实例,并传入实参 Object hsp = constructor.newInstance("hsp"); System.out.println("hsp=" + hsp); //4. 通过非public的有参构造器创建实例 //4.1 得到private的构造器对象 Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class); //4.2 创建实例 //暴破【暴力破解】 , 使用反射可以访问private构造器/方法/属性, 反射面前,都是纸老虎 constructor1.setAccessible(true); Object user2 = constructor1.newInstance(100, "张三丰"); System.out.println("user2=" + user2); } } class User { //User类 private int age = 10; private String name = "明天吃火锅"; public User() {//无参 public } public User(String name) {//public的有参构造器 this.name = name; } private User(int age, String name) {//private 有参构造器 this.age = age; this.name = name; } public String toString() { return "User [age=" + age + ", name=" + name + "]"; } }
访问属性
1.根据属性名获取Field对象
Field f = clazz对象.getDeclaredField(属性名);
2.暴破 : fsetAccessible(true); //f 是Field
3.访问 f.set(o,值); // o 表示对象
syso(f.get(o));//o 表示对象
4.注意: 如果是静态属性,则set和get中的参数o,可以写成null
代码演示:
注意访问私有的属性需要进行爆破设置这个属性 name.setAccessible(true);
package com.reflection; import java.lang.reflect.Field; /** * 演示反射操作属性 */ public class ReflecAccessProperty { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { //1. 得到Student类对应的 Class对象 Class<?> stuClass = Class.forName("com.reflection.Student"); //2. 创建对象 Object o = stuClass.newInstance();//o 的运行类型就是Student System.out.println(o.getClass());//Student //3. 使用反射得到age 属性对象 Field age = stuClass.getField("age"); age.set(o, 88);//通过反射来操作属性 System.out.println(o);// System.out.println(age.get(o));//返回age属性的值 //4. 使用反射操作name 属性 Field name = stuClass.getDeclaredField("name"); //对name 进行暴破, 可以操作private 属性 name.setAccessible(true); //name.set(o, "Jack"); name.set(null, "jack~");//因为name是static属性,因此 o 也可以写出null System.out.println(o); System.out.println(name.get(o)); //获取属性值 System.out.println(name.get(null));//获取属性值, 要求name是static } } class Student {//类 public int age; private static String name; public Student() {//构造器 } public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } }
访问方法
1.根据方法名和参数列表获取Method方法对象:
- Method m = clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法
2.获取对象 : Object o=clazz.newlnstance():
3暴破: m.setAccessible(true);
4访问 : Object returnValue = m.invoke(o,实参列表)://o 就是对象
5注意:如果是静态方法,则invoke的参数o,可以写成null!
本章习题
第一题
代码演示:
要求:
1.定义PrivateTest类,有私有name属性,并且属性值为hellokitty 2.提供getName的公有方法 3创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值并调用getName()的方法打印name属性值
package com.homework; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* 1.定义PrivateTest类,有私有name属性,并且属性值为hellokitty 2.提供getName的公有方法 3创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值并调用getName()的方法打印name属性值 */ public class Homework01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { //得到class类对象 Class<?> aClass = Class.forName("com.homework.PrivateTest"); //创建对象实例 Object o = aClass.newInstance(); //得到所有的字段 Field name = aClass.getDeclaredField("name"); //因为name是private私有的 因此需要进行爆破 name.setAccessible(true); //修改name的值 name.set(o, "你好"); //得到所有方法,因为这里方法是public的因此不需要使用爆破 Method getName = aClass.getMethod("getName"); Object invoke = getName.invoke(o); System.out.println("name的属性值是=" + invoke); } } class PrivateTest { private String name = "helloKitty"; public String getName() { return name; } }
第二题
代码演示:
要求:
利用Class类的forName方法得到File类的class 对象 在控制台打印File类的所有构造器 通过newlnstance的方法创建File对象,并创建E:\ mynew.txt文件 提示:创建文件的正常写法如下:File file = new File("d: aa.txt");file.createNewFile();
package com.homework; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* 利用Class类的forName方法得到File类的class 对象 在控制台打印File类的所有构造器 通过newlnstance的方法创建File对象,并创建E:\ mynew.txt文件 提示:创建文件的正常写法如下:File file = new File("d: aa.txt");file.createNewFile(); */ public class Homework02 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //得到File类 的class对象 Class<?> aClass = Class.forName("java.io.File"); //得到所有的构造器,并输出 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } //得到一个指定的构造器 Constructor<?> constructor = aClass.getConstructor(String.class); //指定文件创建的路径 String filePath = "d:\\mynew.txt"; //创建文件对象 /* 此时o相当于执行了这么一个步骤 File file = new File(filePath); 此时o就是File对象的实例 */ Object o = constructor.newInstance(filePath); //得到createNewFile方法的对象 /* 通过反射,得到file类中的一个方法createNewFIle()方法的对象 */ Method createNewFile = aClass.getMethod("createNewFile"); //最后使用反射调用方法,相当于执行了file.createNewFIle(); //只不过一个是使用反射的方式创建,一个是使用new 出来的File对象的实例进行创建,在反射中,万物皆对象,因此可以使用这种方法创建 createNewFile.invoke(o); //此时o的运行类型是Java.io.File 因为在反射中所有的编译类型都是Object System.out.println(o.getClass()); System.out.println("文件创建成功"); } }
到此这篇关于Java的反射机制之类加载详解的文章就介绍到这了,更多相关Java的反射机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!