[JavaSE]反射
来源:互联网 发布:微商授权系统源码 编辑:程序博客网 时间:2024/04/29 16:29
这两天一直看JavaSE中反射部分,当然看的是佟刚老师的视频,总算是入门了,这里将通过代码【源码地址】形式总结反射的基本用法。
在说什么是反射之前,先说什么是Class 类
一、Class 类
- Class 是一个类
- 对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
- 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一 个 Class 对象包含了特定某个类的有关信息。
- Class 对象只能由系统建立对象
- 一个类在 JVM 中只会有一个Class实例
/** * 如何得到 Class 对象 * 1. 通过 类名.class 的方式. * 2. 通过对象调用 getClass() 方法的方式. * 3. 通过全类名的方式. */ @Test public void testClass() throws ClassNotFoundException{ Class clazz = null; //1. 通过 类名.class 的方式. clazz = Person.class; //2. 通过对象调用 getClass() 方法的方式. Person person = new Person(); clazz = person.getClass(); //3. 通过全类名的方式. String className = "henan.huel.java.Person"; clazz = Class.forName(className); System.out.println(clazz); }
/** * 通过Class 对象的 newInstance() 方法来创建类的一个对象,实际调用的是类的那个无参数的构造器. * 一般地, 一个类若声明了带参数的构造器, 也要声明一个无参数的构造器. */ @Test public void testNewInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException{ Class clazz = Class.forName("henan.huel.java.Person"); Object obj = clazz.newInstance(); System.out.println(obj); }
二、ClassLoader
类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
引导类加载器(Bootstap ClassLoader):用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
扩展类加载器(Extension ClassLoader):负责jdk_home/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库 。
系统类加载器(System ClassLoader):负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 。
/** * 关于ClassLoader: * 1. 获取一个系统类加载器 * 2. 获取系统类加载器的父类加载器, 即扩展类加载器 * 3. 获取扩展类加载器的父类加载器, 即引导类加载器 * 4. 测试的当前类由哪个类加载器进行加载 * 5. 测试 JDK 提供的Object 类由哪个类加载器负责加载 * 6. 关于类加载器的一个主要方法, getResourceAsStream() */ @Test public void testClassLoader() throws ClassNotFoundException{ //1. 获取一个系统类加载器 ClassLoader loader = ClassLoader.getSystemClassLoader(); System.out.println(loader); //2. 获取系统类加载器的父类加载器, 即扩展类加载器 loader = loader.getParent(); System.out.println(loader); //3. 获取扩展类加载器的父类加载器, 即引导类加载器 loader = loader.getParent(); System.out.println(loader); //null(无法获取) //4. 测试的当前类由哪个类加载器进行加载 loader = Class.forName("henan.huel.java.ReflectionTest") .getClassLoader(); System.out.println(loader);//系统类加载器 //5. 测试 JDK 提供的Object 类由哪个类加载器负责加载 loader = Class.forName("java.lang.Object") .getClassLoader(); System.out.println(loader);//引导类加载器 //6. 关于类加载器的一个主要方法, getResourceAsStream() //调用getResourceAsStream 获取类路径下的文件对应的输入流. InputStream in = this.getClass().getClassLoader() .getResourceAsStream("test.properties"); System.out.println(in); }
三、反射
反射概述:
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制主要提供了以下功能:
- 在运行时构造任意一个类的对象
- 在运行时获取任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法(属性)
- 生成动态代理
1. 关于Method
如何获取和调用Method:
/** * 关于Method: * 1. 如何获取类的方法的数组 * 2. 如何获取指定的方法, * 2.1 getMethod(String name, Class ... parameterTypes)、 * 2.2 getDeclaredMethod(String name, Class ... parameterTypes): * name: 方法名 * parameterTypes: 参数类型 * Class ... : 长度可变 * 3. 通过method 对象执行方法: * public Object invoke(Object obj, Object... args) * obj: 执行那个对象的方法? * args: 执行方法时需要传入的参数. */ @Test public void testMethod() throws Exception{ Class clazz = Class.forName("henan.huel.java.Person"); //1. 如何获取类的方法的数组: //1.1 得到clazz 对应的类中有哪些方法, 不能获取private 方法. Method [] methods = clazz.getMethods(); for(Method method : methods){ System.out.println(method.getName()); } //1.2 获取所有的方法, 包括private方法. 且只获取当前类声明的方法. methods = clazz.getDeclaredMethods(); for(Method method : methods){ System.out.println(method.getName()); } //2. 如何获取指定的方法. //2.1 同样地, 不能获取private 方法, 这里setName 通过public 修饰 Method method = clazz.getMethod("setName", String.class); System.out.println(method); //2.2 这里setName 通过private 修饰 method = clazz.getDeclaredMethod("setName", String.class, Integer.class); System.out.println(method); //3. 执行方法. Object obj = clazz.newInstance(); //要想执行私有方法, 需要先调用Method 的 setAccessible(true) 方法, 使其变为可访问的. method.setAccessible(true); method.invoke(obj, "huel", 21); }
如何获取父类:
/** * 获取父类 */ @Test public void testGetSuperClass() throws ClassNotFoundException{ Class clazz = Class.forName("henan.huel.java.Student"); Class superClass = clazz.getSuperclass(); System.out.println(superClass); }
工具方法1:无法完成对父类方法的调用(局限)
/** * * @param className: 某个类的全类名 * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法 * @param args: 调用该方法需要传入的参数 * @return * @throws IllegalAccessException * @throws InstantiationException * @throws ClassNotFoundException */ public Object invoke(String className, String methodName, Object ... args){ Object obj = null; try { obj = Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return invoke(obj, methodName, args); } /** * * @param obj: 方法执行的那个对象. * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法 * @param args: 调用该方法需要传入的参数 * @return */ public Object invoke(Object obj, String methodName, Object ... args){ //1. 获取Method 对象 //完成由 args 到 parameterTypes的转换 Class [] parameterTypes = new Class[args.length]; for(int i = 0; i < args.length; i++){ parameterTypes[i] = args[i].getClass(); } try { Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes); //2. 执行Method 方法 //3. 返回方法的返回值 method.setAccessible(true); method.invoke(obj, args); } catch (Exception e) { e.printStackTrace(); } return null; } @Test public void testInvoke(){ Object obj = new Person(); // 执行obj 的setName 方法 invoke(obj, "setName", "huel", 21); invoke("henan.huel.java.Person", "setName", "huel", 21); }
工具方法2:完成对父类方法的调用
/** * 循环向上转型, 获取对象的 DeclaredMethod * @param clazz: Class 对象clazz * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法, 还可能是该方法在父类中定义的(私有)方法. * @param parameterTypes: 方法的参数类型(使用Class来描述)的列表 * @return */ public Method getMethod(Class<?> clazz, String methodName, Class<?> ... parameterTypes){ for(Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) try { Method method = superClass.getDeclaredMethod(methodName, parameterTypes); return method; } catch (Exception e) {} return null; } /** * 完成Object[] 到 Class[] 的转换 * @param args: Object [] * @return: Class [] */ public Class<?> [] getParameterTypes(Object ... args){ Class<?> [] parameterTypes = new Class[args.length]; for(int i = 0; i < args.length; i++){ parameterTypes[i] = args[i].getClass(); } return parameterTypes; } /** * 执行 obj 对象的方法, 参数为args * @param obj: 方法执行的那个对象. * @param method : 获取的 DeclaredMethod * @param args: 调用该方法需要传入的参数 * @return: 方法的返回值 */ private Object invokeMethod(Object obj, Method method, Object[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException { method.setAccessible(true); Object result = method.invoke(obj, args); return result; } /** * * @param obj: 方法执行的那个对象. * @param methodName: 的一个方法的方法名. * 该方法也可能是私有方法, 还可能是该方法在父类中定义的(私有)方法. * @param args: 调用该方法需要传入的参数 * @return */ public Object invoke2(Object obj, String methodName, Object ... args){ //1. 获取Method 对象 Class<?> [] parameterTypes = getParameterTypes(args); try { Method method = getMethod(obj.getClass(), methodName, parameterTypes); return invokeMethod(obj, method, args); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 工具方法2: */ @Test public void testInvoke2(){ Object obj = new Student(); //其中 setName 是Student 父类Person 中的方法. invoke2(obj, "setName", "huel", 10); }
2. 关于Field
如何获取Field:
/** * Field: 封装了字段的信息 * 1. 获取字段: * 1.1 Field [] fields = clazz.getDeclaredFields(); * 1.2 Field field = clazz.getDeclaredField("name"); * * 2. 获取指定对象的指定字段的值. * public Object get(Object obj) * obj: 字段所在的对象. * * Object val = field.get(person); * 注意: 若该字段是私有的, 需要先调用setAccessible(true) * * 3. 设置指定对象的指定字段的值. * public void set(Object obj, Object value) * obj: 字段所在的对象 * value: 要设置的值. * * field.set(person, "atguigu"); */ @Test public void testField() throws Exception{ String className = "henan.huel.java.Person"; Class clazz = Class.forName(className); //1. 获取字段 //1.1 获取Field 的数组 Field [] fields = clazz.getDeclaredFields(); for(Field field : fields){ System.out.println(field.getName()); } //1.2 获取指定名字的Field Field field = clazz.getDeclaredField("name"); System.out.println(field.getName()); Person person = new Person("huel", 21); //2. 获取指定对象的Field 的值 Object val = field.get(person); System.out.println(val); //3. 设置指定对象的Field 的值 field.set(person, "atguigu"); System.out.println(person.getName()); //4. 若该字段是私有的, 需要调用setAccessible(true) Field field2 = clazz.getDeclaredField("age"); field2.setAccessible(true); System.out.println(field2.get(person)); }
工具方法:为对象的某个字段赋值
/** * 循环向上转型, 获取对象的 DeclaredField * @param clazz: Class 对象clazz * @param fieldName: 类的一个字段的字段名. 该字段可能是private 修饰, 还可能是在父类中定义的(私有) 字段. * @return */ public Field getField(Class<?> clazz, String fieldName){ for(Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()){ try { Field field = superClass.getDeclaredField(fieldName); return field; } catch (Exception e) { } } return null; } /** * 设置指定对象的Field 的值 * @param obj: 字段所在的对象 * @param field: 哪一个字段? * @param val: 要设置的值. * @throws IllegalAccessException */ private void setFieldValue(Object obj, Field field, Object val) throws IllegalAccessException { field.setAccessible(true); field.set(obj, val); } /** * 获取指定对象的Field 的值 */ public Object getFieldValue(Object obj, Field field) throws IllegalArgumentException, IllegalAccessException{ field.setAccessible(true); return field.get(obj); } /** * 为对象的某个字段赋值 */ @Test public void testClassField() throws NoSuchFieldException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException{ String className = "henan.huel.java.Student"; String fieldName = "age"; Object val = 21; Object obj = null; Class clazz = Class.forName(className); Field field = getField(clazz, fieldName); obj = clazz.newInstance(); setFieldValue(obj, field, val); Student stu = (Student)obj; System.out.println(stu.getAge()); }
3. Constructor:
如何获取Constructor:
/** * 关于Constructor: * Constructor: 代表构造器 * 1. 获取Constructor 对象 * 2. 调用构造器 的 newInstance() 方法创建对象 */ @Test public void testConstructor() throws Exception{ String className = "henan.huel.java.Person"; Class clazz = Class.forName(className); //1. 获取Constructor 对象 Constructor<Person> [] constructors = clazz.getConstructors(); for(Constructor<Person> constructor : constructors){ System.out.println(constructor); } Constructor<Person> constructor = clazz.getConstructor(String.class, int.class); System.out.println(constructor); //2. 调用构造器 的 newInstance() 方法创建对象 Object obj = constructor.newInstance("huel", 21); }
4. Annotation
Annotation的简单示例:
/** * 关于Annotation: * * @throws Exception */ @Test public void testAnnotation() throws Exception{ String className = "henan.huel.java.Person"; Class<Person> clazz = (Class<Person>) Class.forName(className); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("setAge", int.class); int val = 30; Annotation annotation = method.getAnnotation(AgeValidator.class); if(annotation != null){ if(annotation instanceof AgeValidator) { AgeValidator ageValidator = (AgeValidator) annotation; if(val < ageValidator.min() || val > ageValidator.max()){ throw new RuntimeException("年龄非法"); } } } method.invoke(obj, val); System.out.println(obj); }
5. 泛型与反射
工具方法:获取泛型参数的类型
/** * 通过反射, 获取定义Class 时声明的父类的泛型参数的类型 * 如: public EmployeeDao extends BaseDao<Employee, String> * @param clazz: 子类对应的Class 对象 * @param index: 子类继承父类时传入的泛型的索引. 从0 开始. * @return */ public static Class<?> getSuperClassGenericType(Class<?> clazz, int index){ //获取BaseDao 子类的带泛型参数的父类 Type type = clazz.getGenericSuperclass(); //获取具体的泛型参数 if(type instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) type; Type [] args = parameterizedType.getActualTypeArguments(); if(args != null && index >=0 && index <= args.length-1){ Type arg = args[index]; if(arg instanceof Class){ return (Class<?>) arg; } } } return null; } /** * 获取泛型参数的类型 */ @Test public void tesGetSuperClassGenericType(){ Class clazz = EmployeeDao.class; Class argsClass = getSuperClassGenericType(clazz, 0); System.out.println(argsClass); argsClass = getSuperClassGenericType(clazz, 1); System.out.println(argsClass); }
总结: 这里只是对反射基础知识的总结,如果以后深入了解反射,还会进行更新。
0 0
- javaSE-反射
- [JavaSE]反射
- JavaSE学习笔记--反射
- JavaSE基础:反射机制
- javaSE之反射
- JavaSE 反射Reflection初学
- JavaSE 反射机制
- JavaSE 反射 (进阶)
- JavaSE 反射技术
- JavaSE学习--反射
- JavaSe——反射
- 黑马程序员-javase-反射
- JavaSE增强之反射
- JavaSE-反射机制
- JavaSE反射机制-笔记
- 【javase复习】## 反射 ##
- JavaSE基础之反射
- JavaSE 16 反射
- 【机房重构】E-R图
- 随机森林算法
- dataguru网络课程优惠码:L3P2
- PS学习第一天
- 一些对自己的告诫
- [JavaSE]反射
- iosGCD线程中的通信
- 条款03 尽可能使用const
- Android Support V7 包中的 Palette
- T001-UT001-0003
- Qt学习2
- JavaScript权威指南笔记6_正则表达式
- e-HR推动知识型企业人力资源管理提升
- T001-UT001-0004