• Java之反射(部分文档摘过来方便以后查看)


    第1章 类加载器

    1.1 类的加载

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

    加载

    就是指将class文件读入内存,并为之创建一个Class对象。

    任何类被使用时系统都会建立一个Class对象

    l 连接

    验证 是否有正确的内部结构,并和其他类协调一致

    准备 负责为类的静态成员分配内存,并设置默认初始化值

    解析 将类的二进制数据中的符号引用替换为直接引用

    初始化

    就是我们以前讲过的初始化步骤

    1.2 类初始化时机

    1. 创建类的实例

    2. 类的静态变量,或者为静态变量赋值

    3. 类的静态方法

    4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

    5. 初始化某个类的子类

    6. 直接使用java.exe命令来运行某个主类

    1.3 类加载器

    l 负责将.class文件加载到内在中,并为之生成对应的Class对象。

    l 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

    1.4 类加载器的组成

    l Bootstrap ClassLoader 根类加载器

    也被称为引导类加载器,负责Java核心类的加载

    比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

    l Extension ClassLoader 扩展类加载器

    负责JRE的扩展目录中jar包的加载。

    在JDK中JRE的lib目录下ext目录

    l System ClassLoader 系统类加载器

    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

    通过这些描述就可以知道我们常用的类,都是由谁来加载完成的。

    到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?

    这就是我们反射要研究的内容。

    第2章 反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

    2.1 Class

    阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

    l 获取Class对象的三种方式

    方式: 通过Object类中的getObject()方法

    Person p = new Person();

    Class c = p.getClass();

    方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。

    Class c2 = Person.class;

    方式三通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。

    Class c3 = Class.forName("Person");

    l 注意:第三种和前两种的区别

    前两种你必须明确Person类型.

    后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

    l 代码演示

     1 /*
     2  * 获取.class字节码文件对象的方式
     3  *         1:通过Object类中的getObject()方法
     4  *         2: 通过 类名.class 获取到字节码文件对象
     5  *         3: 反射中的方法,
     6  *             public static Class<?> forName(String className) throws ClassNotFoundException
     7  *             返回与带有给定字符串名的类或接口相关联的 Class 对象 
     8  */
     9 public class ReflectDemo {
    10     public static void main(String[] args) throws ClassNotFoundException {
    11         // 1: 通过Object类中的getObject()方法
    12         // Person p1 = new Person();
    13         // Class c1 = p1.getClass();
    14         // System.out.println("c1 = "+ c1);
    15 
    16         // 2: 通过 类名.class 获取到字节码文件对象
    17         // Class c2 = Person.class;
    18         // System.out.println("c2 = "+ c2);
    19 
    20         // 3: 反射中的方法
    21         Class c3 = Class.forName("cn.itcast_01_Reflect.Person");// 包名.类名
    22         System.out.println("c3 = " + c3);
    23     }
    24 }
    View Code

        例子中用到的Person类:

     1 package com.xujingyang.reflect;
     2 
     3 public class Person {
     4     // 成员变量
     5     public String name;
     6     public int age;
     7     private String address;
     8 
     9     // 构造方法
    10     public Person() {
    11         System.out.println("空参数构造方法");
    12     }
    13 
    14     public Person(String name) {
    15         this.name = name;
    16         System.out.println("带有String的构造方法");
    17     }
    18 
    19     // 私有的构造方法
    20     private Person(String name, int age) {
    21         this.name = name;
    22         this.age = age;
    23         System.out.println("带有String,int的构造方法");
    24     }
    25 
    26     public Person(String name, int age, String address) {
    27         this.name = name;
    28         this.age = age;
    29         this.address = address;
    30         System.out.println("带有String, int, String的构造方法");
    31     }
    32 
    33     // 成员方法
    34     // 没有返回值没有参数的方法
    35     public void method1() {
    36         System.out.println("没有返回值没有参数的方法");
    37     }
    38 
    39     // 没有返回值,有参数的方法
    40     public void method2(String name) {
    41         System.out.println("没有返回值,有参数的方法 name= " + name);
    42     }
    43 
    44     // 有返回值,没有参数
    45     public int method3() {
    46         System.out.println("有返回值,没有参数的方法");
    47         return 123;
    48     }
    49 
    50     // 有返回值,有参数的方法
    51     public String method4(String name) {
    52         System.out.println("有返回值,有参数的方法");
    53         return "哈哈" + name;
    54     }
    55 
    56     // 私有方法
    57     private void method5() {
    58         System.out.println("私有方法");
    59     }
    60 
    61     @Override
    62     public String toString() {
    63         return "Person [name=" + name + ", age=" + age + ", address=" + address
    64                 + "]";
    65     }
    66 }
    View Code

        

        

    2.2 通过反射获取构造方法并使用

    在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:

    l 返回一个构造方法

    l public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

    l public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

    l 返回多个构造方法

    l public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

    l public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

    2.2.1 通过反射方式,获取构造方法,创建对象

    获取构造方法,步骤如下:

    1. 获取到Class对象

    2. 获取指定的构造方法

    3. 通过构造方法类Constructor中的方法,创建对象

    public T newInstance(Object... initargs)

    2.2.2 通过反射方式,获取私有构造方法,创建对象

    AccessibleObject 类是 FieldMethod Constructor 对象的类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

    对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 FieldMethod Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下

    l public void setAccessible(boolean flag) throws SecurityException 

    参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

    获取私有构造方法,步骤如下:

    1. 获取到Class对象

    2. 获取指定的构造方法

    3. 暴力访问, 通过setAccessible(boolean flag)方法

    4. 通过构造方法类Constructor中的方法,创建对象

    public T newInstance(Object... initargs)

    关于构造方法的所有代码如下:

     1 package com.xujingyang.getconstructor;
     2 
     3 import java.lang.reflect.Constructor;
     4 
     5 public class GetConstructorDemo {
     6     public static void main(String[] args) throws Exception {
     7         Class clazz = Class.forName("com.xujingyang.reflect.Person");
     8 
     9         // 1.获取所有公有的构造函数
    10         Constructor[] constructors1 = clazz.getConstructors();
    11         for (Constructor constructor : constructors1) {
    12             System.out.println(constructor);
    13         }
    14 
    15         System.out.println("=======================================================");
    16         // 2.获取所有的构造函数
    17         Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    18         for (Constructor constructor : declaredConstructors) {
    19             System.out.println(constructor);
    20         }
    21 
    22         System.out.println("=======================================================");
    23 
    24         // 3.获取指定参数类型的公有的构造函数,查不到私有的
    25         Constructor constructor2 = clazz.getConstructor(String.class);
    26         System.out.println(constructor2);
    27 
    28         System.out.println("=======================================================");
    29         // 4.查询所有指定的构造函数,包括私有的
    30         Constructor declaredConstructor2 = clazz.getDeclaredConstructor(String.class, int.class);
    31         System.out.println(declaredConstructor2);
    32         
    33         System.out.println("=======================================================");
    34 
    35 //        这种创建对象,一样不能使用私有构造函数来创建
    36         Object obj = constructor2.newInstance("小明");
    37         System.out.println(obj);
    38         
    39         System.out.println("=======================================================");
    40         
    41 //        可以通过设置Constructor父类AccessibleObject的setAccesible(true)方法来取消Java语言访问权限检查
    42         declaredConstructor2.setAccessible(true);
    43         Object newInstance = declaredConstructor2.newInstance("小红",19);
    44         System.out.println(newInstance);
    45     }
    46 }
    View Code

      

    2.3 通过反射获取成员变量并使用

    在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:

    l 返回一个成员变量

    l public Field getField(String name) 获取指定的 public修饰的变量

    l public Field getDeclaredField(String name) 获取指定的任意变量

    l 返回多个成员变量

    l public Field[] getFields() 获取所有public 修饰的变量

    l public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

     

    2.3.1 通过反射,创建对象,获取指定的成员变量,进行赋值与获取值操作

    获取成员变量,步骤如下:

    1. 获取Class对象

    2. 获取构造方法

    3. 通过构造方法,创建对象

    4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

    5. 通过方法,给指定对象的指定成员变量赋值或者获取值

    l public void set(Object obj, Object value)

    在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值

    l public Object get(Object obj)

              返回指定对象obj中,此 Field 对象表示的成员变量的值

    关于字段的所有代码如下:

     1 package com.xujingyang.getfileds;
     2 
     3 import java.lang.reflect.Field;
     4 
     5 public class getFiledDemo {
     6     public static void main(String[] args) {
     7         try {
     8             Class clazz = Class.forName("com.xujingyang.reflect.Person");
     9 //            获取所有共有成员变量
    10             Field[] fields = clazz.getFields();
    11             for (Field field : fields) {
    12                 System.out.println(field);
    13             }
    14             
    15             System.out.println("==============================");
    16             
    17 //            获取所有成员变量,包括私有的
    18             Field[] declaredFields = clazz.getDeclaredFields();
    19             for (Field field : declaredFields) {
    20                 System.out.println(field);
    21             }
    22             
    23             System.out.println("==============================");
    24             
    25 //            获取指定的公有变量
    26             Field field = clazz.getField("name");
    27             System.out.println(field);
    28             
    29             System.out.println("==============================");
    30             
    31 //            获取指定的私有的变量
    32             Field declaredField = clazz.getDeclaredField("address");
    33             System.out.println(declaredField);
    34             
    35             System.out.println("======================获取和设置变量的值===============================");
    36 //    =========================获取和设置变量的值======================================================
    37         
    38 //            先通过构造方法初始化对象
    39             Object obj = clazz.getConstructor(String.class).newInstance("小明");
    40             
    41 //            获取公有字段的值
    42             System.out.println(field.get(obj));
    43 //        设置公有字段的值    
    44             field.set(obj, "小红");
    45             System.out.println(field.get(obj));
    46 
    47 //            取消Java的安全检查
    48             declaredField.setAccessible(true);
    49 //            给私有属性设置值
    50             declaredField.set(obj, "中国");
    51 //            获取私有属性的值
    52             System.out.println(declaredField.get(obj));
    53             
    54 //            设置和获取私有变量的值
    55             
    56         } catch (Exception e) {
    57             e.printStackTrace();
    58         }
    59     }
    60 }
    View Code

     

    2.4 通过反射获取成员方法并使用

    在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

    l 返回获取一个方法:

    l public Method getMethod(String name, Class<?>... parameterTypes)

      获取public 修饰的方法

    l public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

      获取任意的方法,包含私有的

    参数1: name 要查找的方法名称; 参数2parameterTypes 该方法的参数类型

    l 返回获取多个方法:

    l public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

    public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的) 

    2.4.1 通过反射,创建对象,调用指定的方法

    获取成员方法,步骤如下:

    1. 获取Class对象

    2. 获取构造方法

    3. 通过构造方法,创建对象

      4. 获取指定的方法

      5. 执行找到的方法

    l public Object invoke(Object obj,  Object... args) 

    执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    2.4.2 通过反射,创建对象,调用指定的private 方法

    获取私有成员方法,步骤如下:

    1. 获取Class对象

    2. 获取构造方法

    3. 通过构造方法,创建对象

    4. 获取指定的方法

    5. 开启暴力访问

    6. 执行找到的方法

    l public Object invoke(Object obj,  Object... args)

    执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    关于方法的反射所有代码如下:

     1 package com.xujingyang.getmethod;
     2 
     3 import java.lang.reflect.Constructor;
     4 import java.lang.reflect.Method;
     5 
     6 public class GetMethodDemo {
     7     public static void main(String[] args) {
     8         try {
     9             Class clazz = Class.forName("com.xujingyang.reflect.Person");
    10             
    11 //            获取所有包括私有方法,但不包括继承父类的方法
    12             Method[] declaredMethods = clazz.getDeclaredMethods();
    13             for (Method method : declaredMethods) {
    14                 System.out.println(method);
    15             }
    16             
    17             System.out.println("=============================");
    18             
    19 //            返回所有的公有的方法,包括继承的方法
    20             Method[] methods = clazz.getMethods();
    21             for (Method method : methods) {
    22                 System.out.println(method);
    23             }
    24             
    25             System.out.println("=======================================");
    26             
    27 //            获取公有的指定名称的方法
    28             Method method = clazz.getMethod("method2", String.class);
    29             System.out.println(method);
    30             
    31             System.out.println("============================");
    32             
    33 //            获取私有的指定方法名称和参数类型的方法
    34             Method declaredMethod = clazz.getDeclaredMethod("method5", null);
    35             System.out.println(declaredMethod);
    36             
    37 //    ============================反射调用方法=======================================
    38 //            通过反射得构造函数
    39             Constructor constructor = clazz.getConstructor(null);
    40 //            通过构造函数初始化一个对象
    41             Object obj1 = constructor.newInstance();
    42 //            通过反射得到指定的方法对象
    43             Method method2 = clazz.getMethod("method1", null);
    44 //            通过方法对象的invoke方法,调用指定的方法
    45             method2.invoke(obj1, null);
    46             
    47             
    48 //            ====================
    49 //            调用私有方法
    50             Method declaredMethod2 = clazz.getDeclaredMethod("method5", null);
    51 //            取消安全检查
    52             declaredMethod2.setAccessible(true);
    53             declaredMethod2.invoke(obj1, null);
    54         } catch (Exception e) {
    55             e.printStackTrace();
    56         }
    57     }
    58 }
    View Code

     

    第3章 反射练习

    3.1 泛型擦除

    思考,将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢?

    我来告诉大家,其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素

    l  代码如下

     1 package com.xujingyang.test;
     2 
     3 import java.lang.reflect.Method;
     4 import java.util.ArrayList;
     5 
     6 public class GaiArrayListDemo {
     7     public static void main(String[] args) throws Exception {
     8         ArrayList<Integer> list=new ArrayList<Integer>();
     9         list.add(123);
    10         list.add(new Integer("12345"));
    11         list.add(new Integer("12345"));
    12 //        list.add(new Integer("哈哈"));
    13         
    14         Class clazz = list.getClass();
    15         Method method = clazz.getMethod("add", Object.class);
    16         method.invoke(list, "哈哈");
    17         
    18         
    19         System.out.println(list);
    20     }
    21 }
    View Code

    3.2 反射配置文件

    l 通过反射配置文件,运行配置文件中指定类的对应方法

    读取Peoperties.txt文件中的数据,通过反射技术,来完成Person对象的创建

    Peoperties.txt文件内容如下:

    className=com.xujingyang.reflect.Person

    methodName=method5

     

    l 读取配置文件,调用指定类中的对应方法

     1 package com.xujingyang.getproperties;
     2 
     3 import java.io.FileInputStream;
     4 import java.lang.reflect.Method;
     5 import java.util.Properties;
     6 
     7 public class test2 {
     8     public static void main(String[] args) throws Exception {
     9         Properties properties=new Properties();
    10         properties.load(new FileInputStream("src/properties.p"));
    11         String className = (String) properties.get("className");
    12         String methodName = (String) properties.get("methodName");
    13         
    14         Class clazz = Class.forName(className);
    15         Object obj = clazz.getDeclaredConstructor().newInstance();
    16         Method method = clazz.getDeclaredMethod(methodName, null);
    17         method.setAccessible(true);
    18         method.invoke(obj, null);
    19     }
    20 }
    View Code

    第4章 总结

    4.1 知识点总结

    如何获取.Class文件对象

    1, 通过ObjectgetClass()方法获取 Class对象

    2, 通过类名.class 方式 获取 Class对象

    3, 通过反射的方式, Class.forName(String classname) 获取Class对象

    public static Class<?> forName(String className)throws ClassNotFoundException

    返回与带有给定字符串名的类或接口相关联的 Class 对象

    通过反射, 获取类中的构造方法,并完成对象的创建

    获取指定的构造方法

       public Constructor<T> getConstructor(Class<?>... parameterTypes)

    获取指定的public修饰的构造方法

       public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

    获取指定的构造方法,包含私有的

       获取所有的构造方法

       public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

       public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的

    通过反射, 获取类中的构造方法,并完成对象的创建

       步骤:

       1,获取字节码文件对象

       2,通过字节码文件对象 ,获取到指定的构造方法

       getConstructor(参数);

       3,通过构造方法,创建对象

       public T newInstance(Object... initargs)

    l 私有构造方法,创建对象

      1,获取字节码文件对象

       2,通过字节码文件对象 ,获取到指定的构造方法

       getDeclaredConstructor (参数);

       3,暴力访问

       con.setAccessible(true);

       4,通过构造方法,创建对象

       public T newInstance(Object... initargs)

    通过反射,获取Class文件中的方法

       获取指定的方法

       public Method getMethod(String name, Class<?>... parameterTypes)

       获取指定的public方法

       public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

       获取指定的任意方法,包含私有的

       获取所有的方法

       public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

       ublic Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

    l 通过反射,调用方法

       步骤:

       1,获取Class对象

       2,获取构造方法,创建对象

          3,获取指定的public方法

       4,执行方法

       public Object invoke(Object obj, Object... args)

    l 私有方法的调用:

     1,获取Class对象

     2,获取构造方法,创建对象

     3,获取指定的private方法

     4,开启暴力访问

      m5.setAccessible(true);

     5,执行方法

      public Object invoke(Object obj, Object... args)

    通过反射,获取成员变量(字段)

       获取指定的成员变量

        public Field getField(String name) 获取public修饰的成员变量

        public Field getDeclaredField(String name) 获取任意的成员变量,包含私有

       获取所有的成员变量

        public Field[] getFields() 获取所有public修饰的成员变量

        public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有

    通过反射,获取成员 变量,并赋值使用

       步骤:

       1,获取字节码文件对象

       2,获取构造方法,创建对象

       3,获取指定的成员变量

       4,对成员变量赋值获取值操作

       public void set(Object obj,  Object value) 赋值

       public Object get(Object obj) 获取值

    l 私有成员变量的使用

        步骤:

       1,获取字节码文件对象

       2,获取构造方法,创建对象

       3,获取指定的成员变量

       4,开启暴力访问

       5,对成员变量赋值获取值操作

       public void set(Object obj,  Object value) 赋值

       public Object get(Object obj) 获取值

  • 相关阅读:
    Day 83 VUE——组件、插槽、生命周期
    Power BI制作GDP动态排行榜
    SQL中为什么经常要加WITH(NOLOCK)
    ant-desin-vue——table全选时自定义的禁用行也被选上,且最后一行不选中问题
    ant-design-vue——a-select下拉框值为空字串时,高度偏窄问题
    ant-desgin-vue——tree自定义节点不可选用的置灰或禁用
    js——在A页面打开新页面B,关闭新页面B后刷新A页面
    Vue You may have an infinite update loop in a component render function.
    js实现继承的方法中为何总是要修正constructor方法的指向呢?
    Spring boot 扫描不到 mybatis 接口包
  • 原文地址:https://www.cnblogs.com/xujingyang/p/6486118.html
Copyright © 2020-2023  润新知