反射的主要作用就是获取JVM中的Class对象,获取了对象之后可以实现很多功能,比方说IOC容器通过反射创建对象、或者是动态代理(这个实际应用讲完反射原理后稍微再讲一下)。
一、获取Class对象的三种方式:
如何获取Class对象呢?有三种情况:
- Java代码仅经过编译生成了字节码文件,还未加载到内存中
- Java代码加载到了内存中,但是还未创建对象实例
- 已经创建了对象实例
针对这三种情况分别有以下三种方式获取Class对象:
1、Class.forName(“全类名”):只有字节码文件,还未被加载进内存。多用于配置文件,将类名定义在配置文件中
2、类名.class:已经加载进内存但是还没有对象。多用于参数的传递
3、对象名.getClass():有对象希望获取字节码。
同一个字节码文件在一次程序的运行过程中只会被加载一次。不论哪一种方式获取的class类对象都是同一个,即这三种方式只会创建一个Class对象
二、Class对象的功能(即Class类中有哪些常用的方法)
以下代码均假设已经获取到了Person类的Class对象:personClass
1、获取类名
String getName()
// 获取到的是全类名
String className = personClass.getName();
2、获取构造函数
反射获取Class对象后最重要的一个功能就是创建对象,创建对象自然是要通过构造函数。
方法
-
Constructor<?>[] getConstructors
:获取所有public修饰的构造函数 -
Constructor<T> getConstructor(类<?>...parameterTypes)
获取指定的public修饰的构造函数参数
类<?>...parameterTypes
表示要传构造函数参数的类型,因为同一个类中区分构造函数只能通过参数
如要获取构造函数Person(String name, int get),则personClass.getConstructor(String.class, int.class) -
Constructor<?>[] getDeclaredConstructors()
:获取所有的构造函数 -
Constructor<T> getDeclaredConstructor(类<?>...parameterTypes)
:获取任一指定的构造函数
作用
1、获取构造函数当然是为了创建对象
T newInstance(parameters...)
Class.newInstance()
:调用空参的构造方法可以这样简化,但是这个方法好像JDK9之后被废除了。
Person p = new Person;
// 空参构造函数简化调用:personClass.newInstance();
// 假设获取的构造方法有两个参数:Stirng类型的名字和int类型的年龄
Constructor constructor = personClass.getConstructor(String.class, int.class);
constructor.newInstance("martin", 23);
私有构造函数可以通过getDeclaredConstructor()获取Constructor对象,但是不能constructor.newInstance()调用,会报IllegalAccessException(非法访问异常),可以通过执行constructor.setAccessible(true)忽略访问权限修饰符的安全检查
3、获取成员变量
方法
Field[] getFields()
:获取所有public修饰的成员变量Field getField(String name)
:获取所有指定的public修饰的成员变量Field[] getDeclaredFields()
:获取所有的成员变量Field getDeclaredField(String name)
:获取任一指定的成员变量
作用
1、取值get()
、赋值set()
Field field = personClass.getField("name");
Person p = new Person;
Object value = field.get(p); // 获取对象p的name属性
name.set(field, "martin"); // 设置对象p的name属性
同理可以通过field .setAccessible(true)暴力反射
4、获取成员方法
如何:
Method[] getMethods()
:获取所有public修饰的成员函数,包括从父类如Object等类中继承的Method getMethod(String name, 类<?>...parameterTypes)
:
获取所有public修饰的指定的成员函数。第一个参数为函数方法名,后面的参数是函数的参数类型。Method[] getDeclaredMethods()
:获取本类中所有方法,不会获取父类中的Method[] getDeclaredMethod(String name, 类<?>...parameterTypes)
:获取本类中任一指定的方法
同理,私有方法可以通过setAccessible(true)暴力反射
作用:
1、获取方法名
Method.getName()
2、执行方法
Method.invoke()
Person p = new Person;
// 获取eat()的方法对象
Method method_eat = personClass.getMethod("eat");
method_eat.invoke(p);
// 获取eat(String foodName)的方法对象
Method method_eat = personClass.getMethod("eat", String.class);
method_eat.invoke(p, "rice");
三、案例
1、jdbc连接数据库中通过Class.forName()加载数据库驱动
2、代理模式对方法增强(aop的原理也是动态代理),这里稍微看一下动态代理中的JDK代理,先上关键部分的代码
public class StarProxy implements InvocationHandler {
// target接受的是接口,JDK动态代理面向接口代理
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 加强操作
System.out.println("收代理费");
// 调用原方法
Object result = method.invoke(target, args);
//加强操作
System.out.println("互换名片");
return result;
}
// 这个方法返回的是代理对象实例,newProxyInstance()深入进去就是根据反射调用了构造函数生成实例
//PS:生成代理类的方法不一定写在这里,可以写在一个工厂方法中
public Object CreateProxyObject() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
文章是看了视频总结,视频讲的很好,附上视频链接