一、反射的作用
- 在运行时判断任意一个对象的属性
- 在运行时构造任意一个对象类的对象
- 在运行时判断任意一个类所拒用的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员成员变量和方法
- 在运行时处理注解 生成动态代理
二、反射的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 == 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
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
View Code
问题点
疑问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);
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
View Code
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
View Code
MyInfo
1 package reflect;
2
3 public interface MyInfo {
4
5 void info();
6
7
View Code
我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。