反射机制
- 将类的各个组成部分封装为其他对象,这就是反射机制
- Java代码在计算机里会经历三个阶段:
- 源代码阶段(Source源代码):
.java
文件通过编译形成.class
文件,字节码文件加载进内存中 - class类对象阶段:通过类加载器(ClassLoader)加载形成类对象(包括有
成员变量 Field[] fields
、构造方法Constructor[] cons
、成员方法Method[] methods
) - Runtime运行时阶段:通过类对象创建对象
- 源代码阶段(Source源代码):
- 反射机制的体现就是将class文件里的成员变量封装为类对象里的Field对象,构造方法封装为Constructor对象,将成员方法封装为Method对象
- 反射的好处:
- 1、可以在程序的运行过程中,操纵对象
- 2、可以解耦,提高代码的可扩展性
Class对象
-
获取class对象的三种形式:
- 1、class.forName(“全类名”):将字节码文件加载进内存,返回class对象(多用以配置文件,将类定义在配置文件中,读取文件,加载类)
- 2、类名.class:通过类名的属性class获取(多用于参数的传递)
- 3、对象.class(多用于对象获取字节码的方式)
同一个字节码文件(*.class)在一次程序运行中,只会被加载一次,无论通过哪一种方式加载获取的Class对象都是同一个
1、获取成员变量
* Field[] getFields();//获取所有public修饰的成员变量,包括父类
* Field getField(String name);//也只能获取所有public修饰的成员变量
* Field[] getDeclaredFields();//获取所有的成员变量,不考虑访问权限修饰符,不包括父类
* Field getDeclaredField(String name);
2、获取构造方法
* Constructor<?>[] getConstructors();//获取所有的public修饰的构造器
* Constructor<T> getConstructor(类<?>...parameterTypes);
* Constructor<?>[] getDeclaredConstructors();
* Constructor<T> getDeclaredConstructor(类<?>...parameterTypes);
3、获取成员方法
* Method[] getMethods();//获取所有的方法,包括Object类的方法
* Method getMethod(String name,类<?>... parameterTypes);
* Method[] getDeclaredMethods();
* Method getDeclaredMethod(String name,类<?>... parameterTypes);
4、获取类名
* String getName();
-
Field成员变量的操作:
-
1、设置值:
void set(Object obj, Object value)
-
2、获取值:
Object get(Object obj)
-
3、忽略访问修饰权限符的安全检查:
setAccessible(true)
-
Field getDeclaredField(String name):获取之后,对于private修饰符的成员变量,进行set,get操作时,将会报错:
Exception in thread "main" java.lang.IllegalAccessException: Class Reflect.ReflectDemo can not access a member of class Reflect.Person with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288) at java.lang.reflect.Field.get(Field.java:390)
-
因此,要想在类外操作private修饰的变量,将要忽略访问修饰权限符的安全检查,也就是暴力反射:
setAccessible(true)
-
-
Contructor构造方法的操作
- 构造器的作用就是用来创建对象的。
- 创建对象:
T newInstance(Object... initargs)
- 如果是用空参的构造方法来创建一个对象,可以直接使用Class类的
T newInstance()
来创建
- 如果是用空参的构造方法来创建一个对象,可以直接使用Class类的
- 要获取私有的构造器的情况,也是用
setAccessible(true)
来暴力反射
-
Method构造方法的操作
- 获取所有的方法,会获取除自定义的方法外,还会获取Object类的所有方法
- 执行方法:
Object invoke(Object obj, Object... args)
- 获取方法名称:
String getName()
案例
- 需求:写一个“框架”,在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
- 实现:
- 1、配置文件
- 2、反射
- 步骤:
- 1、将需要创建的对象的全类名和需要执行的方法定义到配置文件中
- 2、在程序中加载读取配置文件
- 3、使用反射技术来加载类文件进内存
- 4、创建对象
- 5、执行方法
//pro.properties
className=domain.Person
methodName=eat
public class ReflectTest {
public static void main(String[] args) throws Exception {
//可以创建任意类的对象,可以执行任意方法
//1 创建Properties对象
Properties pro = new Properties();
//2 加载配置文件,转换成一个集合
//获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream stream = classLoader.getResourceAsStream("pro.properties");
pro.load(stream);
//3 获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//4 加载该类进内存
Class cls = Class.forName(className);
//5 创建对象
Object o = cls.newInstance();
//6 获取方法对象
Method method = cls.getMethod(methodName);
//7 执行方法
method.invoke(o);
}
}