• JAVA反射


    一. 什么是JAVA反射

    java反射机制是在程序运行状态中,对于任何一个类都能够知道这个类的属性和方法。对于任何一个对象都能够调用它的任意一个方法。Java反射机制运行程序判断分析任何一个类的结构,包括成员方法和变量,并调用任意一个对象的方法。

    二. JAVA反射的实例

    在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中

    Class类:代表一个类,位于java.lang包下。

    Field类:代表类的成员变量(成员变量也称为类的属性)。

    Method类:代表类的方法。

    Constructor类:代表类的构造方法。

    Class类

    Class类是用来保存运行时类型信息的类。每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。一般当某个类的Class对象被载入内存时,它就可以用来创建这个类的所有对象。

    常用的获取Class对象的3种方式:

    1.使用Class类的静态方法。例如:  

    Class.forName("java.lang.String");

    2.使用类的.class语法。如:

    String.class;

    3.使用对象的getClass()方法。如:

    String str = "aa";
    Class<?> classType1 = str.getClass();

    Field类

    再来看看通过class对象来获取类的属性

    private String strName;
        private int sum;
        public String strTest;
          
        public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
              
            ReflectTest rTest=new ReflectTest();
            Class<?> clsType = ReflectTest.class;
            //获得当前类和父类中的public类型的所有属性
            Field[] fields=clsType.getFields();
            for(Field field:fields)
                System.out.println(field);
        }

    通过反射来设置属性值

    public class ReflectTest {
          
        private String strName;
        private int sum;
        public String strTest;
          
        public void show(){
            System.out.println(strName);
            System.out.println(""+sum);
            System.out.println(strTest);
        }
          
        public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
              
            ReflectTest rTest=new ReflectTest();
            Class<?> clsType = ReflectTest.class;
            //获取三个属性值
            Field field1=clsType.getDeclaredField("strName");
            Field field2=clsType.getDeclaredField("sum");
            Field field3=clsType.getField("strTest");
            //反射设置对象的属性值
            field1.set(rTest, "james");
            field2.set(rTest, 10);
            field3.set(rTest, "reflect field");
              
            rTest.show();
        }
    }

    Method类

    在获取Class对象后,我们还可以获取类所对应的方法,这时就要用到我们的Method类

    public class ReflectTest {
      
        private void fun() {
            System.out.println("this is fun()");
        }
      
        private void add(int a, int b) {
            System.out.println("the sum is: " + (a + b) + " ");
        }
      
        public static void main(String[] args) {
            Class<?> clsType = ReflectTest.class;
            // 返回class对象所对应的类或接口中,所声明的所有方法的数组(包括私有方法)
            Method[] methods = clsType.getDeclaredMethods();
            // 打印出所有的方法名
            for (Method method : methods) {
                System.out.println(method);
            }
        }
    }

    再来看看怎么反射调用对象的方法

    public class ReflectTest {
      
        private void fun() {
            System.out.println("this is fun()");
        }
      
        public int add(int a, int b) {
            System.out.println("the sum is: " + (a + b) + " ");
            return a+b;
        }
      
        public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            ReflectTest rTest=new ReflectTest();
            Class<?> clsType = ReflectTest.class;
            // 返回对应public类型的方法,第一个参数为方法名,第二个参数为方法的参数列表
            Method method=clsType.getMethod("add"new Class<?>[]{int.class,int.class});
            System.out.println(""+method.invoke(rTest, new Object[]{5,6}));
        }
    }

    Constructor类

    在获取Class对象后,我们就可以通过它创建类的对象啦。这就地用到我们的Constructor类啦。有三种创建对象的方式:

    1.先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:

    Class<?> classType = StringBuffer.class;

    Object obj = classType.newInstance();

    2.当然也可以再获得Class对象后,通过该Class对象获得类的Constructor对象,再通过该Constructor对象的newInstance()方法创建对象

    Class<?> clsType=StringBuffer.class;

    // 获得Constructor对象,此处获取一个无参数的构造方法的
    Constructor<?> constructor=clsType.getConstructor(new Class<?>[]{});

    // 通过Constructor对象的构造方法来生成一个对象
    Object obj=constructor.newInstance(new Object[]{});

    3、如果构造器带有参数,那就不能有上面两个方法来创建对象了,可使用下面这一种方式:

    Class<?> clsType = StringBuffer.class;

    Constructor<?> constructor = clsType.getConstructor(new Class<?>[] { String.class });

    Object obj = constructor.newInstance(new Object[] { "hello, classtest" });

    三. JAVA反射的原理

     Class.forName(classname)的执行过程:

    实际上是调用了Class类中的 Class.forName(classname, true, currentLoader)方法。参数:name - 所需类的完全限定名;initialize - 是否必须初始化类;loader - 用于加载类的类加载器。currentLoader则是通过调用ClassLoader.getCallerClassLoader()获取当前类加载器的。类要想使用,必须用类加载器加载,所以需要加载器。反射机制,不是每次都去重新反射,而是提供了cache,每次都会需要类加载器去自己的cache中查找,如果可以查到,则直接返回该类。

    java的类加载器,它分为BootStrap Class Loader(引导类加载器),Extensions Class Loader (扩展类加载器),App ClassLoader(或System Class Loader),当然少不了Custom ClassLoader(用户自定义类加载器)。其加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

    流程图:

    类加载器的类加载过程,先检查自己是否已经加载过该类,如果加载过,则直接返回该类,若没有则调用父类的loadClass方法,如果父类中没有,则执行findClass方法去尝试加载此类,也就是我们通常所理解的片面的"反射"了。这个过程主要通过ClassLoader.defineClass方法来完成。defineClass 方法将一个字节数组转换为 Class 类的实例(任何类的对象都是Class类的对象)。这种新定义的类的实例需要使用 Class.newInstance 来创建,而不能使用new来实例化。

    在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。

    四. JAVA反射的应用-动态代理

    说动态代理之前,必须先讲讲代理的设计模式。代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式的有三个角色定义:

    1)抽象主题接口(Subject)
    声明真实对象和代理对象的共同接口。代理模式中,代理类和真实对象都实现了接口。
    2)真实主题类(RealSubject)
    真正实现业务逻辑的类。
    3)代理类(Proxy)
    用来代理和封装真实主题。代理对象角色内部含有对真实对象的引用,从而可以操作真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy),
    缺点:1. 如果需要为不同的真实主题类提供代理类,都需要增加新的代理类,这将导致系统中的类个数急剧增加。
               2. 静态代理类和真实主题类实现了相同的接口,代理类通过真实主题类实现了相同的方法,如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。

    这就需要使用我们的动态代理了。

    动态代理(Dynamic Proxy)在系统运行时动态创建代理类,可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类。动态代理是一种较为高级的代理模式,它在事务管理、AOP(Aspect-OrientedProgramming,面向方面编程)等领域都发挥了重要的作用。Java语言提供了对动态代理的支持,在Java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler接口、另一个则是 Proxy类。Proxy这个类的作用就是用来动态创建一个代理对象,通常使用newProxyInstance 方法创建代理对象。Proxy代理类动态创建代理对象都需要关联到一个InvocationHandler接口,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke方法来进行调用。

    动态代理的步骤
    (1).创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    (2).创建被代理的类以及接口
    (3).通过Proxy的静态方法
    newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
    (4).通过代理调用方法

    类图

    Dynamic Proxy
    1)抽象接口
    public interface Subject
    {
        public void doSomething(String str);
    }
    2)真实对象
    public class RealSubject implements Subject
    {
      
        @Override
        public void doSomething(String str)
        {
            // TODO Auto-generated method stub
            System.out.println("do something: " + str);
        }
    }
          
    3)动态代理Handler类
    public class MyInvocationHandler implements InvocationHandler
    {
        //被代理的对象
        private Object target=null;
       
        public MyInvocationHandler(Object obj)
        {
            this.target=obj;
        }
       
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // TODO Auto-generated method stub
            return method.invoke(target, args);
        }
    }
      
      
    4)场景类Client
    public class Client
    {
        public static void main(String[] args)
        {
            //real object
            Subject subject=new RealSubject();
            //handler
            InvocationHandler handler=new MyInvocationHandler(subject);
            //获得代理proxy
            Subject proxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
       
            System.out.println(proxy.getClass().getName());
            proxy.doSomething("yes");
     }
    }
     
    哔哔叭哔哄
  • 相关阅读:
    swift优秀学习博客
    Swift主题色顶级解决方案一
    如何用 Swift 语言构建一个自定控件
    自定义UISlider的样式和滑块
    让你提前知道软件开发(27):创建数据库表和索引
    [6] 算法路
    OGG &quot;Loading data from file to Replicat&quot;table静态数据同步配置过程
    一些书籍和下载链接地址读研究生
    悖论软件测试农药
    Maven直接部署Web应用Tomcat
  • 原文地址:https://www.cnblogs.com/he-px/p/7448271.html
Copyright © 2020-2023  润新知