JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
Class类
阅读API的Class类得知,Class没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass方法自动构造的。
/**
* 获取一个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类型,后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了。
获取公共构造方法,创建对象
获取构造方法,步骤如下:
- 获取到Class对象
- 获取指定的构造方法
- 通过构造方法类Constructor中的方法,创建对象
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
参数值为true则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为false则指示反射的对象应该实施 Java 语言访问检查。
获取私有构造方法,步骤如下:
- 获取到Class对象
- 获取指定的构造方法
- 暴力访问, 通过setAccessible(boolean flag)方法
- 通过构造方法类Constructor中的方法,创建对象public T newInstance(Object… initargs)
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() 获取所有的 变量 (包含私有)
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);
}
}
获取成员方法并使用
获取成员方法,步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的方法
- 执行找到的方法public Object invoke(Object obj, Object… args),执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
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集合中添加一个字符串数据,如何实现呢?
其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。
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对象的创建。
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);
}
}