Java_反射(反射及原理 / 动态代理及原理)
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/77511333
反射: 在运行状态时, 能够获取任意一个类的所有属性和方法; 能够调用任意一个对象的所有属性和方法.
反射性能较低, 一般情况下不考虑用反射.
Proxy 动态代理: 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作
Bean类
java">public class Bean {
/**
* 公有属性
*/
public int number = 10;
/**
* 私有属性
*/
private String name = Bean.class.getSimpleName();
/**
* 静态属性
*/
public static int max = Integer.MAX_VALUE;
/**
* 常量属性
*/
protected static final int min = Integer.MIN_VALUE;
/**
* 无参构造
*/
public Bean(){}
/**
* 有参构造
* @param numbder
*/
public Bean(int number){
this.number = number;
}
/**
* 私有构造
* @param number
* @param name
*/
private Bean(int number, String name){
this.number = number;
this.name = name;
}
protected Bean(int number, String name, int max){
this.number = number;
this.name = name;
Bean.max = max;
}
/**
* 公有方法
* @param number
*/
public void setNumber(int number){
this.number = number;
}
public int getNumber(){
return this.number;
}
/**
* 私有方法
*/
private void setName(String name){
this.name = name;
}
private String getName(){
return this.name;
}
/**
* 静态方法
* @param max
*/
public static void setMax(int max){
Bean.max = max;
}
public static int getMax(){
return Bean.max;
}
/**
* protected方法
* @param min
* @return
*/
protected int getMin(){
return Bean.min;
}
@Override
public String toString() {
return "Bean [number=" + number + ", name=" + name + ", max=" + Bean.max + ", min =" + Bean.min + "]";
}
}
java">@Deprecated
public class SubBean extends Bean implements Serializable{
/**
* 调用父类方法, 并抛出一个异常
* @return
* @throws Exception
*/
public int getSuperNumber(int number) throws Exception{
return super.getNumber();
}
}
Class文件对象
java">/**
* 获取Class文件对象的几种方式
* @author Luzhuo
*/
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1: 通过对象获得
GetClass getClass = new GetClass();
Class<? extends GetClass> class1 = getClass.getClass();
// 方式2: 通过数据类型的一个静态calss属性
Class<GetClass> class2 = GetClass.class;
// 方式3: 通过全类名获得
Class<?> class3 = Class.forName("反射.reflect.GetClass");
// 方式4: 通过 类加载器 加载
Class<?> class4 = GetClass.class.getClassLoader().loadClass("反射.reflect.GetClass");
System.out.println(class1 == class2 && class2 == class3 && class3 == class4); // true
}
}
反射
反射: 在运行状态时, 能够获取任意一个类的所有属性和方法; 能够调用任意一个对象的所有属性和方法.
java">/**
* 反射: 在运行状态时, 能够获取任意一个类的所有属性和方法;
* 能够调用任意一个对象的所有属性和方法.
*
* 反射性能较低, 一般情况下不考虑用反射.
* @author Luzhuo
*/
public class reflect {
public static void main(String[] args) throws Exception {
// 创建对象(构造方法)
constructor();
// 属性
field();
// 方法
method();
// 案例: 通过反射调用已存在对象的方法
callOldInstance();
}
/**
* 对象的创建(构造方法)
* 访问 private / protected 修饰的构造方法时, 需要取消语法检查
*/
private static void constructor() throws Exception {
// 方式1: Class文件对象
Class<?> clazz = Bean.class;
Bean instance1 = (Bean) clazz.newInstance();
// 方式2: Class文件对象的构造方法
Constructor<?>[] cons = clazz.getConstructors(); // 所有公共构造方法
Constructor<?>[] allcons = clazz.getDeclaredConstructors(); // 所有构造方法(public / private / protected)
Constructor<?> con = clazz.getConstructor(int.class); // 获取指定公有构造方法
Constructor<?> decCon = clazz.getDeclaredConstructor(int.class, String.class); // 获取指定私有构造方法
decCon.setAccessible(true); // 取消Java语言访问检查(获取 private / protected 构造时使用)
Bean instance2 = (Bean) cons[0].newInstance(200);
Bean instance3 = (Bean) cons[1].newInstance();
System.out.println(instance1 == instance2 || instance2 == instance3); // false
// 访问 private / protected 修饰的构造方法时, 需要取消语法检查
}
/**
* 属性
* 访问 private / protected / static final 修饰的属性时, 需要取消语法检查
* 修改 private / protected 修饰的属性时, 需要取消语法检查, 不能修改 static final 修饰的属性
* 能获取本类所有字段, 但不能获取父类除public外的所有字段
*/
private static void field() throws Exception {
Class<?> clazz = Bean.class;
// 获取成员变量
Field[] fields = clazz.getFields(); // 所有公共字段(public)(含父类)
Field[] allfields = clazz.getDeclaredFields(); // 本类所有字段(public / private / protected)
Object obj = clazz.getConstructor().newInstance(); // 创建对象
// 使用成员变量
// 公有属性
Field number = clazz.getField("number"); // 获取指定公有字段(public)
System.out.println("number: " + number.getInt(obj)); // 获取字段的值
number.set(obj, 1000); // 设置字段的值
System.out.println(obj.toString());
// 私有属性
Field name = clazz.getDeclaredField("name"); // 获取指定字段(...)
name.setAccessible(true);
System.out.println("name: " + name.get(obj));
name.set(obj, "set name");
System.out.println(obj.toString());
// 静态属性
Field max = clazz.getField("max");
System.out.println("max: " + max.getInt(obj));
max.set(obj, 123);
System.out.println(obj.toString());
// 常量属性(protected)
Field min = clazz.getDeclaredField("min");
min.setAccessible(true);
System.out.println("min: " + min.getInt(obj));
// min.set(obj, 321); // static final 修饰的属性不允许修改
System.out.println(obj.toString());
// 访问 private / protected / static final 修饰的属性时, 需要取消语法检查
// 修改 private / protected 修饰的属性时, 需要取消语法检查, 不能修改 static final 修饰的属性
// 能获取本类所有字段, 但不能获取父类除public外的所有字段
}
/**
* 方法
* 能获取该类的所有方法, 但是不能获取父类除public外的所有方法
* 访问 private / protected 修饰的方法时, 需要取消语法检查
*/
private static void method() throws Exception{
Class<?> clazz = Bean.class;
Method[] allmethods = clazz.getMethods(); // 获取所有公共方法(public / public final / public static)(含父类)
Method[] methods = clazz.getDeclaredMethods(); // 获取本类所有方法(public / private / public static / private / protected ...)
Object obj = clazz.getConstructor().newInstance(); // 创建对象
// 方法
// 公有
Method setNumber = clazz.getMethod("setNumber", int.class); // 获取指定公共方法(public)
setNumber.invoke(obj, 1000); // 调用方法
Method getNumber = clazz.getMethod("getNumber");
int number = (int) getNumber.invoke(obj);
System.out.println("getNumber: " + number);
// 私有
Method setName = clazz.getDeclaredMethod("setName", String.class); // 获取指定方法(...)
setName.setAccessible(true);
setName.invoke(obj, "abc");
Method getName = clazz.getDeclaredMethod("getName");
getName.setAccessible(true);
String name = (String) getName.invoke(obj);
System.out.println("getName: " + name);
// 静态
Method setMax = clazz.getMethod("setMax", int.class);
setMax.invoke(obj, 123); // 调用方法
Method getMax = clazz.getMethod("getMax");
int max = (int) getMax.invoke(obj);
System.out.println("getMax: " + max);
// protected
Method getMin = clazz.getDeclaredMethod("getMin");
getMin.setAccessible(true);
int min = (int) getMin.invoke(obj);
System.out.println("getMin: " + min);
// 能获取该类的所有方法, 但是不能获取父类除public外的所有方法
// 访问 private / protected 修饰的方法时, 需要取消语法检查
}
/**
* 通过反射调用已存在对象的方法
*/
private static void callOldInstance() throws Exception {
Bean bean = new Bean();
Class<? extends Bean> clazz = bean.getClass();
// 通过反射调用私有方法
Method getName = clazz.getDeclaredMethod("getName");
getName.setAccessible(true);
String name = (String) getName.invoke(bean);
System.out.println("getName: " + name);
bean.number = 100000000;
// 通过反射调用已存在对象的公有方法
Method getNumber = clazz.getMethod("getNumber");
int number = (int) getNumber.invoke(bean);
System.out.println("getNumber: " + number);
}
}
反射补充
反射相关的一些补充
java">/**
* 反射的一些补充
* @author Luzhuo
*/
public class Other {
public static void main(String[] args) throws Exception {
Class<?> clazz = SubBean.class;
// 包名
String packageName = clazz.getPackage().getName();
System.out.println("包名: " + packageName); // 包名: 反射.Class
// 完整类名
String className = clazz.getName();
System.out.println("完整类名: " + className); // 完整类名: 反射.Class.Clazz
// 类名
String classSimpleName = clazz.getSimpleName();
System.out.println("类名: " + classSimpleName); // 类名: Clazz
// 获取接口
Class<?>[] interfaces = clazz.getInterfaces();
System.out.println("Interfaces: " + Arrays.toString(interfaces));
// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("SuperClass: " + superClass);
// 获取异常
Method getSuperNumber = clazz.getMethod("getSuperNumber", int.class);
Class<?>[] exceptions = getSuperNumber.getExceptionTypes();
System.out.println("Exceptions: " + Arrays.toString(exceptions));
// 获取权限修饰符
int mod = getSuperNumber.getModifiers();
System.out.println("Mod: " + Modifier.toString(mod));
// 获取返回值类型
Class<?> returnType = getSuperNumber.getReturnType();
System.out.println("Return Type: " + returnType);
// 获取参数列表
Class<?>[] parameterTypes = getSuperNumber.getParameterTypes();
System.out.println("ParameterTypes: " + Arrays.toString(parameterTypes));
// 方法名
System.out.println("GetSupperNumber Name: " + getSuperNumber.getName());
// 获取属性类型
Field number = clazz.getField("number");
Class<?> type = number.getType();
System.out.println("Field Type: " + type);
// 获取类的注解
// 其他获取注解的方法, 详见 注解 http://blog.csdn.net/rozol/article/details/77758192 文章
Annotation[] classAnnotations = clazz.getAnnotations();
System.out.println("ClassAnnotations: " + Arrays.toString(classAnnotations));
}
}
反射的原理
先看下使用方式
java">// 使用正常的方式创建对象
Bean bean1 = new Bean(100);
// 使用反射的方式创建对象
Class clazz = Bean.class; // 获得字节码文件对象
Constructor constructor = clazz.getConstructor(int.class); // 获得构造对象
Bean bean2 = (Bean) constructor.newInstance(100); // 创建实例
同一个字节码对象
通过Java的双亲委托模型加载类的机制可知, 内存中只有一份同全类名的字节码文件对象, 所以以下这两种方式获得的字节码文件对象同一个.
java">Bean bean1 = new Bean(100);
Class clazz1 = bean1.getClass();
Class clazz2 = Bean.class;
System.out.println(clazz1 == clazz2); // true
new Bean()的原理:
当我们使用以下代码创建对象时,
java">Bean bean1 = new Bean(100);
- 获取 Bean.class字节码文件对象 (与反射用的是同一个字节码文件对象);
Bean(100)
就是构造方法- new 构造方法, 创建了一个实例
怎么样, 是不是与反射是一致的.
字节码对象
字节码文件对象的产生:
字节码文件对象的类型:
字节码文件对象的类型是 Class类型
字节码文件对象的加载时机
字节码文件对象的组成:
类里面有什么, 字节码文件对象里就有什么.
类 | 字节码文件对象 |
---|---|
成员变量 | 成员变量对象(Constructor) |
构造函数 | 构造函数对象(Method) |
成员函数 | 成员函数对象(Field) |
注解 | 注解对象(Annotation) |
.etc | .etc对象 |
反射为什么会慢
比喻:
正常的使用相当于你已经知道这个东西就在房间的那个位置, 直接去拿就好了.
反射的使用相当于你不知道这个东西在哪, 但就在这个房间里, 你得一个一个的翻.
你们说哪个快?哪个慢?
这么多构造方法你得找吧
java">Constructor<?>[] allcons = clazz.getDeclaredConstructors();
这么多变量你得找吧
java">Field[] fields = clazz.getFields();
这么多方法哪个才是呢, 你这里没有, 你父亲那可能会有呢
java">Method[] allmethods = clazz.getMethods();
就算找到了这个方法, 你这个invoke()的可变Object参数列表, 还得帮你去匹配呀.
java">Method setNumber = clazz.getMethod("setNumber", int.class);
setNumber.invoke(obj, 1000);
动态代理
Proxy 动态代理: 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作
jdk动态代理是由Java内部的反射机制来实现的
jdk动态代理需要基于同一的 接口
java">/**
* Proxy 动态代理
* 用来修改已经具有的对象的方法, 控制方法是否执行, 或在方法执行之前和执行之后做一些额外的操作
* jdk动态代理是由Java内部的反射机制来实现的
* jdk动态代理需要基于同一的 接口
* @author Luzhuo
*/
public class ProxyTest {
public static void main(String[] args) {
User userProxy = initAccess(new ProxyTest.UserImpl());
System.out.println(userProxy.getName());
System.out.println(userProxy.getAge());
}
public static User initAccess(final User user){
User userProxy = (User)Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getName".equals(method.getName())){
System.out.println("getName() 调用之前");
Object result = method.invoke(user, args);
System.out.println("getName() 调用之后");
return result;
}
return method.invoke(user, args);
}
});
return userProxy;
}
public static interface User {
public String getName();
public int getAge();
}
public static class UserImpl implements User {
@Override
public String getName() {
System.out.println("UserServiceImpl - getName");
return "name";
}
@Override
public int getAge() {
System.out.println("UserServiceImpl - getAge");
return 12;
}
}
}
输出结果:
getName() 调用之前
UserServiceImpl - getName
getName() 调用之后
name
UserServiceImpl - getAge
12
Java Proxy 的原理
通过动态构建了新的Class实现的, 在我们调用接口时, 才会去生成字节码, 所以对性能消耗较大.
由ProxyGenerator构建代理Class字节码文件, 再由ClassLoader转成Class字节码文件对象.
生成的字节码文件对象会被放在内存的方法区, 不会被回收掉.
新生成的字节码文件对象
新生成给的类, 类名为"接口名$Proxy", 同时该类实现User接口.
由ProxyGenerator构建, 可通过以下代码写入文件.
java">@Test
public void buildProxyClass() throws Exception{
byte[] bytes = ProxyGenerator.generateProxyClass("User$Proxy", new Class[]{User.class});
FileOutputStream os = new FileOutputStream(new File("User$Proxy.class"));
os.write(bytes);
os.close();
}
User$Prox.class
我们用反编译工具对该文件进行查看, 得到以下代码
java">package p000;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/* renamed from: User$Proxy */
public final class User$Proxy extends Proxy implements User {
/* renamed from: m0 */
private static Method f0m0;
/* renamed from: m1 */
private static Method f1m1;
/* renamed from: m2 */
private static Method f2m2;
/* renamed from: m3 */
private static Method f3m3;
/* renamed from: m4 */
private static Method f4m4;
static {
try {
f1m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
f3m3 = Class.forName("ProxyTest$User").getMethod("getName", new Class[0]);
f2m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
f4m4 = Class.forName("ProxyTest$User").getMethod("getAge", new Class[0]);
f0m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
} catch (ClassNotFoundException e2) {
throw new NoClassDefFoundError(e2.getMessage());
}
}
public User$Proxy(InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean) this.h.invoke(this, f1m1, new Object[]{obj})).booleanValue();
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e2) {
UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
}
}
public final int getAge() {
try {
return ((Integer) this.h.invoke(this, f4m4, null)).intValue();
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e2) {
UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
}
}
public final String getName() {
try {
return (String) this.h.invoke(this, f3m3, null);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e2) {
UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
}
}
public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, f0m0, null)).intValue();
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e2) {
UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
}
}
public final String toString() {
try {
return (String) this.h.invoke(this, f2m2, null);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable e2) {
UndeclaredThrowableException undeclaredThrowableException = new UndeclaredThrowableException(e2);
}
}
}
可见内部是通过反射获得方法对象
java">f3m3 = Class.forName("ProxyTest$User").getMethod("getName", new Class[0]);
当我们通过接口调用
java">userProxy.getName()
时, User$Proxy类内部其实是用InvocationHandler接口进行回调
java">return (String) this.h.invoke(this, f3m3, null);
喏, 就回调执行我们写的这些代码.
java">new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getName".equals(method.getName())){
System.out.println("getName() 调用之前");
Object result = method.invoke(user, args);
System.out.println("getName() 调用之后");
return result;
}
return method.invoke(user, args);
}
}