1. 反射
1.1 java代码的运行
在学习反射之前,我们首先要理解我们编写的 **java代码是如何编译运行 **的;它经历了三个阶段:
- 源代码阶段:
- 首先假设我们写好了A.java文件
- 然后,我们编译A.java,编程了A.class文件(可执行文件)(字节码文件)
- 加载阶段
- 然后我们要运行A.class文件,首先要加载到内存中(原来是在硬盘上存的来)
- 运行阶段
- 加载到内存中,就可以执行啦
- 加载到内存中,就可以执行啦
1.2 反射基本概念
好,了解了java代码要运行时要经历的三个阶段之后,我们正式开始看反射:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
hh,是不是看得云里雾里的,来让我给大家仔细剖析一下:
反射就是把java类中的各种成分映射成一个个的Java对象
其实反射,就是通过** 运行中 **的.class文件中的内容(成员变量,方法)映射成一个个Java对象,然后通过.class文件,来反向操作这些对象
2. 快速入门
环境准备:
- com.hspedu.Cat实体类,类中有一个hi方法(打印“你好,我是–”)
- 创建.properties文件todo:加链接,里面有俩个信息:Cat类相对路径,方法名hi
classfullpath=com.hspedu.Cat
method=hi
- 通过字符串 **classfullpath **来获取.properties文件中的Cat类相对路径
- 通过字符串 **methodName **来获取.properties文件中方法名hi
使用反射来创建Cat对象:
- 加载类
// classfullpath是通过Properties类获取的.properties
Class cls = Class.forName(classfullpath);
- 通过 cls 的到你加载的类com.hspedu.Cat的对象
Object o = cls.newInstance();
// 拓展一个方法:o.getClass():获取运行类型(o是一个不知道什么类型的对象)
- 通过cls得到你加载的类com.hspedu.Cat 的 methodName"hi" 的方法对象
即:在反射中,可以把方法视为对象
Method method = cls.getMethod(methodName);
- 通过method 调用的invoke(o)方法,相当于来使用对象o调用方法,即通过方法对象来了实现调用方法
method.invoke(o); // 传统方法,对象.方法(),反射机制:方法.invoke(对象)
附:
Class cls = Class.forName(classfullpath);
Object o = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(o);
这其实也是反射(reflection)的一个重要作用:动态的创建对象并调用方法
作用:通过外部文件配置(.properties等类型文件),在不修改源码情况下,来控制程序,符合设计模式ocp原则(开闭原则:不修改源码,扩容功能)
如何理解呢?
在这个案例中,如果我们修改了文件中method的值(方法名),重新运行项目,就可以运行我们指定的方法啦;注意!这里是重新运行,而不是重新编译,因为我们并没有修改源码,但是项目中对象的功能发生了改变
其实有人肯定会说,如果想要通过文件来实现创建对象调用方法,那么只要通过某种方式将文件中的字符串给转化为包名,然后直接new这个对象不就行了?
但样的话,其实本质还是改了源码,这就相当于你直接在源码中new了一个对象,当你名字改变时,你的对象就变了,到时候编译的文件也就自然而然的变了
而使用反射,他是通过编译好的文件,通过反射,映射成一个一个对象,然后操作时,就是寻找我们要操作的东西(类,属性,方法),使我们获取了在运行时分析类以及执行类中方法的能力
3. 获取class对象的三种方式
- Class.forName(“全类名”);
- 类名.class;
- 对象.getClass();
3.1 详解三种方式Class.forName(“全类名”)
- Class.forName(“全类名”);
- 全类名:包名 + 类名
- 全类名的获取:选中类,然后右击点击copy Prefence
- 将所有的类都扫一遍,来获取到对应
- 最常用
- 类名.class;
- 当做参数进行传递
- 对象.getClass();
- 当有了这个类的对象时,才可以使用
- 当有了这个类的对象时,才可以使用
4. 通过Class类来获取构造方法
Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组(包括私有的)
Constructor getConstructor(Class<?>...parameterTypes):返回(指定)单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>…parameterTypes):返回(指定)单个构造方法对象
5. 通过Class类来获取成员变量
Field[] getFields():返回所有公共成员变量对象的数组
Field[] getDeclaredFields():返回所有成员变量对象的数组
Field getField():返回(指定)单个公共成员变量对象
Field getDeclaredField():返回所有公共成员变量对象的数组
6. 通过Class类来获取成员方法
Method[] getMethods():返回所有公共成员变量方法的数组
Method[] getMethods():返回所有公共成员变量方法的数组
Method getMethod():返回所有公共成员变量方法的数组
Method getMethod():返回所有公共成员变量方法的数组
7. 作用
作用1:获取一个类里的所有信息,获取到了之后,再执行其他的业务逻辑
作用2:!!结合配置文件,动态的创建对象并调用方法
7.1 获取类信息
public static void saveObject(Object obj) {
// 1. 获取字节码文件的对象
Class clazz = obj.getClass();
// 2. 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 获取成员变量的名字
String name = field.getName();
// 获取成员变量的值
Object value = field.get(obj);
System.out.println(name + "=" + value);
}
}
7.2 动态创建对象
这个应用就是快速入门里的那个案例;
8. * 八股
8.1 何为反射?
如果说大家研究过框架的底层原理或者咱们自己写过框架的话,一定对反射这个概念不陌生。
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性
– 好好理解这句话,比任何课程对反射的解释都清楚
8.2 反射的应用场景了解么?
像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。
但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。
这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。
比如下面是通过 JDK 实现动态代理的示例代码,其中就使用了反射类 Method 来调用指定的方法。
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("after method " + method.getName());
return result;
}
}
另外,像 Java 中的一大利器 注解 的实现也用到了反射。
为什么你使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
这些都是因为你可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。
8.3 谈谈反射机制的优缺点
优点:可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
缺点:让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。