JAVA 反射

反射学习

反射是Java的动态性之一

反射可以在java运行过程中加载一些只知道相关名字的类

反射机制介绍

特点

同一个类创建的不同的对象共享同一个class对象

常见作用

  1. 动态的加载类, 动态的获取类的信息(属性, 方法, 构造器)
  2. 动态构造对象
  3. 动态调用类和对象的任意方法, 构造器
  4. 动态调用和处理属性
  5. 获取泛型信息
  6. 处理注解

反射的使用

获取Class对象

  1. 通过字节码文件
  2. 对象的getClass()方法
  3. 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();
}

取得类的相关字段, 方法

获取类的名字

  1. String getName() 获取包名+类名
  2. String getSimpleName() 获得类的名字

获得类的属性

  1. Field getField(String fieldName) 得到公共的属性对象
  2. Field getDeclareField(String fieldName) 得到指定的属性对象(包括私有方法)
  3. Field [] getDeclaredFields() 得到所有属性对象

获得类的方法

  1. Method[] getDeclaredMethods() 得到本类中得所有方法, 包括私有得方法, 但是没有父类得方法
  2. Method[] getMethods() 得到父类及本类中的公共的方法对象
  3. Method getDeclaredMethod(String methodName, Class… type) 得到指定名称的本类中的方法
  4. Method getMethod(String methodName, Class type) 得到本类或父类中的公共的方法对象

获得构造方法

  1. Constructor[] getDeclaredConstructors() 得到公共的构造方法的对象
  2. Constructor[] getConstructors() 得到公共的构造方法对象
  3. Constructor getDeclaredConstructor(Class… type) 得到指定参数的构造方法对象

获取父类的class对象

  1. 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对象中的方法

  1. 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中的方法(首先需要取得构造器对象)

  1. Object newInstance(initargs…) 也是新建一个对象, 可以根据此构造方法传递相关的参数

    // 通过构造方法建立对象
    Constructor<?> constructor = userClz.getConstructor(String.class, int.class);
    User user1 = (User) constructor.newInstance("jssd", 21);
    System.out.println(user1.getName());
    

Field中的方法

  1. int getModifiers() 取得属性的操作权限修饰符, 返回int型数据. 1为public, 2为private 3为protect

  2. Class<?> getType(); 取得属性的类型值, 返回class对象

  3. String getName(); 取得属性的名字

    Field declaredField = userClz.getDeclaredField("name"); // 此时取得一个私有属性
    // 输出
    System.out.println("修饰符: " + declaredField.getModifiers() + ", 类型: " + declaredField.getType() + ", 名字: " + declaredField.getName());
    
  4. setAccessible(); 可以使此Field从private变成public. 从不可访问变成可访问

  5. set(targetObject, value); 给相应对象的此属性赋值

  6. get(targetObject); 取得相应对象的此属性的值, 相当于调用相应对象的get方法

    // 操作对象的属性
    // 直接操作私有属性会报异常, 不能操作
    // declaredField.set(user, "lxh");
    // 更改属性的操作权限, true则可以操作私有属性
    declaredField.setAccessible(true); // 不安全
    // 设置属性
    declaredField.set(user, "lxh");
    // 取得属性值
    System.out.println(declaredField.get(user));
    

Method中的方法

  1. int getModifiers() 取得权限修饰符

  2. Class getReturnType() 取得返回值类型

  3. String getName() 取得方法名

  4. Parameter[] getParameters() 取得参数值数组

  5. Class[] getParameterTypes() 取得参数类型数组

  6. 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之后引入了新的数据类型

  1. ParameterizedType: 表 示 一 种 参 数 化 的 类 型 ,比 如 Collection,可以获取 String 信息
  2. GenericArrayType:泛型数组类型
  3. TypeVariable:各种类型变量的公共父接口
  4. 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();
		}
	}
}

就是这样了, 假如你忘了怎么使用这几个数据类型, 看看代码, 你大概能想起来. 假如你想了解一下, 学一学, 那你看我的代码是学不会的. 照着代码敲一敲把.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值