反射相关:
- Java反射机制是在运行状态中
- 对于任意一个类,都能知道这个类的所以属性和方法;
- 对于任何一个对象,都能够调用它的任何一个方法和属性;
- 这样动态获取新的以及动态调用对象方法的功能就叫做反射。
- 反射的基础是Class(java.lang.Class)类。
一、java代码在计算机里的三个时期
class文件其实是字节码文件,也可认为是二进制文件。
class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。注意:这里的Class类型是一个名叫Class的class。它长这样:
public final class Class {
private Class() {} ... //getField、getConstructor等方法皆在此处定义
}
三种时期都可以抽离出在某个类的Class(java.lang.Class)对象,Class对象会将java的一个类对象的内容概括为三个对象,包含所有成员变量的Field[]对象,包含所有构造函数的Constructor[]对象,包含所有成员方法的Method[]对象。
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)。
- 源码阶段使用Class.forName("全包名")的方法加载,即要有src下的路径和类名,适合配置文件使用,因为这种方式是靠字符串加载类对象的。
- Class类对象阶段使用类名.class的方法加载,多用于参数传递。
- 运行阶段使用对象名.getClass()加载进去,适用于对象已经建立的。
二、设置 Class cls为从student类中抽取的Class对象。
cls有常用的几种方法,有cls.getName()获取类名等。
1. 针对成员变量的有(以下四种方法返回field或field[]的类型):
field:getField(String name) //获取类型为public名字为name的成员变量
field[]:getFields() //获取所有类型为public的成员变量
field:getDeclaredField(String name) //获取名字为name的成员变量,类型不限
field[]:getDeclaredFields() //获取所有的成员变量,只要声明过的,类型不限
对于field对象常用的有set和get方法,
//该方法返回类型Object对象,实际为field所保存的成员变量的类型,因此可向下转型。
//obj为student实例化出来的对象。返回值为obj对象上字段field所表示的值。
get(Object obj)
//该方法返回类型为void对象。obj为student实例化出来的对象。
//value为将在obj对象的 Field 字段上所设置的新值。
set(Object obj,Object value)
2. 针对构造方法的有(以下四种方法返回Constructor或Constructor<?>[]的类型):
//获取类型为public,参数为Class<?>... parameterTypes的构造方法
//Class<?>... parameterTypes取值String.class、Int.class这样的类型
//具体参数看实际构造方法。
Constructor:getConstructor(Class<?>... parameterTypes)
//获取所有类型为public的构造方法。
Constructor<?>[]:getConstructors()
//获取所有构造方法。类型不限。
Constructor<?>[]:getDeclaredConstructors()
//获取所有构造方法。类型不限,参数同上。
Constructor:getDeclaredConstructor(Class<?>... parameterTypes)
对于Constructor对象常用的有newInstance方法
//该方法返回自类型为Object,如果该构造函数有参数要在后面加上实参去实例化对象。
constructor.newInstance(Object... initargs)
//该方法更简单直接,但只适用于无参的构造函数实例化
//cls为Class类,例如Student.class,该实例化方法省去了获取构造方法的过程
cls.newInstance()
3. 针对成员方法的有(以下四种方法返回Method或Method[]的类型):
//获取类型为public,名字为name,参数为Class<?>... parameterTypes的成员方法方法。
//Class<?>... parameterTypes取值如在获取构造函数所述。
Method:getMethod(String name,Class<?>... parameterTypes)
Method[]:getMethods() //获取获取类型为public的所有成员方法。
//获取名字为name,参数为Class<?>... parameterTypes的成员方法方法。
//Class<?>... parameterTypes取值如在获取构造函数所述。类型不限。
Method:getDeclaredMethod(String name,Class<?>... parameterTypes)
Method[]:getDeclaredMethods() //获取所有成员方法,类型不限。
对于Method对象常用的有invoke方法
//该方法返回类型Object对象
//实际为Method所代表的成员方法所返回的类型,因此可向下转型
//obj为student实例化出来的对象。Object... args为传入方法的实参
//返回值为obj执行Method后的结果,如果原方法返回值为void则返回null
invoke(Object obj,Object... args)
注:AccessibleObject 类是Field、Constructor和Method都基类,因此这三者都可以通过 名字.setAccessible(true)的形式取消Java 语言访问控制检查,从而导致反射机制具有了能够更改私有变量的能力。请注意使用。有些安全框架会禁用setAccessible(true)方法,因此它也不是什么时候都能起作用的。
4. 获取父类及接口
Class cla = Integer.class;
//获取父类的Class对象
Class n = cla.getSuperclass();
//获取所有继承接口的Class对象
Class[] is = s.getInterfaces();
//instanceof可用来判断两个对象间是否具有继承关系
Object n = Integer.valueOf(123);
boolean isDouble = n instanceof Double; // false
动态代理相关:
//动态代理的底层实现依靠反射
public class Main {
public static void main(String[] args) {
//代理的具体内容
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
//动态代理实际上是JDK在运行期根据代理内容动态创建class字节码并加载的过程。
//从而产生接口的实例
interface Hello {
void morning(String name);
}
把上面的动态代理改写为静态实现类大概长这样(这个类由JDK帮我们编写):
public class HelloDynamicProxy implements Hello {
InvocationHandler handler;
public HelloDynamicProxy(InvocationHandler handler) {
this.handler = handler;
}
public void morning(String name) {
handler.invoke(
this,
Hello.class.getMethod("morning"),
new Object[] { name });
}
}