Java反射第五部分:JDK动态代理中反射的灵活应用

转载请注明出处:https://blog.csdn.net/jiyisuifeng222/article/details/117710305

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 情花打雪 即可关注,每个工作日都有文章更新。

本篇文章主要讲解内容如下:

  • 向上转型和向下转型介绍
  • 类和接口的区别
  • 静态获取接口的实例:接口的实现类
  • 动态获取接口的实例:动态代理
  • 动态代理的原理

1.向上转型和向下转型介绍

父子对象之间的转换分为了向上转型向下转型,它们区别如下:

  • 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
  • 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换

1-1.向上转型

向上转型示例:

class Person {
    public void print() {
        System.out.println("person:print");
    }
}

class Lisi extends Person {
    public void print() {        
        System.out.println("lisi:print");
    }
}

public class TestDemo{
    public static void main(String args[]){
        Person p = new Lisi();//通过子类去实例化父类
        p.print();
    }
}

运行结果:lisi:print

可以看到运行结果的是class Lisi的print,这是因为我们通过子类Lisi去实例化的,所以父类Person的print方法已经被子类Lisi的print方法覆盖了.从而打印class Lisi的print.

注意:向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.

1-2.向下转型

在java中,向下转型是为了通过父类强制转换为子类,从而来调用子类特有的方法

为了保证向下转型的顺利完成,在java中提供了一个关键字:instanceof,通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,否则为false,

instanceof使用如下:

A a = new B();                 //向上转型 (B类是A的子类)
a instanceof A;                //返回true.
a instanceof B;                //返回true
a instanceof C;                //返回false

向下转型示例:

class A {
    public void print() {
        System.out.println("A:print");
    }
}

class B extends A {
    public void print() {
        System.out.println("B:print");
    }

    public void funcB() {
        System.out.println("funcB");
    }
}

class C extends A {
    public void print() {
        System.out.println("C:print");
    }

    public void funcC() {
        System.out.println("funcC");
    }
}

public class Test {
    public static void func(A a) {
        a.print();
        if (a instanceof B) {
            B b = (B) a;//向下转型,通过父类实例化子类
            b.funcB();//调用B类独有的方法
        } else if (a instanceof C) {
            C c = (C) a;//向下转型,通过父类实例化子类
            c.funcC();//调用C类独有的方法
        }
    }

    public static void main(String args[]) {
        func(new A());
        func(new B());
        func(new C());
    }
}

运行结果:

/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/bin/java/Users/JavaSeDemo/JavaSeDemo demo.TestJavase
A:print
B:print
funcB
C:print
funcC

通过向下转型来调用B类和C类独有的方法.

 

2.类和接口的区别

Javaclassinterface的区别:

  • 可以实例化class(非abstract类);
  • 不能实例化interface

所有interface类型的变量总是通过向上转型并指向某个实例的:

CharSequence cs = new StringBuilder();

3.静态获取接口的实例:接口的实现类


传统编码方式流程如下:
首先定义接口:

public interface Hello {
    void morning(String name);
}

然后编写实现类:

public class HelloImpl implements Hello {
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}

最后创建实例,转型为接口并调用:

Hello hello = new HelloImpl();
hello.morning("Bob");

4.动态获取接口的实例:动态代理


目的:(不人为编写实现类的前提下)为了获得接口的实例,并实现接口中的方法,并调用该方法。

public class Main {

    public static void main(String[] args) {
        //1.创建一个handler。它负责接口中的方法实现
        InvocationHandler handler = new InvocationHandler() {
            //invoke函数参数说明:代理对象;想调用的方法;方法的参数
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        //2.创建一个Proxy的instance。需要传入三个参数:接口的类加载器;要实现的接口;调用方法的handler
        Hello hello = (Hello) Proxy.newProxyInstance(
                   Hello.class.getClassLoader(), // 传入ClassLoader
                   new Class[]{Hello.class}, // 传入要实现的接口
        handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

在运行期动态创建一个interface实例的方法如下:

  • 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  • 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    ** 使用的ClassLoader,通常就是接口类的ClassLoader
    ** 需要实现的接口数组,至少需要传入一个接口进去;
    ** 用来处理接口方法调用的InvocationHandler实例。
  • 将返回的Object强制转型为接口。

动态代理实际上是JDK在运行期动态创建class字节码并加载的过程.
把上面的动态代理运行过程还原为静态实现类大概是这样:

public class HelloStaticImpl implements Hello {
    InvocationHandler handler;
    public HelloDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
    }

    public void morning(String name) {
        handler.invoke(//由handler去调用函数
           this,//Proxy对象
           Hello.class.getMethod("morning"),//方法名
           new Object[] { name });//方法参数
    }
}

通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用(invoke方法可能会调用多次,需要根据第二个参数Method判断业务做相应处理)

 

关注我的技术公众号,每天都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值