Java反射机制
文章目录
一、Java反射机制概述概述
Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器
Class类
Class类是Java中的一个特殊的类,它用来描述JVM运行时类或者接口的信息。也可以说所有的运行时类都可以当作Class类的实例。
通过反射机制后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含 了特定某个结构(class/interface/enum/annotation/primitive type/void/[]
)的有关信息。
- Class本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
在Object类中定义了以下的方法,此方法
将被所有子类继承:public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即: 可以通过对象反射求出类的名称。
二、Class类常用方法
static Class forName(String name)
: 返回指定类名 name 的 Class 对象Object newInstance()
: 调用缺省构造函数,返回该Class对象的一个实例getName()
: 返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称 ClassgetSuperClass()
: 返回当前Class对象的父类的Class对象Class [] getInterfaces()
: 获取当前Class对象的接口ClassLoader getClassLoader()
: 返回该类的类加载器Class getSuperclass()
: 返回表示此Class所表示的实体的超类的ClassConstructor[] getConstructors()
: 返回一个包含某些Constructor对象的数组Field[] getDeclaredFields()
: 返回Field对象的一个数组Method getMethod(String name,Class … paramTypes)
: 返回一个Method对象,此对象的形参类型为paramType
三、获取Class类的实例
方式一: 通过类的class属性获取
Class<Person> clazz = Person.class;
方式二: 调用getClass()方法
getClass() 方法在Object类中声明,所有的类都默认继承了Object中的getClass()方法。
// 需要先获取一个类的实例
Person p = new Person();
Class<? extends Person> clazz = p.getClass();
Object o = new Object();
Class<?> clazz2 = o.getClass();
方式三: 调用Class的静态方法 forName(String classPath)
// 该方法的参数是类的全类名,同时还会抛出异常
public void test() throws ClassNotFoundException {
String className = "java.lang.String";
Class<?> clazz = Class.forName(className);
}
方式四: 使用类加载器 ClassLoader
public class ReflectionTest {
@Test
public void test() throws ClassNotFoundException {
// 先获取类加载器
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
// 通过类加载器获取
Class<?> clazz = classLoader.loadClass("java.lang.String");
}
}
四、类的加载与ClassLoader的理解
类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
加载:
- 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时 数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
链接:
- 将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题。
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化:
- 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信 息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的clinit>()方法在多线程环境中被正确加锁和同步。
类初始化的时间
类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化
- 当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常 量池中了)
ClassLoader类加载器
类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
五、Java反射包下的类
通过反射可获取运行时类的完整结构,这些结构也是java.lang.reflect包下也有对应的类。
Field、Method、Constructor、Interface、Annotation、Superclass
Field
用于反编译一个类的属性。
常用方法:
public String getName()
: 返回属性名public int getModifiers()
: 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号(一般配合Modifier类的toString(int x)方法使用)public Class<?> getType()
: 以Class类型,返回属性类型(一般配合Class类的getSimpleName()方法使用)public void set(Object obj, Object value)
: 设置属性值public Object get(Object obj)
: 读取属性值
Method
用于反编译一个类的方法。
常用方法:
Object invoke(Object obj, Object... args)
: 使用对象obj来调用此Method对象所表示的成员方法,实参传递argsint getModifiers()
: 获取方法的访问修饰符Class<?> getReturnType()
: 获取方法的返回值类型String getName()
: 获取方法的名称Class<?>[] getParameterTypes()
: 获取方法所有参数的类型Class<?>[] getExceptionTypes()
: 获取方法的异常信息
Constructor
用于反编译一个类的方法。
常用方法:
- Class getDeclaringClass(): 得到一个 Class 类的对象
- Class[] getParameterTypes(): 获得 Constructor 类对象表示的构造方法中的参数的类型
- String getName(): 获得 Method 类对象所表示的构造方法的名字
- T newInstance(): 通过调用当前 Constructor 类对象所表示的类的构造方法创建一个新对象
六、反射与动态代理
什么是动态代理?
动态代理简单来说就是在程序运行过程中通过反射机制创建代理类的对象,从而实现"动态"的代理模式。
而Spring的一大重要特性AOP就是使用了动态代理。
动态代理与AOP(Aspect Orient Programming)
- 使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有 太大的意义。通常都是为指定的目标对象生成动态代理。
- 这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理 包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。
代码演示:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理测试
*/
public class ProxyTest {
public static void main(String[] args) {
// 创建被代理类的对象
UserServiceImpl userService = new UserServiceImpl();
// 获取代理类对象
UserService proxyInstance = (UserService) ProxyFactory.getProxyInstance(userService);
// 执行代理后的方法
proxyInstance.login("root", "123456");
}
}
class ProxyFactory {
// 获取代理类对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
handler);
}
}
class MyInvocationHandler implements InvocationHandler {
// 被代理类对象
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------前置操作-------");
Object returnVal = method.invoke(obj, args);
System.out.println("-------后置操作-------");
return returnVal;
}
}
interface UserService {
void login(String username, String password);
}
class UserServiceImpl implements UserService {
@Override
public void login(String username, String password) {
System.out.println("UserServiceImpl执行login方法...");
}
}
输出:
-------前置操作-------
UserServiceImpl执行login方法...
-------后置操作-------