Java基础—反射与动态代理

Java基础—反射与动态代理,第1张

Java基础—反射与动态代理

反射与动态代理
    • 一、反射的概述
      • 1. 反射的概述
      • 2. 获取Class示例的三种常见方式
      • 3. Class类的理解
      • 4. 创建Class对应运行时类的对象的通用方法,代码实现。以及这样 *** 作,需要对应的运行时类构造器方面满足的要求。
    • 二、反射的应用:动态代理
      • 1. 代理模式与动态代理
      • 2. 静态代理举例
      • 3. 动态代理类举例


一、反射的概述 1. 反射的概述

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

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

public class Person {
    private String name;
    public int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }

    public void show(){
        System.out.println("你好,我是");
    }

    private String showNation(String nation){
        System.out.println("喷子实在太多了!!!" + nation);
        return nation;
    }
}
	//反射之后 ,堆与Person的 *** 作
    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
        //1.通过反射,创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Jon",18);
        Person p = (Person) obj;
        System.out.println(p.toString());
        //2.通过反射,调用对象指定的属性和方法
        //调用属性
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p.toString());

        //调用方法
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);

        System.out.println("+++++++++++++++++++++++++");

        //通过反射,是可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
        //调用私有的构造器
        Constructor cons2 = clazz.getDeclaredConstructor(String.class);
        cons2.setAccessible(true);
        Person p1 = (Person) cons2.newInstance("kalo");
        System.out.println(p1);

        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"Taoyao");
        System.out.println(p1);

        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("LiNin", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1,"FaceBook");
        //相当于String nation = p1.showNation("FaceBook")
        System.out.println(nation);
    }
2. 获取Class示例的三种常见方式
public class ReflectionTest {

    

    @Test
    public void test3() throws ClassNotFoundException {
        //方式一:编译期直接写死
        Class c1 = Person.class;
        System.out.println(c1);

        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class c2 = p1.getClass();
        System.out.println(c2);

        //方式三:调用Class的静态方法:forName(String classPath) (参数:类的全限定名。常用,体现反射的动态性)
        Class c3 = Class.forName("www.gh110.com");
		// c3 = Class.forName("www.123.com");
        System.out.println(c3);

        System.out.println(c1 == c2);
        System.out.println(c1 == c3);

        //方式四:使用类的加载器:ClassLoader(了解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class c4 = classLoader.loadClass("www.gh110.com");
        System.out.println(c4);

        System.out.println(c1 == c4);
    }
}
3. Class类的理解

Class的实例就对应着加载到内存中的一个运行时类。

哪些类型可以有Class对象?

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitivetype:基本数据类型
(7)void

4. 创建Class对应运行时类的对象的通用方法,代码实现。以及这样 *** 作,需要对应的运行时类构造器方面满足的要求。

Class clazz = Person.class;

Person p = (Person) clazz.newInstance();

要求:

  • 必须有空参的构造器
  • 权限修饰符的权限要够,通常设置为public
public class ReflectionTest {
    
    @Test
    public void testMethod() throws Exception {
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        //1.获取指定的某个方法
        //getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
        Method show = clazz.getDeclaredMethod("show", String.class);

        //2.保证当前方法是可访问的
        show.setAccessible(true);

        //3.调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        //invoke()的返回值即为对应类中调用的方法的返回值。
        Object returnValue = show.invoke(p,"CCA"); //String nation = p.show("CCA");
        System.out.println(returnValue);

        System.out.println("+++++++++如何调用静态方法+++++++++++");

//    private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDown");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
//    Object returnVal = showDesc.invoke(null);
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);//null
    }
}
二、反射的应用:动态代理 1. 代理模式与动态代理
  • 代理设计模式的原理:

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

  • 静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。

  • 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

动态代理相比于静态代理的优点:

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

2. 静态代理举例
interface ClothFactory{
    void produceCloth();
}

//代理类
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;//用被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory){
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂开始做一些准备工作");

        factory.produceCloth();
        
        System.out.println("代理工厂做一些后续收尾工作");
    }
}

//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("Nike工厂计划生产一批卫生纸");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类的对象
        ClothFactory nike = new NikeClothFactory();

        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();
    }
}
3. 动态代理类举例

使用 Proxy 类里面的方法创建代理对象

调用 newProxyInstance 方法,方法有三个参数:

public static Object newProxyInstance(ClassLoader loader,
                                   Class[] interfaces,
                                   InvocationHandler h)

第一参数,类加载器;

第二参数,增强方法所在的类实现的接口,支持多个接口;

第三参数,实现这个接口 InvocationHandler,创建代理对象,添加增强方法的部分

interface Human{
    String getBelief();
    void eat(String food);
}

//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I believe I can fly.";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}


class ProxyFactory{
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler hander = new MyInvocationHandler();
        hander.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hander);
    }
}

class MyInvocationHandler implements InvocationHandler{

    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj){
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()。解决问题二
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);
        
        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}

public class ProductTest {
    public static void main(String[] args) {
        SuperHuman superMan = new SuperHuman();
        //NumTest:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");
        
        System.out.println("*********************************");
        // 2中静态代理改为动态代理
        NikeClothFactory nike = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) BookTest.getProxyInstance(nike);

        proxyClothFactory.produceCloth();
    }
}

参考:

  • 十七:反射与动态代理

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5696701.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存