一、反射的作用
-
在运行时判断任意一个对象的属性
-
在运行时构造任意一个对象类的对象
-
在运行时判断任意一个类所拒用的成员变量和方法
-
在运行时获取泛型信息
-
在运行时调用任意一个对象的成员成员变量和方法
-
在运行时处理注解 生成动态代理
二、反射的api(java.lang.Class)
api:
-
java.lang.reflec.Method:代表类的方法
-
java.lang.reflect.Field:代码类的成员变量
-
java.lang.reflect.Constructor:代表类的构造器
关于 java.lang.Class的理解
类的加载过程:
javac.exe 生成一个或者多个字节码文件(.class), java.exe 命令对某个字节码文件进行解释运行,相当于某个字节码文件加载到内存中。此过程就称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的一个实例 ;也就是说:new Person(),这是造对象的一个过程, 那么 Person.class 也是在造对象,他是Class的一个对象;Class的一个实例就对应这一个运行时类;
加载到内存中的运行时类,会缓存一定的时间。在此时间内,我们可以通过不同的方式来获取运行时类,Class实例的方式获取方式常用的有3种,下面会讲到;
clazz1,clazz2,clazz3 指向的同一个运行时类的地址;如下代码System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3);都是true;
Class实例的方式获取方式常用的有3种
方式一调用运行时类的属性:
Class clazz1 = Person.class;
方式二通过运行时类的对象:
Person p1 = new Person(); Class clazz2 = p1.getClass();
方式3调用Class的静态方法:forName(String classPath),最常用的:
Class clazz3 = Class.forName("reflect.Person");
其他如使用类的加载器:
ClassLoader classLoader = Day6_Conception.class.getClassLoader();
1 /** 2 * 获取Class实例的方式 3 * 4 * @Description 5 * @author lixiuming 6 * @throws ClassNotFoundException 7 * @date 2021年9月19日上午10:43:48 8 * 9 */ 10 11 @Test 12 public void test3() throws ClassNotFoundException { 13 // 方式一:调用运行时类的属性 14 // Class clazz1 = Person.class;//这样也可以 15 Class<Person> clazz1 = Person.class; 16 System.out.println(clazz1); 17 18 // 方式二:通过运行时类的对象 19 Person p1 = new Person(); 20 Class clazz2 = p1.getClass(); 21 System.out.println(clazz2); 22 23 // 方式三:调用Class的静态方法:forName(String classPath),最常用的 24 Class clazz3 = Class.forName("reflect.Person"); 25 System.out.println(clazz3); 26 27 System.out.println(clazz1 == clazz2); 28 System.out.println(clazz2 == clazz3); 29 30 // 方式四使用类的加载器 31 ClassLoader classLoader = Day6_Conception.class.getClassLoader(); 32 classLoader.loadClass("reflect.Person"); 33 34 }
关于方式四的补充:
@Test public void test5() throws IOException { Properties prop = new Properties(); // 默认文件识别位置在src下 ClassLoader clazzLoader = Day6_Conception.class.getClassLoader(); InputStream is = clazzLoader.getResourceAsStream("test.properties"); prop.load(is);// 加载对应流文件 String name = prop.getProperty("name"); String value = prop.getProperty("password"); System.out.println("name:" + name); System.out.println("value:" + value); }
Properties读取文件的普通方式:
@Test public void test5() throws IOException { Properties prop = new Properties(); // 默认此时的文件在当前工程下 // 读取文件方式一 FileInputStream fis = new FileInputStream(new File("test.properties")); prop.load(fis);// 加载对应流文件 String name = prop.getProperty("name"); String value = prop.getProperty("password"); System.out.println("name:" + name); System.out.println("value:" + value); }
哪些类型可以有Class对象
-
1.class :外部类、成员(成员内部类,静态内部类,局部内部类,匿名内部类)
-
2.interface:接口
-
3.enum:枚举
-
4.[]:数组;
-
5.annotation:注解
-
6.primitive type:基本数据类型
-
7.void
1 @Test 2 public void test4() { 3 Class c1 = Object.class; 4 Class c2 = Comparable.class; 5 Class c3 = String[].class; 6 Class c4 = int[][].class; 7 Class c5 = ElementType.class; 8 Class c6 = Override.class; 9 Class c7 = int.class; 10 Class c8 = void.class; 11 Class c9 = Class.class; 12 int[] a = new int[10]; 13 int[] b = new int[100]; 14 15 // 只要数组的元素类型和维度一样,就是同一个class 16 Class c11 = a.getClass(); 17 Class c12 = b.getClass(); 18 System.out.println(c11 == c12); 19 20 }
三、反射动态性的体现
1 /** 2 * 创建一个指定类的对象 classPath:指向一个全类名 当只有在运行时才能确定创建的类需要用到该方法 3 * 4 * 体现反射的动态性 5 * 6 * @Description 7 * @author lixiuming 8 * @throws IllegalAccessException 9 * @throws InstantiationException 10 * @throws ClassNotFoundException 11 * @date 2021年9月19日下午10:58:33 12 * 13 */ 14 @Test 15 public void test7() throws InstantiationException, IllegalAccessException, ClassNotFoundException { 16 int a = new Random().nextInt(3);// 0,1,2 17 String classPath = ""; 18 switch (a) { 19 case 0: 20 classPath = "java.util.Date"; 21 break; 22 case 1: 23 classPath = "java.lang.Object"; 24 break; 25 case 2: 26 classPath = "reflect.Person"; 27 break; 28 } 29 System.out.println(getInstance(classPath)); 30 } 31 32 /** 33 * 创建一个指定类的对象 classPath:指定类的全类名 34 * 35 * @Description 36 * @author lixiuming 37 * @date 2021年9月20日下午10:31:01 38 * @param path 39 * @return 40 * @throws ClassNotFoundException 41 * @throws InstantiationException 42 * @throws IllegalAccessException 43 * 44 */ 45 private Object getInstance(String path) 46 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 47 Class clazzClass = Class.forName(path); 48 Object obj = clazzClass.newInstance(); 49 return obj; 50 }
四、使用反射/不使用反射对类的操作的对比
结论:
使用反射可以获取对象私有个属性、方法、构造器,而没有使用反射的话,不能获取对象的私有的属性、方法、构造器;
对比代码如下:
1 // 反射之前,对于Person的操作 2 @Test 3 public void test1() { 4 //1.创建Person类的对象 5 Person p1 = new Person("tom", 12); 6 // 2.调用内部的属性和方法 7 p1.age = 10; 8 System.out.println(p1.toString()); 9 p1.show(); 10 // 在Person类外部,不可以调用Person类的私有结构 11 // 比如,私用的name showNation以及私用构造器; 12 13 } 14 15 // 反射之后,对于Person的操作:调用私有结构。比如私有构造器、方法和属性; 16 // 17 @Test 18 public void test2() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, 19 IllegalArgumentException, InvocationTargetException, NoSuchFieldException { 20 // 通过反射,创建Person类的对象 21 Class clazz = Person.class; 22 Constructor constructor = clazz.getConstructor(String.class, int.class); 23 Object object = constructor.newInstance("tome", 12); 24 System.out.println(object.toString());
Person类
1 package reflect; 2 3 public class Person { 4 private String name; 5 public int age; 6 7 public String getName() { 8 return name; 9 } 10 11 public void setName(String name) { 12 this.name = name; 13 } 14 15 public int getAge() { 16 return age; 17 } 18 19 public void setAge(int age) { 20 this.age = age; 21 } 22 23 public Person(String name, int age) { 24 super(); 25 this.name = name; 26 this.age = age; 27 } 28 29 public Person() { 30 super(); 31 } 32 33 @Override 34 public String toString() { 35 return "Person [name=" + name + ", age=" + age + "]"; 36 } 37 38 public void show() { 39 System.out.println("我是一个人"); 40 } 41 42 private String showNation(String nation) { 43 System.out.println("我的国籍是:" + nation); 44 return nation; 45 } 46 47 private Person(String name) { 48 super(); 49 this.name = name; 50 } 51 52 }
问题点
疑问1 通过直接new 对象的方式或者反射的方式都可以调用公共的结构,开发中到底用哪个?
建议:直接new的方式
疑问2: 什么时候会用反射的方式;
编译时候的确定不下来传哪个对象,
疑问3,反射机制与面向对象中的封装性是不是矛盾的,如何看待两个技术?
不矛盾。封装性:建议不要调用私有(属性,方法,构造器);反射:能不能调用私有(属性,方法,构造器)
五、通过反射,获取类中的结构:
1.获取属性
-
clazzClass.getFields()// 获取当前运行时类及其父类的public访问权限的属性
-
clazzClass.getDeclaredFields();// 获取当前运行时类中声明的所有属性,不包括父类
1 @Test 2 public void test8xx() { 3 Class clazzClass = Man.class; 4 5 // 获取属性********************************************************************* 6 System.out.println("***********获取当前运行时类及其父类的public访问权限的属性**********"); 7 Field[] fields = clazzClass.getFields();// 获取当前运行时类及其父类的public访问权限的属性,public double reflect.Creater.weight 8 for (Field item : fields) { 9 System.out.println(item);// public int reflect.Man.id 10 } 11 System.out.println("****************获取当前运行时类中声明的所有属性,不包括父类***************************"); 12 Field[] fields1 = clazzClass.getDeclaredFields();// 获取当前运行时类中声明的所有属性,不包括父类, 13 for (Field item : fields1) { 14 System.out.println(item); 15 16 // 1.获取权限修饰符 17 int a = item.getModifiers(); 18 System.out.println("属性权限修饰符:" + Modifier.toString(a)); 19 // 2.数据类型 20 System.out.println("属性的数据类型(带class):" + item.getType()); 21 System.out.println("属性的数据类型(不带class):" + item.getType().getName()); 22 // 3.变量名 23 System.out.println("属性的名称:" + item.getName()); 24 }
2.获取方法
@Test public void test8xx() { Class clazzClass = Man.class; // 获取方法********************************************************************* System.out.println("**************获取当前运行时类及其父类的public访问权限的*********************"); Method[] methods = clazzClass.getMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("*************************获取当前运行时类中声明的所有方法,不包括父类************************************"); // 1.获取注解 Method[] methods1 = clazzClass.getDeclaredMethods(); for (Method method : methods1) { System.out.println(method); Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("获取注解:" + annotation); System.out.println("获取注解类型:" + annotation.annotationType()); } // 2.方法权限修饰符 int a = method.getModifiers(); System.out.println("方法权限修饰符:" + Modifier.toString(a)); // 3.获取返回值类型 System.out.println("方法返回值类型:" + method.getReturnType()); // 4.获取方法名称 System.out.println("方法名称:" + method.getName()); // 5.形参列表 Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { System.out.println(" 第" + (i + 1) + "个参数名称:" + parameterTypes[i].getName()); } // 6.获取抛出异常 Class[] exceptionTypes = method.getExceptionTypes(); for (int i = 0; i < exceptionTypes.length; i++) { System.out.println(" 第" + (i + 1) + "个异常名称:" + exceptionTypes[i].getName()); } } }
3.获取类
1 @Test 2 public void test8xx() { 3 Class clazzClass = Man.class; 4 5 System.out.println("***************************获取当前运行时类中声明的所有构造器,不包括父类***************************"); 6 Constructor[] constructors2 = clazzClass.getDeclaredConstructors(); 7 for (Constructor cons : constructors2) { 8 System.out.println(cons); 9 } 10 11 System.out.println("************************获取当前运行时类的父类 & 带泛型的父类 & 带泛型的父类的泛型***********************"); 12 Class superclass = clazzClass.getSuperclass(); 13 System.out.println("获取当前运行时类的父类:" + superclass.getName()); 14 System.out.println(superclass.getSimpleName()); 15 Type genericSuperclass = clazzClass.getGenericSuperclass(); 16 System.out.println("带泛型的父类:" + genericSuperclass); 17 ParameterizedType paramType = (ParameterizedType) genericSuperclass; 18 Type[] actualTypeArguments = paramType.getActualTypeArguments(); 19 for (int i = 0; i < actualTypeArguments.length; i++) { 20 Class temp = (Class) actualTypeArguments[i]; 21 System.out.println(" 带泛型的父类的泛型:" + actualTypeArguments[i].getTypeName()); 22 System.out.println(" 带泛型的父类的泛型:" + temp.getSimpleName()); 23 } 24 25 System.out.println("*********************************获取当前运行时类的接口************************************"); 26 Class[] interfaces = clazzClass.getInterfaces(); 27 for (Class cls : interfaces) { 28 System.out.println(cls.getName()); 29 System.out.println(cls.getSimpleName()); 30 } 31 //获取运行时类的父类的接口 32 Class[] interfaces2 = clazzClass.getSuperclass().getInterfaces(); 33 for (Class cls : interfaces2) { 34 System.out.println(cls.getName()); 35 System.out.println(cls.getSimpleName()); 36 } 37 }
4.获取包:
1 @Test 2 public void test8xx() { 3 Class clazzClass = Man.class; 4 5 System.out.println("*********************************获取当前运行时所在的包************************************"); 6 Package package1 = clazzClass.getPackage(); 7 System.out.println(package1.getName()); 8 System.out.println("*********************************获取当前运行时类声明的注解************************************"); 9 Annotation[] annotations2 = clazzClass.getAnnotations(); 10 for (Annotation item : annotations2) { 11 System.out.println(item); 12 } 13 }
六、调用运行时类中指定的接口:属性、方法、构造器
1.调用属性
步骤:
1.获取指定的属性 Field fiedName = clazzClass.getDeclaredField("name");//name是属性名称
2.保证可访问:fiedName.setAccessible(true)
3.获取、设置指定对象的此属性的值;fiedName.set(man, "Tom");//man是对象,"Tom"是参数;(String) fiedName.get(man);//获取设置好对象属性
1 @Test 2 public void test8xx() 3 throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException { 4 Class clazzClass = Man.class; 5 6 Man man = (Man) clazzClass.newInstance(); 7 // 获取指定的属性 8 Field field = clazzClass.getField("id"); 9 // 设置当前属性值 10 field.setInt(man, 1001); 11 12 // 获取当前属性的值 13 int mId = (int) field.get(man); 14 System.out.println(mId); 15 16 // 获取指定的属性 17 Field fiedAge = clazzClass.getDeclaredField("age"); 18 // 设置当前属性值 19 fiedAge.setInt(man, 1001); 20 21 // 获取当前属性的值 22 int mAge = (int) fiedAge.get(man); 23 System.out.println(mAge); 24 25 // 1.获取指定的属性 26 Field fiedName = clazzClass.getDeclaredField("name"); 27 28 // 2.保证当前属性可访问 29 fiedName.setAccessible(true);// 否则不打印Tom 30 // 3.获取、设置指定对象的此属性的值; 31 fiedName.set(man, "Tom"); 32 33 // 获取当前属性的值 34 String name = (String) fiedName.get(man); 35 System.out.println(name); 36 }
2.调用指定方法
- 获取非静态方法步骤:
1.获取指定的方法:Method declaredMethod = clazzClass.getDeclaredMethod("show", java.lang.String.class);
2.保证可访问:declaredMethod2.setAccessible(true);
3.调用方法,并且加入参数:Object returnValue = declaredMethod.invoke(man, "USA")
补充:invoke()的返回值即为对应类中调用的方法的返回值
- 获取静态方法步骤:
1.获取指定的方法:Method declaredMethod2 = clazzClass.getDeclaredMethod("shwoDesc");//shwoDesc为方法名
2.保证可访问:declaredMethod2.setAccessible(true);
3.调用方法:Object returnValue2 = declaredMethod2.invoke(Man.class);
补充:若果调用的运行时类中的方法没有返回值,则此invoke返回nul
1 @Test 2 public void test8xx() throws InstantiationException, IllegalAccessException, NoSuchMethodException, 3 SecurityException, IllegalArgumentException, InvocationTargetException { 4 Class clazzClass = Man.class; 5 Man man = (Man) clazzClass.newInstance(); 6 // 获取指定的方法 7 // 1.获取指定的方法 8 Method declaredMethod = clazzClass.getDeclaredMethod("show", java.lang.String.class); 9 10 // 2.保证当方法性可访问 11 declaredMethod.setAccessible(true);// 否则不打印Tom 12 // 3.调用方法并且加入参数; 13 Object returnValue = declaredMethod.invoke(man, "USA");// invoke()的返回值即为对应类中调用的方法的返回值 14 15 16 System.out.println(returnValue); 17 18 // 获取指定的方法(静态) 19 // 1.获取指定的方法 20 Method declaredMethod2 = clazzClass.getDeclaredMethod("shwoDesc"); 21 22 // 2.保证当方法性可访问 23 declaredMethod2.setAccessible(true);// 否则不打印Tom 24 // 3.调用方法; 25 Object returnValue2 = declaredMethod2.invoke(Man.class);// 若果调用的运行时类中的方法没有返回值,则此invoke返回null 26 28 System.out.println(returnValue2); 29 }
3.调用知道构造器
1.获取指定构造器并指明构造器的参数列表:clazzClass.getDeclaredConstructor(String.class)
2.保证可访问:declaredConstructor.setAccessible(true);
3.创建运行时类的对象:eclaredConstructor.newInstance("JERRY");
1 @Test 2 public void test8xx() throws NoSuchMethodException, SecurityException, InstantiationException, 3 IllegalAccessException, IllegalArgumentException, InvocationTargetException { 4 Class clazzClass = Man.class; 5 // 获取指定构造器 6 Constructor declaredConstructor = clazzClass.getDeclaredConstructor(String.class);// 指明构造器的参数列表 7 declaredConstructor.setAccessible(true); 8 // 创建运行时类的对象 9 Object newInstance = declaredConstructor.newInstance("JERRY"); 10 System.out.println(newInstance); 11 }
补充:
创建对象的方式:
- new+构造器
- 要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中是否有静态方法存在,可以调用静态方法创建Xxx对象;
- 通过反射实现;
附录:
Man:
1 package reflect; 2 3 @MyAnnotation 4 public class Man extends Creater<String> implements Comparable<String>, MyInfo { 5 6 @Override 7 public int compareTo(String o) { 8 // TODO Auto-generated method stub 9 return 0; 10 } 11 12 @Override 13 public void info() { 14 // TODO Auto-generated method stub 15 16 } 17 18 private String name; 19 int age; 20 public int id; 21 22 public Man() { 23 24 } 25 26 public String getName() { 27 return name; 28 } 29 30 public void setName(String name) { 31 this.name = name; 32 } 33 34 public int getAge() { 35 return age; 36 } 37 38 public void setAge(int age) { 39 this.age = age; 40 } 41 42 public int getId() { 43 return id; 44 } 45 46 public void setId(int id) { 47 this.id = id; 48 } 49 50 public Man(String name, int age) { 51 super(); 52 this.name = name; 53 this.age = age; 54 } 55 56 @MyAnnotation(value = "hi") 57 public Man(String name) { 58 super(); 59 this.name = name; 60 } 61 62 private Man(int age) { 63 super(); 64 this.age = age; 65 } 66 67 @MyAnnotation 68 private String show(String nation) { 69 System.out.println("我的国籍是:" + nation); 70 return nation; 71 } 72 73 public String display(String interests, int age) throws java.lang.NullPointerException { 74 return interests + age; 75 } 76 77 private static void shwoDesc() { 78 System.out.println("我是一个可爱的人"); 79 } 80 81 @Override 82 public String toString() { 83 return "Man [name=" + name + ", age=" + age + ", id=" + id + "]"; 84 } 85 86 }
MyAnnotation
1 package reflect; 2 3 import static java.lang.annotation.ElementType.CONSTRUCTOR; 4 import static java.lang.annotation.ElementType.FIELD; 5 import static java.lang.annotation.ElementType.LOCAL_VARIABLE; 6 import static java.lang.annotation.ElementType.METHOD; 7 import static java.lang.annotation.ElementType.PARAMETER; 8 import static java.lang.annotation.ElementType.TYPE; 9 10 import java.lang.annotation.Retention; 11 import java.lang.annotation.RetentionPolicy; 12 import java.lang.annotation.Target; 13 14 @Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE }) 15 @Retention(RetentionPolicy.RUNTIME) 16 public @interface MyAnnotation { 17 18 String value() default "hello"; 19 }
MyInfo
1 package reflect; 2 3 public interface MyInfo { 4 5 void info(); 6 7 }