JavaEE - 15反射机制
(1)Java Reflection反射机制概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的结构信息。
- 我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象称为:反射。
- 正常方式: 引入需要的"包类"名称 --> 通过new实例化 --> 取得实例化对象
- 反射方式: 实例化对象 --> getClass()方法 --> 得到完整的"包类"名称
(1.1)Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
(1.2)反射相关的主要API
- java.lang.Class: 代表一个类
- java.lang.reflect.Method: 代表类的方法
- java.lang.reflect.Field: 代表类的成员变量
- java.lang.reflect.Constructor: 代表类的构造器
- ......
@Test public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { Class clazz = Person.class; // 通过反射创建Person类的对象 Constructor cons = clazz.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom", 12); System.out.println(obj.toString()); //Person{name='Tom', age=12, sex=null} // 通过反射,调用对象指定的属性、方法 Person p = (Person) obj; Field sex = clazz.getDeclaredField("sex"); sex.set(p,"man"); System.out.println(p.toString()); //Person{name='Tom', age=12, sex=man} // 调用public方法 Method m = clazz.getDeclaredMethod("show"); //name: Tom,age: 10,sex: man m.invoke(p);
// 调用私有的构造器 Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = (Person) cons1.newInstance("Jerry"); System.out.println(p1); // Person{name='Jerry', age=0, sex=null} // 调用私有属性 Field age = clazz.getDeclaredField("age"); age.setAccessible(true); // Class ReflectionTest can not access a member of class Person with modifiers "private" age.set(p,10); System.out.println(p.toString()); // Person{name='Tom', age=10, sex=man} // 调用私有方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class); showNation.setAccessible(true); showNation.invoke(p1, "美国"); //美国 }
(2)获取Class实例
- 哪些类型可以有Class对象
- class: 外部类, 成员(成员内部类、静态内部类),局部内部类, 匿名内部类
- interface:接口/ []:数组/ enum: 枚举/ annotation:注解/ primitive type:基本数据类型/ void
@Test public void test3(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); System.out.println(c10 == c11); // true }
获取Class的实例的方式
@Test public void test4() throws ClassNotFoundException { // 方式一: 调用运行时类的属性: .class Class clazz1 = Person.class; System.out.println(clazz1); // class Person // 方式二: 通过运行时类的对象,调用getClass() Person p = new Person(); Class clazz2 = p.getClass(); System.out.println(clazz2); // class Person // 方式三: 调用Class的静态方法: forName(String classPath) Class clazz3 = Class.forName("Person"); System.out.println(clazz3); // class Person System.out.println(clazz1 == clazz2); // true // 方式四: 使用类的加载器: ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("Person"); System.out.println(clazz4); // class Person }
(3)类的加载与ClassLoader
类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程称为类的加载。
加载到内存中的类,称为运行时类。此运行时类,作为Class的一个实例。
Class的实例对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,可以通过不同的方式来获取此运行时类。
类加载器作用
类加载器ClassLoader
@Test public void test1(){ // 对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2 // 调用系统类加载器的getParent(): 获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); // sun.misc.Launcher$ExtClassLoader@279f2327 // 调用扩展类加载器的getParent():无法获取引导类加载器 // 引导类加载器主要负责加载java的核心类库,无法加载自定义类的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); // null }
(4)创建运行时类的对象
- 通过反射创建对应的运行时类的对象
- newInstance(): 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
- 要想此方法正常的创建运行时类的对象,要求:
- 运行时类必须提供空参的构造器。
- 空参构造器的访问权限满足。通常设置为public。
- 在java Bean中要求提供一个public的空参构造器。原因:
- 便于通过反射,创建运行时类的对象
- 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
@Test public void test1() throws IllegalAccessException, InstantiationException { Class<Person> clazz = Person.class; Person obj = clazz.newInstance(); System.out.println(obj); //Person{name='null', age=0, sex=null} }
public Object getInstance(String classPath) throws ClassNotFoundException { Class clazz = Class.forName(classPath); return clazz; } @Test public void test2(){ int num = new Random().nextInt(3); String classPath = ""; switch (num){ case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.sql.Date"; break; case 2: classPath = "com.bearpx.exer.Person"; break; } try{ Object obj = getInstance(classPath); System.out.println(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
(5)获取运行时类的完整结构
(5.1)属性
Class clazz = Person.class; // getFields: 获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); // public int Person.id } // getDeclaredFields: 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); // private java.lang.String Person.name java.lang.String Person.sex }
(5.2)属性的 权限修饰符/数据类型/变量名
Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ // 权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); // 数据类型 Class type = f.getType(); System.out.print(type.getName() + "\t"); // 变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); }
(5.3)方法
Class clazz = Person.class; // getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = clazz.getMethods(); for(Method m : methods){ System.out.println(m); //public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException } // getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); }
(5.4)方法的 权限修饰符 / 返回值类型 / 方法名(参数类型1 形参名1...) throws XxxException{}
for(Method m : declaredMethods){ //获取方法声明的注解 Annotation[] annos = m.getAnnotations(); for(Annotation a : annos){ System.out.println(a); } // 权限修饰符 System.out.print(Modifier.toString(m.getModifiers()) + "\t"); // 返回值类型 System.out.print(m.getReturnType().getName() + "\t"); // 方法名 System.out.print(m.getName()); System.out.print("("); // 形参列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for (int i = 0; i < parameterTypes.length; i++){ if(i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + " args_" + i + ","); } } System.out.print(")"); // 抛出的异常 Class[] exceptionType = m.getExceptionTypes(); if(exceptionType.length > 0){ System.out.print(" throws "); for(int i = 0 ; i < exceptionType.length; i++){ if(i == exceptionType.length - 1){ System.out.print(exceptionType[i].getName()); break; } System.out.print(exceptionType[i].getName() + ","); } } System.out.println(); }
(5.5)构造器
// getConstructors(): 获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } // getDeclaredConstructors(): 获取当前运行时类中声明的所有构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); }
(6)调用运行时类的指定结构
(6.1)获取运行时类的父类
@Test public void test1(){ Class clazz = Student.class; //获取运行时类的父类 Class superClass = clazz.getSuperclass(); System.out.println(superClass); //class Person }
(6.2)获取运行时类的带泛型的父类; 获取泛型类型
@Test public void test2(){ // public class PersonDao extends Dao<Person> {} Class clazz = PersonDao.class; // 获取运行时类的带泛型的父类 Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); // Dao<Person> ParameterizedType paramType = (ParameterizedType) genericSuperclass; // 获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); System.out.println(actualTypeArguments[0].getTypeName()); // Person System.out.println(((Class)actualTypeArguments[0]).getName()); // Person }
(6.3)获取运行时类的接口;获取运行时类的父类实现的接口
@Test public void test3(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println("----------------"); Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for (Class c : interfaces){ System.out.println(c); } }
(6.4)获取运行时类的包
@Test public void test4(){ Class clazz = com.bearpx.exer.Person.class; Package pack = clazz.getPackage(); System.out.println(pack); // package com.bearpx.exer }
(6.5)获取运行时类的注解
@Test public void test5(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation a : annotations){ System.out.println(a); //@MyAnnotation(value=ho) } }
(6.6)获取运行时类中指定结构: 属性、方法、构造器
@Test public void test5() throws NoSuchFieldException, IllegalAccessException, InstantiationException { Class clazz = Person.class; // 创建运行时类的对象 Person p = (Person) clazz.newInstance(); // 获取指定的属性: 要求属性声明为public Field id = clazz.getField("id"); // 设置当前属性的值: set() 指明设置哪个对象的属性, 将此属性值设置为多少 id.set(p, 1001); // 获取当前属性的值: get() 指明获取哪个对象的当前属性值 int uid = (int) id.get(p); System.out.println(uid); // 获取指定变量名的属性,私有属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p,"zhangsan"); System.out.println(name.get(p)); // zhangsan }
方法
@Test public void test6() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class clazz = Person.class; Person p = (Person) clazz.newInstance(); // 获取指定的某个方法: getDeclaredMethod() 指定获取的方法名称 指明获取的方法的形参列表 Method showNation = clazz.getDeclaredMethod("showNation", String.class); // 保证方法可访问 showNation.setAccessible(true); // invoke(): 方法的调用者 给方法形参赋值的实参 // invoke()的返回值即为对应类中调用方法的返回值 Object returnValue = showNation.invoke(p,"中国"); System.out.println(returnValue); // 我在中国 // 静态方法 Method staticNation = clazz.getDeclaredMethod("staticNation"); staticNation.setAccessible(true); // 如果调用的方法没有返回值, 此invoke() 返回null // Object object = staticNation.invoke(Person.class); Object object = staticNation.invoke(null); // 传null 也可以 System.out.println(object); // 中国 }
构造器
@Test public void test7() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazz = Person.class; // 获取指定的构造器: getDeclaredConstructor(): 参数: 指明构造器的参数列表 Constructor constructor = clazz.getDeclaredConstructor(String.class); constructor.setAccessible(true); Person p = (Person) constructor.newInstance("Tom"); System.out.println(p); // Person{name='Tom', age=0, sex=null} }