Loading... JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 ![反射概念](https://resource.if010.com/java_classloader_study_01.jpg) 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象. # Class类 阅读API的Class类得知,Class没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass方法自动构造的。 ```java /** * 获取一个class类文件对象的三种方式 * 1、对象获取 * 2、类名获取 * 3、Class类的静态方法获取 */ public class ReflectDemo { public static void main(String[] args) throws Exception { //1、对象获取,通过父类方法getClass获取 Person p = new Person(); Class pC01 = p.getClass(); //2、类名获取 //每个类型,包括基本和引用,都会赋予这个类型一个静态属性class Class pC02 = Person.class; //3、Class类的静态方法获取 Class pC03 = Class.forName("com.if010.classloader.Person"); //疑问:pC01 == pC02 == pC03 ? System.out.println(pC01 == pC02); //True System.out.println(pC01 == pC03); //True System.out.println(pC02 == pC03); //True } } ``` 第三种和前两种的区别,前两种你必须明确Person类型,后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了。 ## 获取公共构造方法,创建对象 获取构造方法,步骤如下: 1. 获取到Class对象 2. 获取指定的构造方法 3. 通过构造方法类Constructor中的方法,创建对象 ```java import java.lang.reflect.Constructor; /** * 通过反射获取Class文件中的构造方法,并运行构造方法 * 运行构造方法创建对象 * 1. 获取到Class对象 * 2. 获取指定的构造方法 * 3. 通过构造方法类Constructor中的方法,创建对象 */ public class ReflectDemo { public static void main(String[] args) throws Exception { Class pClass = Class.forName("com.if010.classloader.Person"); //使用class文件对象,获取类中的构造方法,有两种方式拿取 //1、getConstructors() 获取class文件中的所有的公共的构造方法 Constructor[] constructors = pClass.getConstructors(); for (Constructor c : constructors){ System.out.println(c); } //2、getConstructor() 获取class文件中的一个空参构造方法 Constructor constructor = pClass.getConstructor(); //运行空参构造器的方法,Constructor类newInstance方法 Object obj = constructor.newInstance(); //getConstructor(Class<?>...parameterTypes) 获取class文件中的一个有参构造方法 Constructor constructor1 = pClass.getConstructor(String.class,int.class); //运行有参构造器的方法,Constructor类newInstance方法 Object obj1 = constructor1.newInstance("zhangsan",19); //快捷方法(条件:被反射的类有空参构造方法,且该方法是public) Object obj2 = Class.forName("com.if010.classloader.Person").newInstance(); System.out.println(obj2); } } ``` ## 获取私有构造方法,创建对象 AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下: * public void setAccessible(boolean flag) throws [SecurityException](mk:@MSITStore:D:\传智基础班\JDK_API_1_6_zh_CN.CHM::/java/lang/SecurityException.html "java.lang 中的类") 参数值为true则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为false则指示反射的对象应该实施 Java 语言访问检查。 获取私有构造方法,步骤如下: 1. 获取到Class对象 2. 获取指定的构造方法 3. 暴力访问, 通过setAccessible(boolean flag)方法 4. 通过构造方法类Constructor中的方法,创建对象public T newInstance(Object... initargs) ```java import java.lang.reflect.Constructor; /** * 反射获取私有的构造方法运行 * 不推荐,破坏了程序的封装性、安全性 */ public class ReflectDemo { public static void main(String[] args) throws Exception { Class pClass = Class.forName("com.if010.classloader.Person"); //getDeclaredConstructors()获取所有的构造方法(包括私有的) Constructor[] constructors = pClass.getDeclaredConstructors(); for (Constructor c : constructors){ System.out.println(c); } //getDeclaredConstructor(Class...c)获取指定参数列表的构造方法 Constructor constructor = pClass.getDeclaredConstructor(int.class,String.class); //Constructor类的父类AccessibleObject类setAccessible(boolean flag)方法可以取消访问权限,简称暴力反射 //如果不取消会报错"IllegalAccessException" constructor.setAccessible(true); Object obj = constructor.newInstance(19,"zhangsan"); System.out.println(obj); } } ``` ## 获取成员变量并使用 在反射机制中,把类中的成员变量使用类Field表示,可通过Class类中提供的方法获取成员变量: * 返回一个成员变量 * public Field getField(String name) 获取指定的 public修饰的变量 * public Field getDeclaredField(String name) 获取指定的任意变量 * 返回多个成员变量 * public Field[] getFields() 获取所有public 修饰的变量 * public Field[] getDeclaredFields() 获取所有的 变量 (包含私有) ```java import java.lang.reflect.Field; /** * 反射获取成员变量,并修改值 * Person类中的成员变量,String name */ public class ReflectDemo { public static void main(String[] args) throws Exception { Class pClass = Class.forName("com.if010.classloader.Person"); //获取所有的公共成员变量,Class类中的方法getFields() Field[] fieldsPub = pClass.getFields(); for (Field field : fieldsPub){ System.out.println(field); } //获取所有的成员变量(包括私有的),Class类中的方法getFields() Field[] fieldsAll = pClass.getDeclaredFields(); for (Field field : fieldsAll){ System.out.println(field); } //获取指定的公共成员变量 //Class类的方法 getField(String name) 传递字符串类型的变量名 Field fieldPub = pClass.getField("name"); //修改成员变量的值,void get(Object obj, Object value) //Object obj 必须有对象的支持, Object value修改后的值 Object o = pClass.newInstance(); fieldPub.set(o, "zhangsan"); //获取指定的私有成员变量(不推荐,会破坏封装性和安全性) //Class类的方法 getDeclaredField(String name) 传递字符串类型的变量名 Field fieldAll = pClass.getDeclaredField("age"); //暴力反射,取消访问权限检查,不取消会报错"IllegalAccessException" fieldAll.setAccessible(true); fieldAll.set(o, 19); System.out.println(o); } } ``` ## 获取成员方法并使用 获取成员方法,步骤如下: 1. 获取Class对象 2. 获取构造方法 3. 通过构造方法,创建对象 4. 获取指定的方法 5. 执行找到的方法public Object invoke(Object obj, Object... args),执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。 ```java import java.lang.reflect.Method; /** * 反射获取成员方法并运行 * */ public class ReflectDemo { public static void main(String[] args) throws Exception { Class pClass = Class.forName("com.if010.classloader.Person"); //获取class文件中的公共成员方法(包括继承的) Method[] methods = pClass.getMethods(); for (Method m : methods){ System.out.println(m); } //获取class文件中指定的空参公共成员方法 //Method getMethod(String methodName, Class...c) //String methodName 方法名,Class...c 参数列表 Method method = pClass.getMethod("eat"); //使用Method类方法Object invoke(Object obj, Object...o) 运行class文件中的方法 method.invoke(pClass.newInstance()); //获取class文件中指定的有参公共成员方法 Method method1 = pClass.getMethod("speak", String.class); Object speak = method1.invoke(pClass.newInstance(), "Hello呀!"); System.out.println(speak); //获取class文件中指定的空参私有成员方法(不推荐,会破坏封装性和安全性) Method method2 = pClass.getDeclaredMethod("work"); //暴力反射,取消访问权限检查,不取消会报错"IllegalAccessException" method2.setAccessible(true); method2.invoke(pClass.newInstance()); } } ``` ## 泛型擦除 > 将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢? > > 其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。 ```java import java.lang.reflect.Method; import java.util.ArrayList; /** * 定义一个集合类,固定类型String * 要求向集合中加入int类型 * * 反射方式,获取出集合ArrayList类的class文件对象 * 通过class文件对象,调用add方法 */ public class ReflectTest { public static void main(String[] args) throws Exception { ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add("hello"); arrayList.add("world"); //获取出集合ArrayList类的class文件对象 Class c = arrayList.getClass(); //通过ArrayList类的class文件对象获取add方法 Method method = c.getMethod("add", Object.class); //使用invoke运行add方法 method.invoke(arrayList, 100); } } ``` ## 反射配置文件 通过反射配置文件,运行配置文件中指定类的对应方法,读取Peoperties.txt文件中的数据,通过反射技术,来完成Person对象的创建。 ```java import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; /** * 读取class.properties文件中的数据,通过反射技术,来完成Person对象的创建 * * class.properties文件内容: * ClassName=com.if010.classloader.Person * MethodName=eat */ public class ReflectTest { public static void main(String[] args) throws Exception { //通过IO流读取配置文件 FileReader fileReader = new FileReader("./Test/class.properties"); Properties classPropertie = new Properties(); classPropertie.load(fileReader); fileReader.close(); String className = classPropertie.getProperty("ClassName"); String methodName = classPropertie.getProperty("MethodName"); //开始反射 //1、获取Person.class 字节码文件对象 Class c = Class.forName(className); //2、获取构造方法 Method method = c.getMethod(methodName); //3、创建对象 Object object = c.newInstance(); //4、运行方法 method.invoke(object); } } ``` 最后修改:2023 年 08 月 10 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 -