0
点赞
收藏
分享

微信扫一扫

十六、反射

一、反射的作用

  • 在运行时判断任意一个对象的属性
  • 在运行时构造任意一个对象类的对象
  • 在运行时判断任意一个类所拒用的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员成员变量和方法
  • 在运行时处理注解 生成动态代理

二、反射的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类

十六、反射_Java

十六、反射_构造器_02

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:

十六、反射_Java

十六、反射_构造器_02

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

十六、反射_Java

十六、反射_构造器_02

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

十六、反射_Java

十六、反射_构造器_02

1 package reflect;
2
3 public interface MyInfo {
4
5 void info();
6
7

View Code

 

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。



举报

相关推荐

0 条评论