JavaSE基础之反射

来源:互联网 发布:淘宝中国质造加入条件 编辑:程序博客网 时间:2024/06/04 18:42
->理解 Class 类
->理解 Java 的类加载机制
->学会使用 ClassLoader 进行类加载
->理解反射的机制
->掌握 Constructor、Method、Field 类的用法

->理解并掌握动态代理 

反射概述

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
  • 生成动态代理

Class 类

对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
Class 对象只能由系统建立对象
一个类在 JVM 中只会有一个Class实例 
每个类的实例都会记得自己是由哪个 Class 实例所生成 

获取 Class 对象的方式

/** * 关于 Class:  * 1. Class 是一个类 * 2. 对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、 某个类到底实现了哪些接口。 * 3. 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。  * 4. Class对象只能由系统建立对象  * 5. 一个类在 JVM 中只会有一个Class实例 *  * @throws ClassNotFoundException */@Testpublic void testClass() throws ClassNotFoundException {Class clazz = null;// *1. 得到 Class 对象// 1.1 直接通过 类名.class 的方式得到clazz = Person.class;// 1.2 通过对象调用 getClass() 方法来获取Object obj = new Person();clazz = obj.getClass();// 1.3 通过全类名的方式获取. 用的较多。String className = "com.zto.reflection.entity.Person";clazz = Class.forName(className);// Field[] fields = clazz.getDeclaredFields();System.out.println();}

获取Class对象的一个实例

/** * *Class 类的 newInstance() 方法. *  * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */@Testpublic void testNewInstance() throws ClassNotFoundException,InstantiationException, IllegalAccessException {String className = "com.zto.reflection.entity.Person";Class clazz = Class.forName(className);// 利用 Class 对象的 newInstance() 方法来创建类的一个对象.// 实际调用的是类的那个 无参数的 构造器!// 一般地, 一个类若声明了带参数的构造器, 也要声明一个无参数的构造器.Object obj = clazz.newInstance();System.out.println(obj);}


ClassLoader类加载器

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。
JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:



//表示系统类装载器实例化自类sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader@19821f


//表示系统类装载器的parent实例化自类sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$ExtClassLoader@addbf1


//表示系统类装载器parent的parent为bootstrap,无法直接获取
null


@Testpublic void testClassLoader() throws ClassNotFoundException, FileNotFoundException {// 1. 获取一个系统的类加载器ClassLoader classLoader = ClassLoader.getSystemClassLoader();System.out.println(classLoader);// 2. 获取系统类加载器的父类加载器.扩展类加载器classLoader = classLoader.getParent();System.out.println(classLoader);// 3. 获取扩展类加载器的父类加载器.引导类加载器classLoader = classLoader.getParent();System.out.println(classLoader);// 4. 测试当前类由哪个类加载器进行加载:classLoader = Class.forName("com.zto.reflection.ReflectionTest").getClassLoader();System.out.println(classLoader);// 5. 测试 JDK 提供的 Object 类由哪个类加载器负责加载classLoader = Class.forName("java.lang.Object").getClassLoader();System.out.println(classLoader);// 6*. 关于类加载器的一个主要方法.// 通过系统类加载器获取classpath下的文件对应的输入流// 调用 getResourceAsStream 获取类路径下的文件对应的输入流.InputStream in = null; in = this.getClass().getClassLoader().getResourceAsStream("test.properties");System.err.println(in);// new FileInputStream("test.properties");}

Method

类的方法: Method

/** * Class 是对一个类的描述 * 类的属性: Field * 类的方法: Method * 类的构造器: Constrctor *  * Method: 对应类中的方法. * 1. 获取 Method:  * 1.1  获取类的方法的数组: clazz.getDeclaredMethods(); * 1.2 获取类的指定的方法: getDeclaredMethod(String name,Class<?>... parameterTypes)    *    name: 方法名 *    parameterTypes: 方法的参数类型(使用Class来来描述)的列表         *     *    Method method = clazz.getDeclaredMethod("setName", String.class);   *     *    method = clazz.getDeclaredMethod("setName", String.class, Integer.class);             *  * 1.3 通过 method 对象执行方法: * public Object invoke(Object obj, Object... args) *  * obj: 执行哪个对象的方法? * args: 执行方法时需要传入的参数. * * Object obj = clazz.newInstance(); * //method 对应的原方法为:  * public void setName(String name, Integer age) * method.invoke(obj, "xyc", 24); *  */@Testpublic void testMethod() throws Exception{Class clazz = Class.forName("com.zto.reflection.entity.Person");//1. 得到 clazz 对应的类中有哪些方法, 不能获取 private 方法. Method [] methods = clazz.getMethods();for(Method method: methods){System.out.println("^" + method.getName()); }//2. 获取所有的方法, 包括 private 方法, 且只获取当前类声明的方法. Method [] methods2 = clazz.getDeclaredMethods();for(Method method: methods2){System.out.println("~" + method.getName()); }//3. 获取指定的方法. Method method = clazz.getDeclaredMethod("setName", String.class);System.out.println(method); method = clazz.getDeclaredMethod("test");System.out.println(method);method = clazz.getDeclaredMethod("setName", String.class, Integer.class);System.out.println(method);//4. 执行方法!Object obj = clazz.newInstance();method.invoke(obj, "xyc", 24);}

练习

/** *  * @param className: 某个类的全类名 * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.  * @param args: 调用该方法需要传入的参数 * @return: 调用方法后的返回值 */public Object invoke(String className, String methodName, Object ... args){Object obj = null;try {obj = Class.forName(className).newInstance();return invoke(obj, methodName, args);}catch(Exception e) {e.printStackTrace();}return null;}/** *  * @param obj: 方法执行的那个对象.  * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.  * @param args: 调用该方法需要传入的参数 * @return: 调用方法后的返回值 */public Object invoke(Object obj, String methodName, Object ... args){//1. 获取 Method 对象Class [] parameterTypes = new Class[args.length];for(int i = 0; i < args.length; i++){parameterTypes[i] = args[i].getClass();System.out.println(parameterTypes[i]); }try {Method method = obj.getClass().getMethod(methodName, parameterTypes);//2. 执行 Method 方法//3. 返回方法的返回值return method.invoke(obj, args);} catch (Exception e) {e.printStackTrace();}return null;}@Testpublic void testInvoke(){Object obj = new Person();invoke(obj, "setName", "xyc", 24);invoke("com.atguigu.javase.lesson12.Person", "setName", "xyc", 24);Object result = invoke("java.text.SimpleDateFormat", "format", new Date());System.out.println(result); }

获取当前类的父类

/** * 获取当前类的父类: * 直接调动 Class 对象的 getSuperClass() 方法.  * @throws Exception  */@Testpublic void testGetSuperClass() throws Exception{String className = "com.zto.reflection.entity.Student";Class clazz = Class.forName(className);Class superClazz = clazz.getSuperclass();System.out.println(superClazz); }

执行类的私有方法

/** * 执行类的私有方法 * 若通过 Method 的 invoke() 方法调用方法, 而访问权限不足, 则可以先使该方法 * 变为可被访问的:  *  * method.setAccessible(true); *  * @throws Exception */@Testpublic void testInvokePrivateMethod() throws Exception{Object obj = new Student();Class clazz = obj.getClass();Method method = clazz.getDeclaredMethod("method1", Integer.class);System.out.println(method); //若需要通过反射执行私有方法method.setAccessible(true);method.invoke(obj, 10);}

练习:执行给定类的私有方法,如果当前类中没有次方法,从父类中找到执行

/** * @throws Exception  *  */@SuppressWarnings("unchecked")@Testpublic void testClassMethod() throws Exception{//1. 全类名String className = "com.zto.reflection.entity.Student";//2. 方法名: 可能在 1 给的类中, 也可能在父类中. 可能是私有方法, 也可能是公有方法.String methodName = "method3";//3. 执行 2 对应的方法时需要传入的参数列表. Object [] args = {"邢宇超", 24};//根据以下条件, 执行 methodName 对应的方法, 并打印返回值.Class clazz = Class.forName(className);Class [] parameterTypes = getParamerTypes(args);Method method = getMethod(clazz, methodName, parameterTypes);Object result = invokeMethod(clazz.newInstance(), method, args);System.out.println(result); }/** * 由 Object 数组得到其对应的 Class 数组. * @param args * @return */public Class [] getParamerTypes(Object ... args){Class [] parameterTypes = new Class[args.length];for(int i = 0; i < args.length; i++){parameterTypes[i] = args[i].getClass();}return parameterTypes;}/** * 执行 obj 对象的 method 方法, 参数值为 args * @param obj * @param method * @param args * @return * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException */public Object invokeMethod(Object obj, Method method, Object[] args)throws InstantiationException, IllegalAccessException,InvocationTargetException {method.setAccessible(true);Object result = method.invoke(obj, args);return result;}/** * 获取 clazz 的 methodName 方法. 该方法可能是私有方法, 还可能在父类中(私有方法) * @param clazz * @param methodName * @param parameterTypes * @return */public Method getMethod(Class clazz, String methodName, Class ... parameterTypes){for(Class clazz2 = clazz; clazz2 != Object.class; clazz2 = clazz2.getSuperclass()){try {Method method = clazz2.getDeclaredMethod(methodName, parameterTypes);return method;} catch (Exception e) {}}return null;}

Field

/** * Field: 封装了字段的信息.  * 1. 获取字段: * 1.1 Field [] fields = clazz.getDeclaredFields(); * 1.2 Field field2 = clazz.getDeclaredField("age"); *  * 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, "xyc"); *  *  */@Testpublic void testField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{String className = "com.zto.reflection.entity.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 获取指定名字的 FieldField field = clazz.getDeclaredField("name");System.out.println(field.getName()); Person person = new Person("ABC", 12);field.setAccessible(true);//2. 获取指定对象的 Field 的值Object val = field.get(person);System.out.println(val);//3. 设置指定对象的 Field 的值field.set(person, "xyc");System.out.println(person.getName());//4. 若该字段是私有的, 需要调用 setAccessible(true) 方法Field field2 = clazz.getDeclaredField("age");field2.setAccessible(true);System.out.println(field2.get(person)); }

练习:创建 className 对应类的对象, 并为其 fieldName 赋值为 val

/** *  * @Title: testClassField    * @Description: 创建 className 对应类的对象, 并为其 fieldName 赋值为 val * @param: @throws ClassNotFoundException * @param: @throws InstantiationException * @param: @throws IllegalAccessException       * @return: void       * @throws */@Testpublic void testClassField() throws ClassNotFoundException, InstantiationException, IllegalAccessException{String className = "com.zto.reflection.entity.Student";String fieldName = "age"; //可能为私有, 可能在其父类中. Object val = 20;//创建 className 对应类的对象, 并为其 fieldName 赋值为 valObject obj = null;Class clazz = Class.forName(className);Field field = getField(clazz, fieldName);obj = clazz.newInstance();setFieldValue(obj, field, val);Object fieldValue = getFieldValue(obj, field);System.out.println(fieldValue);}public Object getFieldValue(Object obj, Field field) throws IllegalArgumentException, IllegalAccessException{field.setAccessible(true);return field.get(obj);}public void setFieldValue(Object obj, Field field, Object val)throws IllegalAccessException {field.setAccessible(true);field.set(obj, val);}public Field getField(Class clazz, String fieldName) {Field field = null;for(Class clazz2 = clazz; clazz2 != Object.class; clazz2 = clazz2.getSuperclass()){try {field = clazz2.getDeclaredField(fieldName);} catch (Exception e) {}}return field;}

构造器

/** * Constructor: 代表构造器 * @throws ClassNotFoundException  * @throws SecurityException  * @throws NoSuchMethodException  * @throws InvocationTargetException  * @throws IllegalArgumentException  * @throws IllegalAccessException  * @throws InstantiationException  */@Testpublic void testConstructor() throws SecurityException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{String className = "com.zto.reflection.entity.Person";Class<Person> clazz = (Class<Person>) Class.forName(className);//1. 获取 Constructor 对象Constructor<Person> [] constructors = (Constructor<Person>[]) Class.forName(className).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("xyc", 24);}

Annotation 和 反射:

@Retention(RetentionPolicy.RUNTIME)@Target(value={ElementType.METHOD})public @interface AgeValidator {public int min();public int max();}

@AgeValidator(max = 80,min = 20)public void setAge(int age) {this.age = age;}

/** * Annotation 和 反射: * 1. 获取 Annotation *  * getAnnotation(Class<T> annotationClass)  * getDeclaredAnnotations()  *  */@Testpublic void testAnnotation() throws Exception{String className = "com.zto.reflection.entity.Person";Class clazz = Class.forName(className);Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("setAge", int.class);int val = 6;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, 1000);System.out.println(obj);  }


1 0