【JavaSE】Java反射机制详解
【JavaSE】Java反射机制详解
文章目录
- 【JavaSE】Java反射机制详解
- 一:反射简介?
- 二:反射的应用场景
- 三:反射实战
- 1:获取 Class 对象
- 2:获取构造方法
- 3:获取字段
- 4:获取方法
- 5: 反射的一些基本操作
- 四:反射的优缺点?
一:反射简介?
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。
Class clazz = Class.forName("com.yby.Writer");
Method method = clazz.getMethod("setName", String.class);
Constructor constructor = clazz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object,"blblccc");
像上面这个例子,就可以理解为“反射”。
二:反射的应用场景
- 开发通用框架:像 Spring,为了保持通用性,通过配置文件来加载不同的对象,调用不同的方法。
- 动态代理:在面向切面编程中,需要拦截特定的方法,就会选择动态代理的方式,而动态代理的底层技术就是反射。
- 注解:注解本身只是起到一个标记符的作用,它需要利用发射机制,根据标记符去执行特定的行为。
比如下面是通过 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;
}
}
三:反射实战
1:获取 Class 对象
如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象:
1. 知道具体类的情况下可以使用:
Class alunbarClass = TargetObject.class;
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化
2. 通过 Class.forName()
传入类的全路径获取:
Class alunbarClass1 = Class.forName("cn.yby.TargetObject");
3. 通过对象实例instance.getClass()
获取:
TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();
4. 通过类加载器xxxClassLoader.loadClass()
传入类路径获取:
ClassLoader.getSystemClassLoader().loadClass("cn.yby.TargetObject");
通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行
2:获取构造方法
Class 对象提供了以下方法来获取构造方法 Constructor 对象:
getConstructor()
:返回反射类的特定 public 构造方法,可以传递参数,参数为构造方法参数对应 Class 对象;缺省的时候返回默认构造方法。getDeclaredConstructor()
:返回反射类的特定构造方法,不限定于 public 的。getConstructors()
:返回类的所有 public 构造方法。getDeclaredConstructors()
:返回类的所有构造方法,不限定于 public 的。
Class c2 = Class.forName("com.yby.Writer");
Constructor constructor = c2.getConstructor();
Constructor[] constructors1 = String.class.getDeclaredConstructors();
for (Constructor c : constructors1) {
System.out.println(c)
}
3:获取字段
大体上和获取构造方法类似,把关键字 Constructor 换成 Field 即可。
Method setNameMethod = clazz.getMethod("setName", String.class);
Method getNameMethod = clazz.getMethod("getName");
4:获取方法
大体上和获取构造方法类似,把关键字 Constructor 换成 Method 即可。
Method[] methods1 = System.class.getDeclaredMethods();
Method[] methods2 = System.class.getMethods();
如果你想反射访问私有字段和(构造)方法的话,需要使用Constructor/Field/Method.setAccessible(true)
来绕开 Java 语言的访问限制。
5: 反射的一些基本操作
-
创建一个我们要使用反射操作的类
TargetObject
。package cn.yby; public class TargetObject { private String value; public TargetObject() { value = "yby"; } public void publicMethod(String s) { System.out.println("I love " + s); } private void privateMethod() { System.out.println("value is " + value); } }
-
使用反射操作这个类的方法以及参数
package cn.yby;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
Class<?> targetClass = Class.forName("cn.yby.TargetObject");
TargetObject targetObject = (TargetObject) targetClass.newInstance();
/**
* 获取 TargetObject 类中定义的所有方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
/**
* 获取指定方法并调用
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
publicMethod.invoke(targetObject, "yby");
/**
* 获取指定参数并对参数进行修改
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改我们取消安全检查
field.setAccessible(true);
field.set(targetObject, "yby");
/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}
输出内容:
publicMethod
privateMethod
I love yby
value is yby
四:反射的优缺点?
反射可以让我们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利。
不过,反射让我们在运行时有了分析操作类的能力的同时,也增加了安全问题,比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。