• 十六、反射


    一、反射的作用

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

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

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

    • 在运行时获取泛型信息

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

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

    二、反射的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 }
    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);

      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 }
    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
    我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。
  • 相关阅读:
    Android 开发遇到的问题及解决办法
    Android Studio HelloWorld
    Android Studio与Genymontion的安装
    自定义简单版本python线程池
    唯一图库爬取图片
    网页登陆验证之图片验证码
    向后台提交数据:利用cookie加session提交更多数据,
    向后台提交数据:cookie,secure_cookie,
    利用js里的Dom和Date,自定义cookie的前端设置方法
    向后台提交数据:通过form表单提交数据需刷新网页 但通过Ajax提交数据不用刷新网页可通过原生态Ajax或jqueryAjax。Ajax代码部分
  • 原文地址:https://www.cnblogs.com/lixiuming521125/p/15316648.html
Copyright © 2020-2023  润新知