• Java反射以及动态代理(上)-- 套路


      在常用的各种框架中,反射与动态代理很常见,也很重要。本篇就对这一小节的内容基础性地总结。

      首先需要了解什么是类型信息,以及RTTI与反射的关系与区别。

      Java中,使用Class对象来表示所有类的对象。利用Class对象来获取类中的成员变量,构造函数以及方法,这些内容我们称之为类型信息。RTTI的含义是,在运行时识别一个对象的类型,但有一个前提,就是类型在编译时必须已知,这样才能用RTTI识别,并利用这些信息做一些有用的事情。但是如果在编译时,程序没有办法获知到这个对象所属的类,怎样才能使用这个类呢?答案是,我们可以在运行时,让JVM加载这个类的Class对象。由于没有经过编译期的检查,所以这里在使用之前,JVM也要简单地检查这个对象,看它属于哪个特定的类,而后加载这个类的Class对象。所以RTTI与反射的区别就在于,前者是在编译时打开和检查.class文件,运行时加载所需的类的Class对象,而后者则是在运行时打开和检查.class文件,并加载所需要类的Class对象。

      举个例子:

    class Test{
        public static void main(String[] args){
            new Dev();
        }
    }

    在对它进行编译时,如果包下没有Dev这个类,那么编译会报错:

    C:UsersBruceChanDesktop	est>javac Test.java
    Test.java:3: 错误: 找不到符号
                    new Dev();
                        ^
      符号:   类 Dev
      位置: 类 Test
    1 个错误

    但如果我们修改为反射的方式:

    class Test{
        public static void main(String[] args){
            try{
                Class.forName("Dev");
            }catch(ClassNotFoundException e){
                System.out.print(e.toString());
            }    
        }
    }

    对其进行编译时会发现,即便在没有Dev这个类存在的情况下,编译还是会顺利进行,因为它是在运行时,用到这个Dev类时,才会加载这个类。

    ---------------------------------------------------------------------------------->

      接下来,这里用代码的形式小节一下反射技术中,常用的套路。反射中,有三种方法可以获得类型的Class对象:

    try {
        Class cls1 = Class.forName("com.changjiang.test.RFP01.testReflect.MyClass");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    MyClass myClass
    = new MyClass(); Class cls2 = myClass.getClass(); Class cls3 = MyClass.class;

    通过该类的Class对象,可以获得它的属性,方法,构造函数,这里只说一下它的方法调用,首先给出原始类:

    package com.changjiang.test.RFP01.testReflect;
    
    import java.util.Date;
    
    public class MyClass {
        private int myInt;
        private String myString;
    
        public MyClass(){
    
        }
    
        public MyClass(int a){
            this.myInt = a;
            System.out.println(a);
        }
    
        public void Method2Void(){
    
        }
    
        public int Method2Int(){
            System.out.println("Method2Int has run");
            return 0;
        }
    
        public String Method2String(){
            return "";
        }
    
        public Object Method2Object() {
            return new Date();
        }
    
        public void Method3Param(int a, String b){
            System.out.println("Method3Param has run with param-a:" + a + " and param-b:" + b);
        }
    }

    再给出几个方法的调用以及输出:

    package com.changjiang.test.RFP01;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import com.changjiang.test.RFP01.testReflect.MyClass;
    
    public class Main {
        public static void main(String[] args) {
            MyClass myClass = new MyClass();
            Class cls = myClass.getClass();
            try{  
                 Method m = cls.getMethod("Method2Int", new Class[]{});//获取一个类的方法  
                 m.invoke(myClass, new Object[]{});//精髓所在,调用这个类的方法  
                 
                 Method m2 = cls.getMethod("Method3Param", new Class[]{int.class, String.class});  
                 m2.invoke(myClass, new Object[]{5, "fake"});//调用这个类的方法,带参数的  
                 
                 Constructor<MyClass> cons = cls.getConstructor(new Class[]{int.class});
                 cons.newInstance(47);
             }catch(Exception e){  
                 e.printStackTrace();  
             }  
             Method[] ms = cls.getMethods();  
             for(Method m:ms){  
                 System.out.print(m.getName()+"	");  
             }
        }
    }

    output:

    Method2Int has run
    Method3Param has run with param-a:5 and param-b:fake
    47
    Method2Int Method3Param Method2String Method2Object Method2Void wait wait wait equals toString hashCode getClass notify notifyAll

     ---------------------------------------------------------------------------------->

      OK,说到这里,我们该了解一下代理模式。清楚之后,我们再看动态代理。代理模式,简单地说,就是一个接口,两个实现。实现一中我们需要做一些事情,在实现二中,我们不仅要做实现一中的事情,而且可以在此之前与之后都添加我们想要做的别的事情。具体内容看看代码。

    package com.changjiang.test.RFP01.testReflect;
    
    public class SampleProxy {
        public static void main(String[] args) {
            new BaseSample1().doSomething();
            System.out.println("--------------------------------");
            new ProxySample(new BaseSample1()).doSomething();;
        }
    }
    
    interface BaseInterface{
        public void doSomething();
    }
    
    class BaseSample1 implements BaseInterface{
        public void doSomething() {
            System.out.println("BaseSample1");
        }
    }
    
    class ProxySample implements BaseInterface{
        private BaseInterface bi;
        public ProxySample(BaseInterface bi) {
            this.bi = bi;
        }
        public void doSomething() {
            System.out.println("before");
            bi.doSomething();
            System.out.println("after");
        }
    }

    这里的接口,被两个类实现,一个是原生类,另一个是代理类,而代理类中不但实现了这个接口,还需要将代理类通过构造器赋给自己的属性中,而后在接口实现方法中使用原始类的方法。

     ---------------------------------------------------------------------------------->

    接下来看看动态代理,主要是如何实现动态代理以及核心类Proxy与其方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InovationHandler h)中几个参数的详细含义与作用剖析。(下篇)

  • 相关阅读:
    Window10和Ubuntu 18.04双系统安装的引导问题解决
    Linux gcc链接动态库出错:LIBRARY_PATH和LD_LIBRARY_PATH的区别
    PaddleBook的部署安全性问题
    ubuntu docker中crontab任务不执行的问题
    zsh 自动补全导致命令显示重复
    For Freedom —— 代理篇
    搭建自己私有的PKM系统,各家PKM大比拼。。附:构建自己熟悉的基础Docker,破解联通光猫
    Docker上ubuntu新建用户的网络访问不通问题
    Android学习杂记
    jquery中的工具函数 Utilities
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/5895722.html
Copyright © 2020-2023  润新知