Java类型信息、反射与动态代理
类型信息
一个类的类型信息(class信息,静态域、静态方法和各种字面量常量)在被类加载机制加载进Java虚拟机后存储在方法区中,平常我们通过类实例化的引用对类信息进行访问,这实际上是对方法区中类型信息的间接访问。
Exampleone one = new Exampleone();
这段代码会在栈中创建Exampleone对象的引用one并指向堆中的Exampleone对象实例信息,而处于堆中的Exampleone对象实例信息又包含指向方法区Exampleone类型信息的引用。
不过,反射机制使得我们可以直接访问一个类的类型信息而不需要实例化类。
反射
获取类的Class对象
一个类的Class对象有三种获取方式。
/* * 类名.class * ---不会主动初始化类--- * */ Class<?> c1 = Exampleone.class; /* * class.forName("类的全名") * ---会报ClassNotFoundException异常--- * */ try { Class<?> c2 = Class.forName("MyProject.CSDN.Exampleone"); } catch (ClassNotFoundException e) { e.printStackTrace(); } /* * 对象.getClass() * ---使用对象实例获取Class--- * */ Exampleone exampleone = new Exampleone(); Class<?> c3 = exampleone.getClass();
上面说的初始化是类加载里的初始化而不是实例对象的初始化,类加载机制分为加载、验证、准备、解析、初始化五个阶段。在初始化阶段将对静态域赋值和执行静态初始化块。
使用Class对象获取常用类型信息与实例信息
/*
* 获取公共字段、公共构造器、公共方法
* */
Field field = c.getField("str");
//第一个参数为方法名,后面跟着参数的class对象,这用于区别重载方法。
Method method = c.getMethod("fun_public",String.class);
Constructor constructor = c.getConstructor();
/*
* 获取所有已定义的字段、方法、构造器。
* 如果是私有属性那么必须将可访问性(Accessible)设置为true
* */
Field field = c.getDeclaredField("str");
Method method1 = c.getDeclaredMethod("fun_private");
method.setAccessible(true);
Constructor constructor = c.getDeclaredConstructor();
/*
* 上面的属性都可以获取相应的对象数组
* class.getXXXXXs()
* */
Field[] fields = c.getDeclaredFields();
/*
* 1.get(Object o)将返回字段变量的Object对象
* 2.如果获取的字段是静态的,那么get()的参数可以是null
* 3.否则字段是实例变量,那么必须传入实例对象才能获取相应的实例变量
* */
String str = (String)field.get(null);
/*
* 调用方法
* 如果是静态方法第一参数设置为null
* 否则必须传入一个实例对象作为调用者
* 后面传入方法所需的参数
* */
method.invoke(null,"example");
/*
* 获取属性修饰符
* */
Method method = c.getMethod("fun_public");
method.getModifiers();
/*
* 获取方法参数类型数组
* */
Type[] types = method.getGenericParameterTypes();
/*
* 获取注解
* */
Annotation annotation = c.getAnnotation(Annotation.class);
动态代理
interface Exampleinte{
public void fun1();
public void fun2(String arg);
public void fun3();
}
class Example implements Exampleinte{
public void fun1(){
System.out.println("I am fun1");
}
public void fun2(String arg){
System.out.println("I am fun2"+arg);
}
public void fun3() {
System.out.println("I am fun3");
}
}
/*
* 创建一个静态方法
* 传入类加载器和需要代理的Examleinte对象
* */
public static Exampleinte ProxyExample(ClassLoader classLoader,final Exampleinte exampleinte){
/*
* 实例化处理器
* 代理对象的所有方法调用都会被重定向到处理器的invoke()方法中。
* */
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("I am Proxy invoke ");
return method.invoke(exampleinte,args);
}
};
/*
* 创建代理对象并返回
* */
Exampleinte exampleone =
(Exampleinte)Proxy.newProxyInstance(classLoader,new Class[]{ Exampleinte.class },handler);
return exampleone;
}
ClassLoader classLoader = Exampleinte.class.getClassLoader();
Example example = new Example();
Exampleinte proxy = ProxyExample(classLoader,example);
proxy.fun1();
proxy.fun2(" 动态代理");
proxy.fun3();
/*
* Output:
I am Proxy invoke I am fun1
I am Proxy invoke I am fun2 动态代理
I am Proxy invoke I am fun3
* */
动态代理使得我们可以在不改动原代码的基础上对方法进行一些测试,并且方便部署和移除。