使用JAVA反射技术实现代码零耦合与功能无限扩展!

1、反射使用的背景

  最近在做一个功能,就是实现邮件发送功能,但是邮件发送有不同的内容和数据格式,在开始设计的时候直接将发送的内容写在了发送模块中,后来发现功能增加后,无法继续在里边写了,因为里边的功能已经固定住了,只好重新添加一个发送模块 ,这样就相当于维护了两套代码,发送功能和发送格式配置代码耦合太严重,一直想着重构这个功能,后来有时间了,开始考虑重构了。

  一直在想我的发送功能是固定的,如何把发送的不同内容和格式抽取取来呢,以后添加新内容,只需把样式和格式的类写好,发送模块会自动匹配要发送的内容呢;一开始想到用多态,父类去调用子类的功能,但是发现一个问题,就是每次调用的时候需要通过 Fu f = new Zi() 这种模式进行创建对象,但是在发送模块不能确定使用哪个子类去创建,在不改变代码的情况下无法做到new Zi()的动态化;

  后来相当了,如果我每次发送不同功能的时候,可以读取配置文件来确定使用哪个类进行调用,然后发送这个类的内容和格式,这时候突然想到了使用反射技术,在发送模块我写成反射模式,反射的时候调用的通过读取配置文件来确定所要调用的类和方法,每次添加了新功能,我只要设置配置文件,那么反射的代码可以更具配置去使用该类,然后调用其方法,完全做到了发送模块与内容格式的分离,不需要再去维护发送模块了。nice!!!

反射技术是实现各大框架的重要技术之一!

2、过程描述

从图中看出 反射地方可以根据配置文件自动的实现调用不同的功能,所以说 以后当我们新增功能的时候,我们只需要写好对应的类以及对应配置文件,那么就会自动调用新增代码了;

 3、反射技术的原理

 3.1、获取class的方式 通用方式

Class car = Class.forName("com.gxy.src.Car");   //注意此字符串必须是真实路径,就是带包名的类路径,包名.类名

 3.2、获取class的构造函数

1.获取构造方法:

  1).批量的方法: public Constructor[] getConstructors():所有"公有的"构造方法             public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

Constructor[] con = car .getDeclaredConstructors();   2).获取单个的方法,并调用: public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法: public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有; Constructor con = clazz.getConstructor(null);

Constructor con = clazz.getConstructor(Char.class);

  调用构造方法:

Constructor-->newInstance(Object... initargs)

  Object obj = con.newInstance();

  Object obj = con.newInstance('r');

2、newInstance是 Constructor类的方法(管理构造函数的类)

api的解释为:

newInstance(Object... initargs)            使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

 3.3、获取class的方法 通过代码来看

//获取类中所有的方法。
        public static void method_1() throws Exception {
            Class clazz = Class.forName("com.makaruina.reflect.Person");
            //获取的是该类中所有的公有方法,包含继承和实现的方法。
            Method[] methods = clazz.getMethods();
            //获取的是该类中的所有方法,包含私有方法,但不包含继承的方法。
            methods = clazz.getDeclaredMethods();
            for(Method method : methods) {
                System.out.println(method);
            }
        }
        //获取指定方法;
        public static void method_2() throws Exception {
            Class clazz = Class.forName("com.makaruina.reflect.Person");
            //获取指定名称的方法。
            Method method = clazz.getMethod("show", int.class,String.class);
            //想要运行指定方法,当然是方法对象最清楚,为了让方法运行,调用方法对象的invoke方法即可,但是方法运行必须要明确所属的对象和具体的实际参数。
            Object obj = clazz.newInstance();
            method.invoke(obj, 39,"hehehe");//执行一个方法
        }
        //想要运行私有方法。
        public static void method_3() throws Exception {
            Class clazz = Class.forName("com.makaruina.reflect.Person");
            //想要获取私有方法。必须用getDeclearMethod();
            Method method = clazz.getDeclaredMethod("method", null);
            // 私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。
            method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。
        }
        //反射静态方法。
        public static void method_4() throws Exception {
            Class clazz = Class.forName("com.makaruina.reflect.Person");
            Method method = clazz.getMethod("function",null);
            method.invoke(null,null);
        }

 3.3、获取class的属性 通过代码来看

* 获取成员变量并调用:
 * 
 * 1.批量的
 *         1).Field[] getFields():获取所有的"公有字段"
 *         2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
 * 2.获取单个的:
 *         1).public Field getField(String fieldName):获取某个"公有的"字段;
 *         2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
 * 
 *      设置字段的值:
 *         Field --> public void set(Object obj,Object value):
 *                     参数说明:
 *                     1.obj:要设置的字段所在的对象;
 *                     2.value:要为字段设置的值;

 3.4、获取main方法 通过代码来看

try {
            //1、获取Student对象的字节码
            Class clazz = Class.forName("fanshe.main.Student");
            
            //2、获取main方法
             Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
            //3、调用main方法
            // methodMain.invoke(null, new String[]{"a","b","c"});
             //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
             //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
            // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
            
        } catch (Exception e) {
            e.printStackTrace();
        }

4、通过反射可以越过泛型检查

 为什么泛型可以越过泛型检查  ---  泛型用在编译期,编译过后泛型擦除(自动失效)。

           举例:

ArrayList<String> strList = new ArrayList<>();
        strList.add("aaa");
        strList.add("bbb");
        
    //    strList.add(100);
        //获取ArrayList的Class对象,反向的调用add()方法,添加数据
        Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
        //获取add()方法
        Method m = listClass.getMethod("add", Object.class);
        //调用add()方法
        m.invoke(strList, 100);
        
        //遍历集合
        for(Object obj : strList){
            System.out.println(obj);
        }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-11总结Eclipse使用,API,Object类

1:Eclipse的概述使用(掌握) 1:Eclipse的安装 2:用Eclipse写一个HelloWorld案例,最终在控制台输出你的名字 A:创建项目 ...

3606
来自专栏CreateAMind

coach运行流程梳理

962
来自专栏我爱编程

Day12面向对象高级编程3/3

使用元类 type() class Hello(object): def hello(self,name='World!'): prin...

3547
来自专栏desperate633

深入理解SortSet类型的使用及应用Redis 有序集合(sorted set)SortSet的应用场景SortSet的常用命令

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。

3032
来自专栏C/C++基础

关于函数参数入栈的思考(函数调用约定,入栈顺序)

首先,要实现函数调用,除了要知道函数的入口地址外,还要向函数传递合适的参数。向被调函数传递参数,可以有不同的方式实现。这些方式被称为“调用规范”或“调用约定”。...

963
来自专栏nnngu

014 Java的反射机制

这篇文章要总结java的反射机制,将从以下几点进行总结: 一、什么是反射机制 二、哪里用到反射机制 三、反射机制的优点与缺点 四、利用反射机制能获得什么信息 五...

2553
来自专栏LuckQI

学习Java基础知识,打通面试关七

931
来自专栏小勇DW3

设计模式--代理模式(附源码分析)

 在平时的开发过程中,我们实现方法的调用往往只是普通的对象调用方法,实现复杂的业务就是一层一层的对象调用方法依次进行实现,但是如果我要实现在某些方法执行前或者...

2383
来自专栏Python爱好者

Python高效编程(三)

1315
来自专栏Spark学习技巧

Java动态代理原理及解析

代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及...

4405

扫码关注云+社区

领取腾讯云代金券