反射学习
反射是Java的动态性之一
反射可以在java运行过程中加载一些只知道相关名字的类
反射机制介绍
特点
同一个类创建的不同的对象共享同一个class对象
常见作用
- 动态的加载类, 动态的获取类的信息(属性, 方法, 构造器)
- 动态构造对象
- 动态调用类和对象的任意方法, 构造器
- 动态调用和处理属性
- 获取泛型信息
- 处理注解
反射的使用
获取Class对象
- 通过字节码文件
- 对象的getClass()方法
- Class类的静态方法forName();
User u = new User();
// 1. 通过字节码文件创建
Class clz1 = User.class;
// 2. 通过对象的getClass()方法创建
Class clz2 = u.getClass();
// 3. 通过类名创建(常用)
try {
Class clz3 = Class.forName("pers.jssd.entity.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
取得类的相关字段, 方法
获取类的名字
- String getName() 获取包名+类名
- String getSimpleName() 获得类的名字
获得类的属性
- Field getField(String fieldName) 得到公共的属性对象
- Field getDeclareField(String fieldName) 得到指定的属性对象(包括私有方法)
- Field [] getDeclaredFields() 得到所有属性对象
获得类的方法
- Method[] getDeclaredMethods() 得到本类中得所有方法, 包括私有得方法, 但是没有父类得方法
- Method[] getMethods() 得到父类及本类中的公共的方法对象
- Method getDeclaredMethod(String methodName, Class… type) 得到指定名称的本类中的方法
- Method getMethod(String methodName, Class type) 得到本类或父类中的公共的方法对象
获得构造方法
- Constructor[] getDeclaredConstructors() 得到公共的构造方法的对象
- Constructor[] getConstructors() 得到公共的构造方法对象
- Constructor getDeclaredConstructor(Class… type) 得到指定参数的构造方法对象
获取父类的class对象
- Class getSuperclass(); 得到此类的父类class对象
简单举例使用情况
/**
* 取得字段中的值
* @author jssd
* Create 2019-07-12 8:30
*/
public class Test2 {
public static void main(String[] args) {
Class<?> clz;
try {
// 利用类名获取Class对象
clz = Class.forName("pers.jssd.entity.User");
System.out.println("=============输出一个类==============");
// 输出class对象
System.out.println("clz = " + clz);
// 输出包名+类名
System.out.println(clz.getName());
// 输出简单的类名
System.out.println(clz.getSimpleName());
System.out.println("==============取得一个类得属性================");
// 获得类的公共属性
Field[] fields = clz.getFields();
System.out.println("getFields 取得的属性");
for (Field field : fields) {
System.out.println(field.getName());
}
// 获得类的指定属性, 包括私有属性
Field[] fields1 = clz.getDeclaredFields();
System.out.println("getDeclaredFields() 取得的属性");
for (Field field : fields1) {
System.out.println(field.getName());
}
// 按照指定的属性名获取属性
// 只能获取公共属性
clz.getField("sax");
// 可获取私有属性
clz.getDeclaredField("name");
System.out.println("===========取得一个class对象得方法=============");
// 获取类的方法
// 获取本类和父类得所有公共方法
Method[] methods = clz.getMethods();
System.out.println("getMethods() 方法取得得方法: ");
for (Method method : methods) {
System.out.println(method.getName());
}
//获取本类中得所有方法, 包括私有得方法, 没有父类得方法
Method[] declaredMethods = clz.getDeclaredMethods();
System.out.println("getDeclaredMethods 取得得方法");
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
// 获取指定名称得方法, 注意, 可变参数可以不进行传参
clz.getMethod("getName");
clz.getMethod("setName", String.class);
// 这个方法可以取得class对象中的私有方法
clz.getDeclaredMethod("setSax", int.class);
System.out.println("========获得构造方法=======");
// 获得构造方法
// 获得公共构造方法
Constructor<?>[] constructors = clz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
// 可获得私有的构造方法
Constructor<?>[] declaredConstructors = clz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
//获得指定参数的构造方法
clz.getConstructor(String.class, int.class);
clz.getDeclaredConstructor(String.class);
System.out.println("=======获取父类的class对象========");
Class supClz = clz.getSuperclass();
System.out.println(supClz.getSimpleName());
} catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
通过class对象动态操作对象的属性方法
Class对象中的方法
-
Object newInstance(); 通过此class对象创建一个对应的类的对象, 返回Object类型,需要强转类型. 此方法只适用于对应类具有无参构造方法
// 获取User类的class对象 userClz = Class.forName("pers.jssd.entity.User"); System.out.println("=====通过class对象动态创建class对象 对应类 的对象====="); // 通过class类新建User对象, 仅限于存在无参构造方法 User user = (User) userClz.newInstance();
Constructor中的方法(首先需要取得构造器对象)
-
Object newInstance(initargs…) 也是新建一个对象, 可以根据此构造方法传递相关的参数
// 通过构造方法建立对象 Constructor<?> constructor = userClz.getConstructor(String.class, int.class); User user1 = (User) constructor.newInstance("jssd", 21); System.out.println(user1.getName());
Field中的方法
-
int getModifiers() 取得属性的操作权限修饰符, 返回int型数据. 1为public, 2为private 3为protect
-
Class<?> getType(); 取得属性的类型值, 返回class对象
-
String getName(); 取得属性的名字
Field declaredField = userClz.getDeclaredField("name"); // 此时取得一个私有属性 // 输出 System.out.println("修饰符: " + declaredField.getModifiers() + ", 类型: " + declaredField.getType() + ", 名字: " + declaredField.getName());
-
setAccessible(); 可以使此Field从private变成public. 从不可访问变成可访问
-
set(targetObject, value); 给相应对象的此属性赋值
-
get(targetObject); 取得相应对象的此属性的值, 相当于调用相应对象的get方法
// 操作对象的属性 // 直接操作私有属性会报异常, 不能操作 // declaredField.set(user, "lxh"); // 更改属性的操作权限, true则可以操作私有属性 declaredField.setAccessible(true); // 不安全 // 设置属性 declaredField.set(user, "lxh"); // 取得属性值 System.out.println(declaredField.get(user));
Method中的方法
-
int getModifiers() 取得权限修饰符
-
Class getReturnType() 取得返回值类型
-
String getName() 取得方法名
-
Parameter[] getParameters() 取得参数值数组
-
Class[] getParameterTypes() 取得参数类型数组
-
invoke(targetObject, …args) 调用指定对象中的此方法
// 动态操作方法 Method setName = userClz.getDeclaredMethod("setName", String.class); System.out.println("修饰符: " + setName.getModifiers() + ", 返回值类型: " + setName.getReturnType() + ", 名字: " + setName.getName()); // 取得方法参数列表 System.out.println("参数: "); Class<?>[] parameterTypes = setName.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType.getSimpleName()); } // 调用此方法 setName.invoke(user, "jssd"); // 输出查看是否修改 System.out.println(user.getName());
提高反射的执行小路
通过 setAccessible 提高性能
setAccessible 启用和禁用访问安全检查的开关, 值为true 则指示反射的对象在使用时应该取消 Java 语言访问检查,值为 false 则指示反射的对象不实施 Java 语言访问检查, 并不是为 true 就能访问为 false 就不能访问
测试
package pers.jssd.reflect;
import pers.jssd.entity.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author jssd
* Create 2019-07-12 11:17
*/
public class Test4 {
private void test01() {
User user = new User();
long startTime = System.currentTimeMillis();
for (long i = 0; i < 10000000000L; i++) {
user.hashCode();
}
long endTime = System.currentTimeMillis();
System.out.println("普通调用执行时间: " + (endTime - startTime) + "ms");
}
private void test02() {
User user = new User();
Class<?> clz = user.getClass();
Method hashCode = null;
try {
hashCode = clz.getMethod("hashCode");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
long startTime = System.currentTimeMillis();
for (long i = 0; i < 1000000000L; i++) {
try {
if (hashCode != null) {
hashCode.invoke(user);
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
long endTime = System.currentTimeMillis();
System.out.println("反射调用方法执行时间: " + (endTime - startTime) + "ms");
}
private void test03() {
User user = new User();
Class<?> clz = user.getClass();
Method hashCode = null;
try {
hashCode = clz.getMethod("hashCode");
hashCode.setAccessible(true); // 关闭安全检查
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
long startTime = System.currentTimeMillis();
for (long i = 0; i < 1000000000L; i++) {
try {
if (hashCode != null) {
hashCode.invoke(user);
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
long endTime = System.currentTimeMillis();
System.out.println("反射调用方法执行时间(关闭安全检查): " + (endTime - startTime) + "ms");
}
public static void main (String[]args){
Test4 test4 = new Test4();
test4.test01();
test4.test02();
test4.test03();
}
}
运行结果如下:
普通调用执行时间: 12087ms
反射调用方法执行时间: 3178ms
反射调用方法执行时间(关闭安全检查): 2346ms
反射操作泛型
Java中使用泛型擦除机制, 泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦, 但是一旦编译完成, 所有与泛型有关的类型全部擦除. 所以我们使用反射直接读取泛型是读取不到的. 所以1.5之后引入了新的数据类型
- ParameterizedType: 表 示 一 种 参 数 化 的 类 型 ,比 如 Collection,可以获取 String 信息
- GenericArrayType:泛型数组类型
- TypeVariable:各种类型变量的公共父接口
- WildcardType:代表一种通配符类型表达式,
简单说明并不能体现什么, 请看下面代码:
/**
* java 反射操作泛型测试类
*
* @author jssd
* Create 2019-07-12 21:26
*/
public class Test5 {
private List<String> test01(String str, List<User> list, Map<String, User> map) {
System.out.println("Test5.test01");
return null;
}
private void test02(List<User>[] lists) {
System.out.println("Test5.test02");
}
private void test03(List<? super User> list) {
System.out.println("Test5.test03");
}
public static void main(String[] args) {
Class<?> clz = Test5.class;
Method test01;
Method test02;
try {
test01 = clz.getDeclaredMethod("test01", String.class, List.class, Map.class);
test02 = clz.getDeclaredMethod("test02", List[].class);
// 取得所有的参数类型
Type[] genericParameterTypes = test01.getGenericParameterTypes();
// 遍历所有参数类型
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType.getTypeName());
// 假如这个类型含有泛型类型
if (genericParameterType instanceof ParameterizedType) {
System.out.println("ParameterizedType : " + genericParameterType.getTypeName());
// 取得这个泛型类型中的泛型参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("actualTypeArgument = " + actualTypeArgument);
}
}
}
System.out.println("====================test02===========================");
Type[] genericParameterTypes1 = test02.getGenericParameterTypes();
for (Type type : genericParameterTypes1) {
System.out.println("type = " + type);
// 查看是不是泛型数组类型
if (type instanceof GenericArrayType) {
Type genericComponentType = ((GenericArrayType) type).getGenericComponentType();
// 输出泛型数组类型
System.out.println("genericComponentType = " + genericComponentType);
// 输出泛型数组类型中的泛型类型
Type[] actualTypeArguments = ((ParameterizedType) genericComponentType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("actualTypeArgument = " + actualTypeArgument);
}
}
}
System.out.println("==========test03================");
Method test03 = clz.getDeclaredMethod("test03", List.class);
Type[] genericParameterTypes2 = test03.getGenericParameterTypes();
for (Type type : genericParameterTypes2) {
System.out.println("type = " + type);
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("actualTypeArgument = " + actualTypeArgument);
if (actualTypeArgument instanceof WildcardType) {
Type[] lowerBounds = ((WildcardType) actualTypeArgument).getLowerBounds();
for (Type lowerBound : lowerBounds) {
System.out.println("lowerBound = " + lowerBound);
}
Type[] upperBounds = ((WildcardType) actualTypeArgument).getUpperBounds();
for (Type upperBound : upperBounds) {
System.out.println("upperBound = " + upperBound);
}
}
}
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
就是这样了, 假如你忘了怎么使用这几个数据类型, 看看代码, 你大概能想起来. 假如你想了解一下, 学一学, 那你看我的代码是学不会的. 照着代码敲一敲把.