反射. 程序运行期间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.class
true, 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是如何构建一个实例的。