JAVASE进阶:反射与动态代理

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:JAVASE进阶:网络编程(编程实现TCP、UDP传输)
📚订阅专栏:JAVASE进阶
希望文章对你们有所帮助

前段时间都在做别人课设,现在终于有时间继续学习新技术、新知识了。

这部分内容在学习java设计模式的时候肯定都讲过,做课程作业的时候也是自己编码实现过的,属于很重要很重要的内容。

反射

概述

官方描述的反射:反射允许对封装类的字段,方法和构造函数的信息进行编程访问,实际上就是允许对成员变量、成员方法、构造方法的信息进行编程访问。

当我们想查看一个对象的成员变量或者成员方法时,利用**对象.**的方式,idea即可弹出所有的可以获取的变量和可以调用的方法;当我们使用构造函数创建对象时,可能会忘记需要的形参,这时候我们使用Ctrl+P即可查看需要的形参。这两种都是反射的体现,说白了就是把一个类中的所有东西都取出来做操作

但为什么不使用IO流来取出所有的内容呢?其实很容易想到,java中的多态使得我们难以用IO流区分不同的函数,而如果成员变量和局部变量的类型和名称都相同,IO流显然也难以识别出来。
而反射就可以很好的获取出成员变量、构造方法以及成员方法,还很好的对其结构进行解刨

1、字段(成员变量):获取修饰符、获取名字、获取类型、赋值/获取值
2、构造方法:获取修饰符、获取名字、获取形参、创建对象
3、成员方法:获取修饰符、获取名字、获取形参、获取返回值、抛出的异常、获取注解、运行方法

所以对于反射的学习,先要学习如何获取,再学习如何解刨,这里的获取并不是直接获取.java文件,而是获取其字节码文件.class。

获取class对象的三种方式

1、Class.forName(“全类名”);
2、类名.class
3、对象.getClass

这三种方式都可以获取,那么其什么时候使用呢?这就关乎到java文件执行的全流程了:

1、源代码阶段:Class.forName("全类名");,将.java文件编译成.class文件,这部分操作是在硬盘执行的,并没有加载到内存,因此.java和.class都是源代码阶段
2、加载阶段:类名.class,加载阶段将.class字节码文件加载到内存中
3、运行阶段:对象.getClass,运行阶段会在内存中创建对象,使用该方法即可获取class对象。

public class MyReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 方式一:Class.forName(全类名)
         * 全类名:报名+类名 com.wang.MyReflect.Student
         * 最为常用
         */
        Class clazz1 = Class.forName("com.wang.MyReflect.Student");

        /**
         * 方式二:类名.class
         * 常用来做参数传递,例如synchronized中的参数
         */
        Class clazz2 = Student.class;

        /**
         * 方式三:对象.getClass
         * 比较少用
         */
        Student student = new Student();
        Class clazz3 = student.getClass();

        System.out.println(clazz1 == clazz2 && clazz1 == clazz3);
    }
}

反射获取构造方法

java之中,万物皆对象,既然是对象,那就有对应的类:

构造方法:Constructor
字段(成员变量):Field
成员方法:Method

Class类中用于获取构造方法的方法:

方法作用
Constructor[] getConstructors()返回所有公共构造方法对象的数组
Constructor[] getDeclaredConstructors()返回所有构造方法对象的数组
Constructor getConstructor(parameterTypes)传递参数类型,返回单个公共构造方法对象
Constructor getDeclaredConstructor(parameterTypes)传递参数类型,返回单个构造方法对象

Constructor类中用于创建对象的方法:

方法作用
newInstance(形参)根据指定的构造方法来创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查(若构造方法是私有的)
getModifiers()获取构造方法的权限,底层会使用,一般不用
getParameter()获取构造方法的形参,底层会使用,一般不用

所以,我们在使用new 类名() 的时候,会弹出一系列的构造方法,但是private的构造方法却不会弹出,这是因为底层用getModifiers()方法做了判定,同理,当我们Ctrl+P时,不会弹出被private修饰符修饰的构造方法里面的参数。

反射获取成员变量

Class类中用于获取成员变量的方法,获取成员变量的对象,只需要指定变量名就好,这么设计是因为变量名肯定是唯一的:

方法说明
Field[] getFields()返回所有公共成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredField(String name)返回单个成员变量对象

Field类中用于创建对象的方法:

方法说明
void set(Object obj,Object value)赋值
Object get(Object obj)获取值

反射获取成员方法

Class类中用于获取成员方法的方法,需要注意,获取成员方法的对象,不仅要指定方法名,还需要指定参数类型(方法的重载):

方法说明
Method[] getMethods()返回所有公共成员方法对象的数组
Method[] getDeclaredFields()返回所有成员方法对象的数组
Method getMethod(String name, Class parameterTypes)返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class parameterTypes)返回单个成员方法对象

Method类中用于创建对象的方法:

Object invoke(Object obj, Object… args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(若没有则不写)
返回值:方法的返回值(若没有则不写)
invoke配合setAccessible即可调用private修饰的方法

动态代理

动态代理属于是javase中的难点,但是是很有必要攻克的。
学习过设计模式都知道,当我们要给系统增加功能的时候,不应该直接修改源代码,即不应该侵入式修改,这就是开闭原则,动态代理就是用来解决这个问题的。

思想分析

为了不直接修改源代码,我们需要一个代理,可以理解为中介,它可以实现无侵入式的给代码增加额外的功能。
举一个例子,坤坤可以执行唱歌和跳舞两个方法。在唱歌方法中,唱歌前需要准备话筒;在跳舞方法中,跳舞前需要准备场地。事先的准备肯定不是坤坤本人去干的,而是由相关的负责人来进行的,这个负责人就称为代理。
也就是说,对象如果有很多的动作,那么就可以通过代理来转移部分的职责

这需要负责人本身也拥有唱歌和跳舞两个方法:

唱歌(){
	准备话筒
	调用坤坤的唱歌函数,找坤坤唱歌
}
跳舞(){
	准备场地
	调用坤坤的跳舞函数,找坤坤跳舞
}

也就是说,对象有什么方法想被代理,代理就一定要有对应的方法
但是负责人是怎么知道要调用的是坤坤的唱歌方法或者跳舞方法,而不是坤坤的打篮球方法呢?这就需要一个接口:

public interface Star{
	sing方法
	dance方法
}

接下来,负责人和坤坤都需要实现这个接口。
也就是说,代理和实现类对象都需要实现公共接口

总结:

1、为什么需要代理:代理可以无侵入式的给对象增强其他的功能
2、代理长什么样:代理里面就是对象要被代理的方法
3、java通过什么来保证代理的样子:通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法

代码实现

1、定义接口:

public interface Star {
    public abstract String sing(String name);
    public abstract void dance();
}

2、创建实现类对象:

public class Kunkun {
    private String name;

    public Kunkun() {
    }

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

    //唱歌 跳舞
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "一个真正的man";
    }

    public void dance(String name){
        System.out.println(this.name + "正在跳" + name);
    }

    public String getName() {
        return name;
    }

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

3、定义代理:

/**
 * 创建一个代理
 */
public class ProxyUtil {
    /**
     * 作用:给一个对象创建一个代理
     * 形参:被代理的对象
     * 返回值:给被代理对象创建的代理
     */
    public static Star createProxy(Kunkun kunkun) {
        /**
         * java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法
         * public static Object newProxyInstance(ClassLoader loader, Class<>[] interfaces, InvocationHandler h)
         * 参数一:用于指定用哪个类加载器,去加载生成的代理类
         * 参数二:指定接口,这些接口用于指定生成的代理,包括需要的方法
         * 参数三:指定生成的代理对象要做什么事情
         */
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//类加载器,表示"把当前对象(要做动态代理的对象)的字节码文件加载到内存"
                new Class[]{Star.class},
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /**
                         * 参数一:代理的对象
                         * 参数二:要运行的方法
                         * 参数三:调用方法时,传递的实参
                         */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地");
                        }
                        //找被代理对象做相应的职责,使用反射
                        return method.invoke(kunkun, args);
                    }
                }
        );
        return star;
    }
}

4、测试类:

public class Test {
    public static void main(String[] args) {
        //获取代理对象
        Kunkun kunkun = new Kunkun("坤坤");
        Star proxy = ProxyUtil.createProxy(kunkun);
        //调用唱歌的方法
        String result = proxy.sing("鸡你太美");
        System.out.println(result);
    }
}
  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值