VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • 十六、反射

一、反射的作用

  • 在运行时判断任意一个对象的属性

  • 在运行时构造任意一个对象类的对象

  • 在运行时判断任意一个类所拒用的成员变量和方法

  • 在运行时获取泛型信息

  • 在运行时调用任意一个对象的成员成员变量和方法

  • 在运行时处理注解 生成动态代理

回到顶部

二、反射的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 }
复制代码

 

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

相关教程