目录
反射
概述介绍
概述:
-
将类的各个组成部分封装为其他对象,这就是反射机制
好处:
-
可以在程序运行过程中,操作这些对象。
-
可以解耦,提高程序的可扩展性。
获取Class对象三种方式
-
Class.forName("全类名") 将字节码文件加载进内存,返回class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
-
类名.class :通过类名的属性class获取
多用于参数的传递
-
对象.getclass() : getclass()方法在object类中定义着。
多用于对象的获取字节码的方式
注意:同一个类中,不论通过哪一种方式获取的Class对象都是同一个
Class对象的功能
获取成员变量
-
Field getField(string name) 获取public且指定的成员变量
-
Field[] getFields() 获取所以public修饰的所以成员变量
-
Field getDedclaredField(string name) 获取指定的成员变量
-
Field[] getDeclaredFields() 获取所以的成员变量,不考虑修饰符
注意:得到了以上的field对象,都可以通过 field对象.get(访问的那个对象) ,获取具体的成员变量值
但是,private修饰的不能直接field.get(obj)获取,需要 field.setAccessible(true);//叫暴力反射
package com.xxx.index;
import java.lang.reflect.Field;
public class Text {
public static void main(String[] args) throws Exception{
// 获取Class类 和 Demo01对象
Class cla=Demo.class;
Demo demo01=new Demo();
// 获取public修饰的
System.out.println(cla.getField("name").get(demo01));//mxp
// 获取private修饰的
Field field=cla.getDeclaredField("age");
System.out.println(field);//private int com.xxx.index.Demo01.age
// System.out.println(field.get(demo01));//java.lang.IllegalAccessException(非法访问) 因为是private修饰的,
// 忽略访问权限修饰符的安全检查
field.setAccessible(true);//叫暴力反射
System.out.println(field.get(demo01));
}
}
class Demo {
public String name="mxp";
private int age=18;
}
看看输出结果:
获取构造方法
-
Constructor<?> getConstructor(Class<?>... parameterTypes) 返回一个public修饰的且指定的构造器
-
Constructor<T>[] getConstructors() 返回全部public修饰的构造器
-
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 获取指定的构造器
-
Constructor<?>[] getDeclaredConstructors() 获取所有的构造器
直接上代码吧
package com.xxx.index; import java.lang.reflect.Constructor; public class Text { public static void main(String[] args) throws Exception { Class cla1=Demo.class; // 获取有参构造器 Constructor<Object> con01=cla1.getConstructor(String.class,int.class); System.out.println(con01);//public com.xxx.index.Demo(java.lang.String,int) // 创建对象 newInstance System.out.println(con01.newInstance("mxp",18)); // 为了方便 Class还提供了一个可以直接创建无参对象的方法 System.out.println(cla1.newInstance()); } } class Demo { public Demo() { System.out.println("执行了无参构造"); } public Demo(String name,int age) { System.out.println("执行了有参构造"); } }
看看输出结果:
获取成员方法
-
Method getMethod(String name,类<?>... parameterTypes) 获取public修饰的指定方法
第一个参数:String是获取的那个方法名
第二个参数:这个方法参数的Class对象。
-
其他的三个方法跟上面都差不多,我就不列出来了
-
method.invoke(Object obj,Object... args); 执行获取的method对象
第一个参数:目标对象
第二个参数:传给实现方法的参数传值
package com.xxx.index;
import java.lang.reflect.Method;
public class Text {
public static void main(String[] args) throws Exception {
Class class1=Demo.class;
Demo demo=new Demo();
//获取方法
Method method=class1.getMethod("eat", String.class,int.class);
System.out.println(method);//public void com.xxx.index.Demo.eat(java.lang.String,int)
//执行方法
method.invoke(demo, "佩奇",7);//我叫佩奇一顿吃7碗
}
}
class Demo {
public void eat(String name, int a) {
System.out.println("我叫" + name + "一顿吃" + a + "碗");
}
}
看下输出结果:
案例
看了这么多是不是感觉不出,学这个有啥用?直接上代码
我们的需求就是在不修改任何代码的情况下,调用不同的方法
package anli;
public class Person01 {
public void eat() {
System.out.println("eat...");
}
}
class Person02{
public void sleep() {
System.out.println("sleep...");
}
}
package anli;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception{
// 1.创建properties 用于读取Java的配置文件
Properties pro=new Properties();
ClassLoader classLoader=Test.class.getClassLoader();//获取这个字节码文件的内加载器
InputStream is=classLoader.getResourceAsStream("pro.properties");//获取资源对应的字节流
pro.load(is);//这里需要传一个字节或字符 , 从输入流中读取属性列表(键和元素对), 读取properties的配置文件
// 获取配置文件的数据
String className=pro.getProperty("className");
String methodName=pro.getProperty("methodName");
// 加载该类进内存
Class cls=Class.forName(className);
Object obj=cls.newInstance();
Method method=cls.getMethod(methodName);
method.invoke(obj);//sleep...
}
}
pro.properties
className=anli.Person02
methodName=sleep
看下输出结果:
现在我们就不需要改源代码了,修改配置文件就行了
className是全类名
methodName是方法名
className=anli.Person01
methodName=eat
看下输出结果:
现在明白了哈,瑞思拜