反射. 程序运行期间JVM会对任意一个类洞悉它的属性和方法,对任意一个对象都能够访问它的属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。
反射本身的用法很简单,但是背后牵扯的东西则不那么简单,从简单的使用入手,三言两语就可以说完了,但是牵扯到背后的东西,估计就得掉几根头发了,好在已经有前人分享了很多,可以少掉几根。
为什么我们需要反射
反射是实现一些只有动态语言才有的特性的关键。并非所有事情都可以在编译时确定,有时我们需要实例化一个类或仅根据文字字符串名称调用一个方法。
如果没有提供反射,开发人员就必须做一些像字节码工程这样的脏活。许多框架大量使用反射功能。耳熟能详的 Spring Mybatis , Fastjson 等等
反射的基本使用
获取一个对象实例
/*** @author luobo.cs@raycloud.com* @since 2021/7/17 10:19 下午*/public class ReflectTest {public static void main(String[] args) throws Exception {//Spring Bean的生命周期一环(). 首先实例化,然后是aware接口以及init等等方法final Class<?> clazz = Class.forName("java.lang.Object",//package.classtrue, ReflectTest.class.getClassLoader());final Object object = clazz.newInstance();System.out.println(object.toString());}}
访问一个字段
实例化一个java.util.ArrayList
public class ReflectTest {@SuppressWarnings("unchecked")public static void main(String[] args) throws Exception {final Class<ArrayList<Object>> clazz = (Class<ArrayList<Object>>) Class.forName("java.util.ArrayList",true, ReflectTest.class.getClassLoader());final ArrayList<Object> object = clazz.newInstance();object.add("chenshun00");final Field elementData = object.getClass().getDeclaredField("elementData");elementData.setAccessible(true);final Object[] objects = (Object[]) elementData.get(object);System.out.println(Arrays.toString(objects));}}//output//[chenshun00, null, null, null, null, null, null, null, null, null]
访问一个方法
访问一个方法同上,不过getDeclaredField,换成了 getDeclaredMethod
public class ReflectTest {@SuppressWarnings("unchecked")public static void main(String[] args) throws Exception {final Class<ArrayList<Object>> clazz = (Class<ArrayList<Object>>) Class.forName("java.util.ArrayList",true, ReflectTest.class.getClassLoader());final ArrayList<Object> object = clazz.newInstance();object.add("chenshun00");final Method method = object.getClass().getDeclaredMethod("contains",Object.class);method.setAccessible(true);final boolean result = (boolean) method.invoke(object, "chenshun00");System.out.println(result);}}//output --> true
构建构造函数
package com.raycloud.api.common.core.http;/*** @author luobo.cs@raycloud.com* @since 2021/7/17 10:49 下午*/public class Temp {public static Temp first = new Temp("first");public static Temp second = new Temp("second");private final String name;private Temp(String name) {this.name = name;}public String getName() {return name;}}------------------------------------------------------------------------package com.raycloud.api.common.core.http;import java.lang.reflect.Constructor;public class ReflectTest {@SuppressWarnings("unchecked")public static void main(String[] args) throws Exception {final Class<Temp> clazz = (Class<Temp>) Class.forName("com.raycloud.api.common.core.http.Temp",true, ReflectTest.class.getClassLoader());final Constructor<Temp> constructor = clazz.getDeclaredConstructor(String.class);constructor.setAccessible(true);final Temp temp = constructor.newInstance("chenshun00");System.out.println(temp.getName());}}
他们特点都是 setAccessible(true) ,如果不设置只能访问 public 的字段哦,如果设置了可以访问到 private 等等,如果不设置会出现如下错误.
Exception in thread “main” java.lang.IllegalAccessException: Class com.raycloud.api.common.core.http.ReflectTest can not access a member of class com.raycloud.api.common.core.http.Temp with modifiers “private”
引申: 反射引入动态性相对而言影响了系统等安全🔐,那么有没有什么方式,禁止反射对某一些Bean的操作呢?
反射依赖的是什么
反射可以在运行时动态获取class对象或实例,并且调用这些对象或实例的方法。
如此必然有一个数据的输入和输出,那么在调用反射的时候输入输出分别是什么. 从不同的层面可以给出不同的解释。
- 加载输入的是class字节码文件,输出的是位于内存中的Class实例对象 把字节码文件加载Class的过程
- 输入的是Class实例,输出的时候实例调用方法后的接口,具体反射API的应用.
这个过程就涉及以下这些问题。
- A.class 文件,根据源代码生成的class文件. (Javac将源代码生成字节码class文件)
- 如何加载class文件到JVM进程中 (从物理层面的字节码文件编程内存中的一个Class实例)
- Class.forName(“package.Class”,true,ClassLoader) 的过程中发生了什么。
- 加载class文件到JVM中—-> 如何验证呢? **
-verbose** - classLoader用来干啥(类加载机制)
- 加载class文件到JVM中—-> 如何验证呢? **
- JVM是如何构建一个实例的。
