zoukankan      html  css  js  c++  java
  • Java反射机制详解

    一、Java反射机制

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

    反射机制主要是用来破解类文件的:

    正常的文件执行:在源代码已知的情况下导入类路径,通过new关键字实例化该类对象,通过对象名使用这个类。

    反射文件执行:在源代码未知的情况下,对类文件进行破解,然后再去使用这个类。

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

    二、 Class类

      想要破解类,就必须对类进行了解,类其实也是一种类型,对于一个类来讲,应该是由属性、普通方法、构造方法等等构成的。API中同样提供了一个Class类,用来对类本身进行操作,通过Class可以完整的得到一个类的结构,例如:方法、属性...

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

    获取Class对象:

      Class类没有公共构造方法,创建Class对象,获取Class对象的三种方式:

    1. 通过Object类中的getClass()方法:对象名.getClass();

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

    3. 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可):Class.forName("完整包名.类名");

    注:第三种方式与前两种的区别:前两种必须明确类的类型,第三种是指定这种类型的字符串就行,这种扩展性更强,灵活性较强,不需要知道你的类,只提供字符串,按照配置文件加载就可以了。

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

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

    // 获取一个构造方法:
    // 获取public修饰,指定参数类型所对应的构造方法:
    public Constructor<T> getConstructor(Class<?> parameterTypes);  
    
    // 获取指定参数类型所对应的构造方法(包含私有的):
    public Consturctor<T> getDeclaredConstructor(Class<?> parameterTypes);
    
    // 返回多个构造方法:
    // 获取所有的public 修饰的构造方法:
    public Constructor<T>[] getConstructor();
    
    // 获取所有的构造方法(包含私有的):
    public Consturctor<T>[] getDeclaredConstructor();
    

    四、利用反射获得到的构造方法创建对象

    1. 获取到Class对象

    2. 获取指定的构造方法

    3. 通过构造方法类Constructor中的方法创建对象:public T newInstance(Object obj);

    /**
     * copyright(c)2021 zbh.ALL rights Reserved
     * <p>
     * 描述:利用反射获得到的构造方法创建对象
     *
     * @author zbh
     * @version 1.0
     * @date 2021/5/17
     */
    public class ConstructorDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InvocationTargetException {
            //1,获取到Class对象
            Class c = Class.forName("Person");//包名.类名
            //2,获取指定的构造方法
            //public Person()
            //Constructor con = c.getConstructor(null);
    
            //public Person(int id, String name, String address)
            Constructor con = c.getConstructor(int.class, String.class, String.class);
    
            //3,通过构造方法类中Constructor的方法,创建对象
            //Object obj = con.newInstance(null);
            Object obj = con.newInstance(22, "小明", "哈尔滨");
    
            //显示
            System.out.println(obj);// Person{id=22, name='小明', address='哈尔滨'}
        }
    }
    

    五、利用反射获得的私有构造方法创建对象

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

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

      在Constructor的父类java.lang.reflect.AccessibleObject的常用方法:public void setAccessible(boolean flag) throws SecurityException 的参数为true时,则指示反射的对象在使用时应该取消Java语言访问检查,参数值为false时,则指示反射的对象应该实施Java语言访问检查。

    获取私有构造方法并创建对象,步骤:

    1. 获取到Class对象。

    2. 获取指定的构造方法。

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

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

    六、通过反射获取属性(成员变量)并使用

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

    // 返回一个成员变量(属性)
    // 获取指定的 public修饰的变量
    public Field getField(String name);
    // 获取指定的任意变量
    public Field getDeclaredField(String name);
    
    // 返回多个成员变量(属性)
    // 获取所有public 修饰的变量(属性)
    public Field[] getFields();
    // 获取所有的 变量 (包含私有)
    public Field[] getDeclaredFields();
    

    获取成员变量及其赋值与获取值的步骤:

    1. 获取Class对象

    2. 获取构造方法

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

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

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

    • 在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值:public void set(Object obj, Object value)
    • 返回指定对象obj中,此 Field 对象表示的成员变量的值:public Object get(Object obj)
    /**
     * copyright(c)2021 zbh.ALL rights Reserved
     * <p>
     * 描述:通过反射获取属性(成员变量)并使用
     *
     * @author zbh
     * @version 1.0
     * @date 2021/5/17
     */
    public class FieldDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException, InvocationTargetException {
            //1,获取Class对象
            Class c = Class.forName("Person");
    
            //2,获取构造方法
            //public Person(String name)
            Constructor con = c.getConstructor(String.class);
    
            //3,通过构造方法,创建对象
            Object obj = con.newInstance("小明");
    
            //4,获取指定的成员变量
            //public String name;
            Field nameField = c.getField("name");
            //public int id;
            Field idField = c.getField("id");
            //private String address;
            Field addressField = c.getDeclaredField("address");
            addressField.setAccessible(true); //取消 Java 语言访问检查
    
            //5,通过方法,给指定对象的指定成员变量赋值或者获取值
            System.out.println("name = "+ nameField.get(obj));// name = 小明
            System.out.println("id = "+ idField.get(obj));// id = 0
            // 注:如果没有addressField.setAccessible(true);这行会报错
            System.out.println("address = "+ addressField.get(obj));// address = null
    
            //赋值
            idField.set(obj, 23);
            addressField.set(obj, "五一广场");
    
            System.out.println("------------------------");
            System.out.println("name = "+ nameField.get(obj));// name = 小明
            System.out.println("id = "+ idField.get(obj));// id = 23
            System.out.println("address = "+ addressField.get(obj));// address = 五一广场
        }
    }
    

    七、通过反射获取成员方法并调用

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

    // 1、返回获取一个方法:
    // 获取public 修饰的方法
    public Method getMethod(String name, Class<?> parameterTypes);
    // 获取任意的方法,包含私有的,默认的,protected修饰
    public Method getDeclaredMethod(String name, Class<?>parameterTypes);
    // 参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
    
    // 2、返回获取多个方法:
    // 获取本类与父类中所有public 修饰的方法
    public Method[] getMethods();
    // 获取本类中所有的方法(包含私有的)
    public Method[] getDeclaredMethods();
    
    // Method的常用方法:
       getName();//获取方法的名称
       getReturnType();//获取方法的返回类型
    

    获取成员方法步骤:

    1. 获取Class对象。

    2. 获取构造方法。

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

    4. 获取指定的方法。

    5. 执行找到的方法:public Object invoke(Object obj, Object args); 执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    /**
     * copyright(c)2021 zbh.ALL rights Reserved
     * <p>
     * 描述:通过反射获取成员方法并调用
     *
     * @author zbh
     * @version 1.0
     * @date 2021/5/17
     */
    public class MethodDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            //1, 获取Class对象
            Class c = Class.forName("Person");
    
            //2,获取构造方法
            //public Person(String name, int age, String address){
            Constructor con = c.getConstructor(int.class, String.class, String.class);
    
            //3,通过构造方法,创建对象
            Object obj = con.newInstance(23, "小明", "哈尔滨");
    
            //4,获取指定的方法
            //public void method1()  没有返回值没有参数的方法
            //Method m1 = c.getMethod("method1", null);
    
            //public String method4(String name)
            Method m4 = c.getMethod("method4", String.class);
    
            //5,执行找到的方法
            //m1.invoke(obj, null);
            Object result = m4.invoke(obj, "zhangsan");
            System.out.println("result = " + result);// zhangsan
    
            
            //4,获取指定的方法(访问private修饰符的方法)
            //private void method5(){
            Method m5 = c.getDeclaredMethod("method5", null);
            //5,开启暴力访问
            m5.setAccessible(true);
            //6,执行找到的方法
            m5.invoke(obj, null);
        }
    }
    

    八、反射的应用场景

    1. 需要获取Class对象,使用其方法

    2. 开发代码生成工具时

    3. 在框架中(如:Hibernate、Spring、Struts2)

    4. 泛型的擦除:在程序编译后产生的.class文件中是没有泛型约束的,因为在编译时,就将类型替换完成了,没有填写类型的默认是Object,这种现象我们称之为泛型的擦除。

    5. 利用反射读取配置文件:通过反射配置文件,运行配置文件中指定类的对应方法。Properties类在java.util包中读取Properties.txt文件中的数据,通过反射技术,来完成对象的创建。

    九、动态代理

    代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

    Java中的动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。

    在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,JDK提供的代理只能针对接口做代理。而我们又更强大的代理cglib

    Proxy类中的方法创建动态代理类对象:public static Object
    newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);

    Proxy类中创建动态代理对象的三个参数:

    • ClassLoader对象:定义了由哪个ClassLoader对象来对生成的代理对象进行加载

    • Interface对象的数组:表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。

    • InvocationHandler对象:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

    最终会调用InvocationHandler的方法:InvocationHandler Object invoke(Object proxy,Method method,Object[] args);

    InvocationHandler接口中invoke方法的三个参数:

    • proxy:代表动态代理对象

    • method:代表正在执行的方法

    • args:代表调用目标方法时传入的实参

      每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。

      Proxy.newProxyInstance:创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

  • 相关阅读:
    .Net/C# 应用程序直接读取本地 Cookies 文件(WinXP SP2 调用 API: InternetGetCookie 无果)
    wininet.dll函数库:不会过期的cookie
    WinForm中TextBox控件循环自动滚动示例
    JScript中Date.getTime转.Net中的DateTime
    js gettime c# ticks
    mysql查看整库个表详情
    rds分区实践
    mysql5.7.21源码安装
    EXPLAIN详解
    C#基础温习(4):C#中string数组和list的相互转换
  • 原文地址:https://www.cnblogs.com/zbh355376/p/14775843.html
Copyright © 2011-2022 走看看