持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
反射
框架设计的灵魂
框架:半成品软件,可以在框架的基础上进行软件开发,简化代码
反射:将类的各个部分封装为其他对象,这就是反射机制
Java代码在计算机中经理的三个阶段
-
阶段1:source源代码阶段
- .java文件经过javac编译转化为.class文件
- 还在硬盘中,未进入内存
-
阶段2:Class类对象阶段
- 通过类加载器加载进内存
- 将其他类的各个组成部分封装为Class类对象
-
阶段3:运行时阶段
好处
- 可以在程序运行过程中,操作这些对象
- 可以解耦,提高程序的可扩展性
获取Class类对象的方式
- 阶段1获取:Class.forName("全类名");将字节码文佳加载进内存,返回Class对象
- 多用于配置文件,将类名定义在配置文件中,如读取文件,加载类
- 用于类本身内部是使用反射
public class Apple { private int price; public void setPrice(int price) { this.price = price; } public static void main(String[] args) throws Exception{ //正常的调用 Apple apple = new Apple(); apple.setPrice(5); //使用反射调用 Class clz = Class.forName("com.chenshuyi.api.Apple"); Method setPriceMethod = clz.getMethod("setPrice", int.class); Constructor appleConstructor = clz.getConstructor(); Object appleObj = appleConstructor.newInstance(); setPriceMethod.invoke(appleObj, 14); } }
- 阶段2获取:类名.class; 通过类名的属性class获取
- 多用于参数的传递
- Class testClass = test.class; test为类名
- 阶段3获取:对象.getClass();
- 多用于对象的获取字节码的方式
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过那种方式获取的Class对象都是同一个
Class对象的相关方法
获取功能
- 获取成员变量们
- Filed[] getFileds() -- 获取所有public修饰的成员变量
- Filed getFiled(String name) -- 获取指定名称的public修饰的成员变量
- Filed[] getDeclaredFileds() -- 获取所有的成员变量,不可考虑修饰符
- Filed getDeclaredFiled(String name) -- 获取指定名称的成员变量
- 获取构造方法
- 同上类似
- 获取成员方法
- 同上类似
- 获取类名 String getNmae()
Filed成员变量对象
操作:
- 设置值
- void set(Object obj,Objevt value)
- 获取值
- get(Object obj)
- 忽略访问权限修饰符的安全检查
- setAccessible(true);暴力反射
Constructor构造方法对象
创建对象:
- getConstructor()方法获取空参构造函数,如果传入参数,则返回对应参数的构造器
- newInstance(Object... 参数)方法构造对象
- 如果构造空参对象,则直接使用Class对象的newInstance()方法即可
Method成员方法对象
调用类的成员方法:
- 通过method.invoke(obj,args1,...,argsX)方法
- obj为拥有method方法的类
- agrs为method方法需要传入的参数
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws Exception{
//正常的调用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用
Class clz = Class.forName("com.chenshuyi.api.Apple");
Method setPriceMethod = clz.getMethod("setPrice", int.class);
Constructor appleConstructor = clz.getConstructor();
Object appleObj = appleConstructor.newInstance();
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}
获取反射中的对象(实例化) & 调用类的成员方法
利用反射实例化类对象
步骤:
- 加载Class类对象,三种不同时期加载方式,视情况而定
- 通过反射创建类对象
- 方法一:通过Class对象的newInstance()方法
Class clz = Apple.class; Apple apple = (Apple)clz.newInstance();- 方法二:通过Constructor对象的newInstance()方法
Class clz = Apple.class; Constructor constructor = clz.getConstructor(); Apple apple = (Apple)constructor.newInstance();
通过方法一方式创建类对象,只能使用该类的无参构造方法,有参构造方法不能调用;方法二方式,则可以选择特定构造方法,只需要根据传入的对象进行选择
利用反射调用类的成员方法
步骤:
- 加载Class类对象 -- clz
- 获取方法的Method对象
Method setPriceMethod = clz.getMethod("setPrice", int.class); - 创建类对象 -- obj
- 调用invoke(obk,args1,...)方法
setPriceMethod.invoke(0bj, 14);
配置文件加载案例
public static void main(String[] args)
throws Exception {
// 加载配置文件
// 1. 创建properties对象
Properties prop = new Properties();
// 2. 加载配置文件
ClassLoader classLoader = reflect.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream(
"prop.properties"
);
prop.load(is);
// 获取配置文件中的数据
String className = prop.getProperty("className");
String methodNmae = prop.getProperty("methodName");
// 加载该类进内存
Class cls = Class.forName(className);
// 创建对象
Object obj = cls.newInstance();
// 获取方法对象
Method method = cls.getMethod(methodNmae);
// 执行方法
method.invoke(obj);
}