反射(reflect)
1. Class对象
1.1 什么是Class对象
当JVM加载某个class文件的时候,会自动创建一个唯一的Class对象(注意:由同一个类加载器加载的class文件),这个Class对象包含了整个class类的信息(例如:类的名称、访问修饰符、字段、字段描述、方法等等一切信息)。当使用new关键字创建这个类的实例时,jvm都会使用这个Class对象来创建类的实例。
1.1 获取Class对象方式
方式 |
说明 |
Test.class |
通过类名.class直接获取 |
t1.getClass() |
通过对象的getClass()方法获取 |
Class.forName("...") |
使用Class类的forName静态方法获取,参数给的是完整类名(包名+类名) |
2. 反射
2.1 什么是反射
所谓的反射,是在程序运行时动态获得任何一个Class对象的成员信息(类信息、字段、构造方法、成员方法等),并且能在运行时依据某个Class对象创建当前类的实例。
2.2 Class常用API
方法 |
说明 |
newInstance() |
创建类实例 |
getName() |
获取类的完整类名 |
getSimpleName() |
获取类的简单类名 |
getPackage() |
获取类对应的包信息 |
getSuperclass() |
获取父类的Class对象 |
getInterfaces() |
获取实现的所有接口 |
getClassLoader() |
获取当前类的类加载器 |
getConstructor(..) |
获取公共且明确参数类型的构造方法 |
getDeclaredConstructor(..) |
获取受保护且明确参数类型的构造方法 |
getConstructors() |
获取所有公共的构造方法 |
getDeclaredConstructors() |
获取所有(包括受保护)的构造方法 |
getField(..) |
根据字段名称获取某一个公共的字段 |
getDeclaredField(..) |
根据字段名称获取当前类某一个的字段(包括受保护的) |
getFields() |
获取所有公共的字段,包括继承自父类的公共字段 |
getDeclaredFields |
获取当前类所有字段,包括受保护的(不包括继承父类的字段) |
getMethod(..) |
根据方法名以及参数类型获取一个公共的方法 |
getDeclaredMethod(..) |
根据方法名以及参数类型获取当前类的一个受保护的方法 |
getMethods() |
获取所有公共的方法(包括继承自父类的公共方法) |
getDeclaredMethods() |
获取当前类的所有方法,包括受保护的(不包括继承父类的方法) |
isAnnotation() |
此Class是否是一个注解 |
isAnnotationPresent(..) |
当前Class时是否定义了某个注解 |
getAnnotation(..) |
获取当前类上定义的某个注解 |
getAnnotations() |
获取当前类上定义的所有注解 |
isEnum() |
此Class是否是一个枚举 |
isArray() |
此Class是否是一个数组类型 |
isInterface() |
此Class是否是一个接口 |
... |
其他API请参照官方文档 |
public class TestClass {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("edu.nf.reflect.Users");
//通过Class对象来创建实例,当前类必须提供一个公开并且无参的构造方法
Users user = (Users) clazz.newInstance();
//获取类的完整类名
System.out.println(clazz.getName());
//获取类的简单类名
System.out.println(clazz.getSimpleName());
//获取类所在的包名
System.out.println(clazz.getPackage().getName());
//获取当前类的父类的Class
System.out.println(clazz.getSuperclass());
//获取当前类实现的所有接口
System.out.println(clazz.getInterfaces());
//获取加载这个类的类加载器
System.out.println(clazz.getClassLoader());
//获取公共并且参数类型为String的构造方法
clazz.getConstructor(String.class);
//获取私有并且参数为int类型的构造方法
clazz.getDeclaredConstructor(Integer.TYPE);
//获取所有公共的构造方法
Constructor[] consArray1 = clazz.getConstructors();
//获取所有构造方法,包括公共和是有的
Constructor[] consArray2 = clazz.getDeclaredConstructors();
//获取某一个公共的字段,参数指定字段的名字
Field f1 = clazz.getField("address");
System.out.println(f1.getName());
//获取某一个受保护的字段
Field f2 = clazz.getDeclaredField("tel");
System.out.println(f2.getName());
System.out.println("-----------------------");
//获取所有公共的字段,包括继承自父类的公共字段
Field[] fieldArray1 = clazz.getFields();
for (Field field : fieldArray1) {
System.out.println(field.getName());
}
System.out.println("-----------------------");
//获取所有字段,包括受保护的(不包括继承父类的字段)
Field[] fieldArray2 = clazz.getDeclaredFields();
for (Field field : fieldArray2) {
System.out.println(field.getName());
}
System.out.println("-----------------------");
//获取某一个公共的方法(第一个参数指定方法名,第二个参数指定方法参数的类型,这是一个可变参数,有多少个参数就要指定多少个类型)
Method m1 = clazz.getMethod("say", String.class);
System.out.println(m1.getName());
//获取某一个受保护的方法
Method m2 = clazz.getDeclaredMethod("call");
System.out.println(m2.getName());
System.out.println("-----------------------");
//获取所有公共的方法(包括继承自父类的公共方法)
Method[] methodArray1 = clazz.getMethods();
for (Method method : methodArray1) {
System.out.println(method.getName());
}
System.out.println("-----------------------");
//获取本类的所有方法,包括受保护的(不包括父类的方法)
Method[] methodArray2 = clazz.getDeclaredMethods();
for (Method method : methodArray2) {
System.out.println(method.getName());
}
}
}
2.3 Class对象中的成员
成员 |
说明 |
Constructor |
用于描述类的构造方法 |
Field |
用于描述类的字段 |
Method |
用于描述类的方法 |
Parameter |
用于描述方法或构造方法的参数信息 |
2.3.1 Constructor常用API
API |
说明 |
getName() |
获取当前构造方法的名称 |
getParameterCount() |
获取构造方法的参数个数 |
getDeclaringClass() |
获取声明此构造方法的Class类 |
setAccessible(..) |
设置访问开关 |
newInstance(..) |
使用当前的Constructor构建实例 |
... |
其他API请参照官方文档 |
public class TestCons {
public static void main(String[] args) throws Exception{
//加载Users的class信息,并构建Class对象
Class<?> clazz = Class.forName("edu.nf.reflect.Users");
//通过class对象访问构造方法信息,
// 根据构造方法的参数类型获取某一个公共的构造方法
Constructor cons1 = clazz.getConstructor(int.class);
// 访问受保护的构造方法
Constructor cons2 = clazz.getDeclaredConstructor(int.class);
//构造方法的名称
System.out.println(cons1.getName());
//获取构造方法的参数个数
System.out.println(cons1.getParameterCount());
//获取定义这个构造方法的Class类
System.out.println(cons1.getDeclaringClass());
//通过构造方法来创建类的实例
//由于构这个造方法是私有的,因此必须强制打开访问开关
cons1.setAccessible(true);
//然后通过newInstance方法来创建类的实例,并传入构造方法所需的参数
Users user = (Users)cons1.newInstance(21);
//获取所有的构造方法(包括私有和公有的)
Constructor[] cons = clazz.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println("参数个数: "+con.getParameterCount());
}
}
}
2.3.2 Field常用API
API |
说明 |
getType() |
获取当前字段类型 |
getName() |
获取当前字段名称 |
get(..) |
获取当前字段的值 |
set(..) |
给当前字段赋值 |
setAccessible(..) |
设置访问开关 |
... |
其他API请参照官方文档 |
public class TestField {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("edu.nf.reflect.Users");
//创建类的实例
Object instance = clazz.newInstance();
//获取一个受保护的字段
Field f = clazz.getDeclaredField("tel");
//打开访问开关
f.setAccessible(true);
//给字段赋值(第一个参数指定当前类的实例,第二个参数是具体的值)
f.set(instance, "123456");
//取值(get方法的参数也是指定当前类的实例)
System.out.println(f.get(instance));
//获取字段的类型
System.out.println(f.getType());
//获取字段的名称
System.out.println(f.getName());
}
}
2.3.3 Method常用API
API |
说明 |
getName() |
获取当前方法名称 |
getReturnType() |
获取当前方法的返回值 |
getParameterCount() |
获取当前方法参数的个数 |
getParameters() |
获取当前方法的参数对象 |
getParameterTypes() |
获取当前方法的所有参数类型 |
setAccessible(..) |
设置访问开关 |
invoke(..) |
回调当前的method |
... |
其他API请参照官方文档 |
public class TestMethod {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("edu.nf.reflect.Users");
//构建当前类的实例
Object instance = clazz.newInstance();
//获取受保护的call方法
Method method = clazz.getDeclaredMethod("call", String.class);
//获取方法的名称
System.out.println(method.getName());
//获取方法的返回值类型
System.out.println(method.getReturnType());
//获取方法参数的个数
System.out.println(method.getParameterCount());
//获取方法的所有参数对象
System.out.println(method.getParameters());
//获取方法中所有参数的类型
Class<?>[] paramsType = method.getParameterTypes();
for (Class<?> aClass : paramsType) {
System.out.println(aClass);
}
//调用当前方法(第一个参数是当前类的实例,第二个参数开始是方法所需的参数)
//invoke方法返回的是当前调用的方法的返回值
//这个方法是受保护的,因此需要先打开访问开关
method.setAccessible(true);
Object returnValue = method.invoke(instance, "12345678");
System.out.println(returnValue);
}
}
2.3.4 Parameter常用API
API |
说明 |
getType() |
获取参数类型 |
getName() |
获取参数的名称(说明:JDK8默认编译的时候是不会将参数名信息保存在字节码中,如果想要获取参数名,那么在编译时就需要加上编译参数 ,如:javac -parameters Users.java) |
... |
其他API请参照官方文档 |
public class TestParameter {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("edu.nf.reflect.Users");
//先获取指定的受保护的方法
Method callMethod = clazz.getDeclaredMethod("call", String.class);
//获取该方法的参数信息
Parameter[] params = callMethod.getParameters();
for (Parameter param : params) {
//获取参数的类型
System.out.println(param.getType());
//获取参数的名称(在JDK8之前是没有办法获取参数名的,必须通过第三方字节码工具来获取)
//JDK8默认编译的时候是不会讲参数名信息保存在字节码中
//如果想要获取参数名,那么在编译时就需要加上编译参数
//如:javac -parameters Users.java
System.out.println(param.getName());
}
}
}