有关Java反射机制的作用及用法浅析

本文欢迎转载,转载请注明出处,谢谢! http://blog.csdn.net/colton_null 作者:喝酒不骑马 Colton_Null from CSDN


前言

一直想要研究一下Java的反射机制。但是一直以来没有时间好好研究反射的来龙去脉。其实如果没有对于反射用法的需求,而强行想学习反射的话是很难理解它的意义的。

前一段在工作中,我碰到了一个惹人烦的功能。当时用的是Jfinal框架的Model类对数据库进行持久化操作。同样的界面,分10种情况,分别对应十个表。也就是说在Java中对应十个Model类。

假设每个Model类中都有一个queryInfo()这个方法,十种情况分别要调用十次queryInfo()这个方法。
比如:

Model1 model1 = new Model1();
model1  = model1.queryInfo();

Model2 model2 = new Model2();
model2 = model2.queryInfo();
...
Model10 model10 = new Model3();
model10  = model10.queryInfo();

如果其中业务有变动,那就要同时改十个地方。当时对反射还不是很懂,所以就很笨拙的写了十套差不多的代码。后来想用反射重写一下,一直也没有时间和机会。再后来就不在那个项目组了……(也不知道那个项目现在如何了,若是谁维护时发现了我那段青涩的代码,怕是要骂死我吧)

本文记录了最近一段时间学习反射的一些理解,研究还不够透彻,文笔粗略,各位看官请多包涵。

如何理解Java反射?

查阅了很多关于反射的资料,发现大多blog或者资源上都偏重于如何使用反射,但是对反射的意义描述却很少。大部分都停留在“反射允许我们在运行时发现和使用类的信息”层面上,这种概念如果硬生生理解的话很难琢磨透。

我的理解是,Java反射可以允许开发人员以字符串形式传入类名、方法名等,就可以生成该类或者调用某个方法。这样会使代码及其灵活,利于维护。

还是利用我上面说到的那个例子。如果通过用户每次进入不同类型的界面时,根据界面类别传入”Model1”、”Model2”等这样的字符串,那么我就可以直接用反射机制实例化该类并调用其中queryInfo()方法。这样,之前十套代码的工作量就可以浓缩到一套代码中完成了。如果要修改queryInfo相关业务,那么只需要修改一处即可。

举个栗子

或许上面的表述还是不好理解。这里我准备了一个例子,更加方便理解反射的用法和意义所在。

新建如下4个类,Animals.java,Dog.java, Cat.java, Monkey.java。其中Dog, Cat, Monkey类继承Animals。

Animals.java

public abstract class Animals {

    //动物名字
    public String name;

    /**
     * 叫一叫
     */
    public abstract void makeNoise();
}

Cat.java

public class Cat extends Animals {

    public Cat(){}

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

    @Override
    public void makeNoise() {
        System.out.println(name + "可以 喵喵喵");
    }
}

Dog.java

public class Dog extends Animals {

    public Dog(){}

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

    @Override
    public void makeNoise() {
        System.out.println(name + "可以 汪汪汪");
    }
}

Monkey.java

public class Monkey extends Animals {

    public Monkey(){}

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

    @Override
    public void makeNoise() {
        System.out.println(name + "可以 嗷嗷嗷");
    }
}

再创建一个测试类TestDemo.java

public class TestDemo {

    // 这里要填写类的全路径
    private static String[] classNames = new String[]{"myz.com.myz_20170907.test1.Monkey",
            "myz.com.myz_20170907.test1.Dog",
            "myz.com.myz_20170907.test1.Cat"};

    public static void main(String[] args) {
        try {
            // 通过遍历classNames数组,分别实例化3个类并执行makeNoise()方法
            for (String className : classNames){
                Class clazz = Class.forName(className);
                Method method = clazz.getDeclaredMethod("makeNoise");
                Constructor constructor = clazz.getDeclaredConstructor(new Class[]{String.class});

                // 执行makeNoise方法
                method.invoke(constructor.newInstance(new Object[]{className.split("\\.")[className.split("\\.").length - 1]}));// 按照"."分割字符串取最后一段
            }
        } catch (ClassNotFoundException e) {
            System.out.println("生成Class失败!");
        } catch (NoSuchMethodException e) {
            System.out.println("无此方法");
        }
        catch (IllegalAccessException e) {
            System.out.println("生成对象失败!IllegalAccessException");
        } catch (InstantiationException e) {
            System.out.println("生成对象失败!InstantiationException");
        } catch (InvocationTargetException e) {
            System.out.println("方法调用失败!");
        }
    }
}

这里,我通过遍历包含三个类名全路径的数组,动态的实例化三个类,并分别执行makeNoise()方法。最终得到的结果为

输出:
Monkey可以 嗷嗷嗷
Dog可以 汪汪汪
Cat可以 喵喵喵

也就是说,现在有三种动物,如果我再添加10种动物,只需将这十种动物的类名传入,就可以实例化相应的对象了。如果按照传统做法,分别需要new出来10个对象,再分别调用makeNoise方法。如果是100种动物呢?传统做法的弊端就显露出来了。而利用反射,我无需在编译的时候创建那么多对象,只需要传入相应的类名,就可以在运行时生成想要创建的对象。

这也就是对于反射概念中提到的“程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载”。用户选择哪个动物在编译时是完全不知道的,运行起来后用户才会有选择动物的操作。那么不论用户选择哪种动物,通过反射可以在程序运行时动态生成和使用相对应的对象,这就是反射的意义。

最后多说几句

正是由于有了反射机制,才有的Spring、Mybatis等各种优秀的框架。Spring的核心IOC(控制反转)的底层实现跟反射机制是密不可分。最近博主也在研究Spring的原理,有时间的话也会分享一下学习Spring原理的心得。

这里还要简单提一下Annotation注解。注解也是和反射密不可分的。通过反射,可以获取到某个类、某个参数、属性、方法等上的注解的信息,这样可以根据注解信息进行相关的操作。可以理解为,注解即为一个标识,遇到什么标识就该做什么标识的事。

以上就是我对Java反射机制的浅析,文笔粗略,请多包涵。如果有理解有误的地方,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值