反射与动态代理

一、反射概述

1.1 反射是什么

​ 反射式 Java 被视为动态语言的关键,反射机制允许程序在执行期借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。

​ 加载完类之后,在堆内存的方法区中就产生了一个 Class 类型 的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的内部结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为反射

1.2 反射的作用

​ 1、在运行时判断任意一个对象所属的类

​ 2、在运行时构造任意一个类的对象

​ 3、在运行时判断任意一个类所具有的成员变量和方法

​ 4、在运行时调用任意一个对象的成员变量和方法

​ 5、在运行时生成注解

二、对 Class 类的理解

2.1 初步认识

​ Class 类是用来描述类的类,就是说 Class 类的对象就是其他类(而不是其他类的对象),如:

Person person = new Person();	//这是创建了一个 Person 对象,person 的类型是 Person

Class clazz = Class.forName("Person");	//这是创建了一个 Class 对象,Person.class 是把 Person 类加载到内存中,并没有创建 Person 的实例。

Person person2 = clazz.newInstance(); //使用 newInstance() 方法才创建了 Person 的实例。

2.2 深入理解

​ 1、Class 本身也是一个类(可以有 Class clazz = Class.class)的操作

​ 2、Class 对象只能由系统建立

​ 3、一个加载的类在 JVMA中只会有一个 Class 实例

  • 例:使用 javac.exe HelloWorld.java 生成一个 HelloWorld.c 文件,然后使用 java.exe HelloWorld 把 HelloWorld.c 文件加载到内存中,在 JVM 中就只有这一个 HelloWorld.c 。

​ 4、每个类的实例都会记得自己是由哪个 Class 实例生成。

​ 5、通过 Class 可以完整地得到一个类中所有被加载的结构。

​ 6、Class 类是反射的根源,针对任何想动态加载、运行的类,唯有先获得相应的 Class 对象。

三、反射与封装性的个人理解

3.1 反射和封装性是站在不同的层面的

​ 首先,分装性是对类的对象来说的,我们在类的外部创建了一个类的对象,那么通过这个对象不能访问 private的方法和属性,如下面代码,在类的外部创建了 Person 的对象 zhanSan,那么通过 zhangSan 是不可以访问到 private 属性的;

class Person {
	private String name;
    private int age;
    
    public String getName() {
        return this.name;
    }
    
    public int age getAge() {
        return this.age;
    }
}

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

​ 而反射创建的是 Class 的对象,而不是 Person 的对象,Class 是用来描述类的,它获得 Person 的方法与成员变量的方式是通过 Class 中的方法获得的,而不是通过 Person 的对象获得的。

public class ReflectTest {
    @Test
    public void test1() throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
        
        Class clazz = Class.forName("reflectTest.Person");
        Person p2 = (Person)clazz.newInstance();
        Field name = clazz.getDeclaredField("name");	//通过 Class 对象的方法来获得 Person 的私有属性
        name.setAccessible(true);
        name.set(p2, "liSi");   
    }
}

3.2 更深层次的理解封装性

​ 1、封装的意思不是禁止,而是不建议,如果一个类中声明了 private 的属性或方法,那么说明这个类会有更好用的 public 方法来提供对应的功能,我们只需要用 public 方法就可以了,private 声明的方法只在类内部有用,它是用来实现 public 方法的基础,这种方法在类的外部使用是没有意义的,就好比说:

​ 一辆汽车,对外提供的 public 方法是踩油门,踩油门可以实现汽车加速,为了实现这个public 方法汽车内部可能有 private 方法用来气缸加温,点火等,我们作为使用者只需要踩油门就可以了,知道怎么给气缸加温毫无意义。

​ 2、反射是可以访问私有成员和方法,但是我们并不建议这么做。

四、反射的应用——动态代理

4.1 代理设计模式的原理

​ 使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

​ 听起来比较抽象,举个例子来理解吧,比如说张三有一套房子要卖,那么他可以直接把自己的电话和房子信息放到网上,买家直接和张三进行对接,这种方式就是没有代理的;但是现在张三很忙,所以需要顾一个中介来帮张三,中介知道这个房子的信息以及张三这个房子想要卖多少钱,中介会负责与买家进行沟通,这就是使用了代理模式。

4.2 静态代理

​ 静态代理就是指定了被代理的类形(即接口)。

interface SellHouse {
    void sell();
}

class Customer implements SellHouse {
    private String name;

    public Customer(String name) {
        this.name = name;
    }

    @Override
    public void sell() {
        System.out.println("我是客户,我要卖房子");
    }
}

class HouseProxy implements SellHouse {
    private SellHousePeople customer;

    public HouseProxy(SellHousePeople customer) {
        this.customer = customer;
    }
    @Override
    public void sell() {
        System.out.println("我是房产中介,我帮人卖房子......");
        System.out.println("中介帮客户办手续,带领买家看房子......");
        customer.sell();
        System.out.println("中介卖完了房子。");
    }
}
public class StaticProxy {
    public static void main(String[] args) {
        SellHouse houseProxy = new HouseProxy(new Customer("张三"));
        houseProxy.sell();
    }
}

4.3 动态代理

4.1 动态代理的引入

​ 静态代理的逻辑清晰,代码易读,但它存在两个问题:

​ 1、编译期间就必须指定被代理的类,而我们在一个程序中可能有多个类需要被代理,那么就需要写多个代理类,这会使代码量过大,所以我们为了关注业务本身,在程序运行中创建代理对象则方便了很多。

​ 2、程序在可能在运行期间才确定要代理的类,那么这种情况下就必须使用动态代理了

4.2 动态代理的实现

​ 想要实现动态代理,首先要解决两个问题:

​ 1、如何根据被代理类动态地创建代理类

​ 2、如何通过调用代理类的方法来调用到被代理类的方法

​ 解决问题一:

class ProxyFactory {
    //该方法返回一个代理类对象
    public static Object getProxyInstance(Object obj) {
        //参数分别是被代理类,被代理类实习的接口,第三个参数 null 稍后再讨论
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), null);		//通过调用该方法,就能根据被代理类返回一个代理类的对象
    }
}

​ 解决问题二:

//在上述的代码上添加一些东西
class ProxyFactory {
    //该方法返回一个代理类对象
    public static Object getProxyInstance(Object obj) {
		//这两行代码实现通过调用代理类的方法来调用到被代理类的方法
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
		//这里把上面的 null 换成了 handler
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);	
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object obj;

    public void bind(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //returnValue 就是方法的返回值,注意:void 也是一种返回类型
        Object returnValue = method.invoke(obj, args);

        return returnValue;
    }
}

现在问题已经全部解决了,我们来演示一个实例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//定义接口:行驶
interface Driving {
    void drive();
}

//汽车实现行驶接口
class Car implements Driving {
    @Override
    public void drive() {
        System.out.println("燃烧汽油行驶");
    }
}

//定义接口:飞翔
interface Flying {
    String fly();
}

//鸟实现飞翔接口
class Bird implements Flying {

    @Override
    public String fly() {
        System.out.println("小鸟飞翔");
        return "小鸟飞翔";
    }
}

//代理类
class ProxyFactory {
    //该方法返回一个代理类对象
    public static Object getProxyInstance(Object obj) {

        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

//负责调用被代理类的方法
class MyInvocationHandler implements InvocationHandler {
    private Object obj;

    public void bind(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理负责做第一件事
        System.out.println("-----------------通用方法一-----------------");

        //调用函数的返回值类型
        Object returnValue = method.invoke(obj, args);

        //代理负责做第二件事
        System.out.println("-----------------通用方法二-----------------");

        return returnValue;
    }
}

public class DynamicProxy {
    public static void main(String[] args) {
        Car car = new Car();
        Driving carProxy = (Driving) ProxyFactory.getProxyInstance(car);
        carProxy.drive();

        System.out.println("\n-----------------------------------------------------\n");

        Bird bird = new Bird();
        Flying birdProxy = (Flying) ProxyFactory.getProxyInstance(bird);
        birdProxy.fly();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值