反射的概念
java 的放射机制:在程序运行时,程序有能力获取一个类的所有方法和属性;并且对于任意一个对象,可以调用它的任意方法或者获取其属性
通俗解析:java 文件需要编译成. class 文件才能被 jvm 加载使用, 对象的. class 数据在 jvm 里就是 Class;我们如果能拿到这个 Class对象,就能获取该 Class对应的对象类型,及在该类型声明的方法和属性值;还可以根据 Class创建相应的类型对象,通过 Field,Method 反过来操作对象
java 相关类介绍
类名 | 描述 |
---|---|
Class | 代表类的实体,在运行的 Java 应用程序中表示类或者接口 |
Field | 类的成员变量(成员变量也称为类的属性) |
Method | 类的方法 |
Constructor | 类的构造方法 |
获取 Class 的三种方法
- 1 通过已知的类型获取 class
// 根据Example 获取Class =》Example.class
public Class getExample(){
Class clazz = Example.class;return clazz;
}
- 2 通过实例对象获取 class
public Class getExampleByInstance(){
Example example = new Example();
// getClass是Object类里面的方法;《?》 是通配符
Class clazz = example.getClass();return (Class)clazz;
}
- 3 通过 Class.forName 获取全路径指定类名的 class
/\*\* forName0 本地方法,C++实现,jvm调用
\* 1 className 是个类名 2 initialize 是否延迟加载 3 loader 加载器
\*/
private static native Class forName0(String className, boolean initialize,
ClassLoader loader, Class caller) throws ClassNotFoundException;
public static Class forName(String className) throws ClassNotFoundException {
Class caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
// 两个forName方法最终都会调用forName0方法去加载class
public static Class forName(String name,
boolean initialize, ClassLoader loader) throws ClassNotFoundException {
....
return forName0(name, initialize, loader, caller);
}
// 示例:通过java.lang.Integer
public Class getInteger()throws ClassNotFoundException{
Class clazz = Class.forName("java.lang.Integer");return (Class)clazz;
}
JAVA 反射 API
- Class 常用操作方法
//获取所有的构造方法 / private public
public Constructor\[\] getDeclaredConstructors()
//获取特定的构造方法 / private public
public Constructor getDeclaredConstructor(Class... parameterTypes)
//获取类的父类
public native Class getSuperclass()
//获取类实现的接口
private Class\[\] getInterfaces(boolean cloneArray)
//获取在类内定义的内部类或接口
public Class\[\] getDeclaredClasses()
//获取所有的方法
public Method\[\] getDeclaredMethods() throws SecurityException
//根据方法名和参数获得特定的方法
public Method getDeclaredMethod(String name, Class... parameterTypes)
//获取类型的定义的所有属性
public Field\[\] getFields() throws SecurityException
// 根据属性命名获得特定的Field
public Field getField(String name)
- Method 常用的操作方法
//获得方法的放回类型
public Class getReturnType()
//获得方法的传入参数类型
public Class\[\] getParameterTypes()
//obj是实例对象,args是方法,反过来由Method控制对象的方法调用
public Object invoke(Object obj, Object... args)
- Field 常用的操作方法
//属性与obj相等则返回true
public boolean equals(Object obj)
//获得obj中对应的属性值
public Object get(Object obj)
//设置obj中对应属性值
public void set(Object obj, Object value)
- Constructor
//根据传递的参数创建类的对象:initargs 构造方法参数
public T newInstance(Object... initargs)
- 1 根据 class 创建对象
//方式一 clazz.newInstance()
Class clazz = Example.class;
Example example = clazz.newInstance();
//方式二 先获取再由Constructor:clazz.getConstructors()/getConstructor(...)
//再由Constructor.newInstance 方法构造对象
-----------------------------------------
public class Example {
private int value;
public Example(){ } // 如果只声明有参构造函数,clazz.newInstance()会报错
public Example(Integer value){ this.value = value; }
static public void main(String\[\] args) throws Exception{
Class clazz = Example.class;
//根据指定构造函数参数获取Constructor
Constructor constructor = clazz.getConstructor(Integer.class);
Example example = constructor.newInstance(100);
System.out.println(example.value);
}
}
- 2 由 class 获取 Field,并操作实例的属性
public class Example {
private int value , count;
static public void main(String\[\] args) throws Exception{
Class clazz = Example.class;
//获取所有的属性,getField只能获取public的属性
Field\[\] fs = clazz.getDeclaredFields();
//根据名称获取指定 Field
Field value = clazz.getDeclaredField("value");
Example example = clazz.newInstance();
//使用反射机制可以打破封装性,导致了java对象的属性不安全
value.setAccessible(true); //setAccessible(true)让private的参数可赋值操作
//由Field反过去设置example的值
value.set(example,100);
System.out.println(example.value);
}
}
- 3 由 class 获取 Method,并反射调用实例方法
public class Example {
public static void main(String\[\] args) throws Exception {
Class clazz = Example.class;
Example example = clazz.newInstance();
Method\[\] methods = clazz.getDeclaredMethods();
//getDeclaredMethod和getMethod是:getMethod只能返回public的方法
Method method = clazz.getDeclaredMethod("hello", String.class);
method.setAccessible(true);
method.invoke(example, "cscw");
}
private void hello(String name) { System.out.println(name + " Hello!"); }
}
-----
cscw Hello!
反射机制应用的场景
1 动态拓展:假设有同一组类是实现相同的接口,并且类的加载方式不限制。当我们需要那种具体类实现的功能时,只需加载. class 文件,并获取对应的 Class对象。可以由 Class 或者 Constructor 实例化对象 instance;根据接口定义,可以获取 Class里的某一方法 Method,并配合 instance 反射调用功能方法
2 Spring 的 IOC 就是基于反射机制实现
3 JDK 的动态代理
反射和 JDK 动态代理
- 在 Java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口。通过这个类和接口可以生成 JDK 动态代理类或动态代理对象
public interface InvocationHandler {
//所有方法都会调用此代理方法
Object invoke(Object var1, Method var2, Object\[\] var3) throws Throwable;
}
public class Proxy implements Serializable{
...
//根据interfaces和InvocationHandler生成代理对象
public static Object newProxyInstance(ClassLoader loader,
Class\[\] interfaces, InvocationHandler h)
...
}
JDK 的动态代理由 Proxy 和 InvocationHandler 实现;而被代理对象必须实现一个接口。代理对象由 Proxy 生成,可转为接口 interface 的实现类对象 OBJ。当调用 OBJ 的方法时,则会触发 InvocationHandler.invoke,参数依次为「代理对象」,「Method 对象」, 和「方法 Method 所需的参数」。在 invoke 方法可以加入拓展的逻辑,如日志记录操作;「并可以在 invoke 里利用反射的技术调用被代理对象方法」
示例
public class ExampleFactory implements InvocationHandler{
private T target; //被代理对象
public T bind(T obj){
target = obj;return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),this);
}
/\*\* Object o 是代理对象; o的方法调用 -> ExampleFactory.invoke
\* invoke(...) -> 在invoke方法里面 反射调用代理对象方法+增强逻辑
\*/
@Override
public Object invoke(Object o, Method method, Object\[\] objects) throws Throwable {
//增强逻辑
System.out.println("log start");
//反射调用被代理对象方法
Object result = method.invoke(target,objects);
System.out.println("log end");return result;
}
}
-----------
public interface Face {
void hello(String name);
}
---------
//被代理对象必须实现一个接口,并由接口方法对方提供功能
public class Example implements Face {
public void hello(String name) {
System.out.println(name + " Hello!");
}
public static void main(String\[\] args) {
//ExampleFactory 相当于一个中介人
ExampleFactory factory = new ExampleFactory<>();
//example 是代理对象
Face example = exampleProxy.bind(new Example());
example.hello("思婷");
}
}
-----log start
思婷 Hello!log end