有关Java反射机制的作用及用法浅析
2017-09-08 01:04
357 查看
本文欢迎转载,转载请注明出处,谢谢! http://blog.csdn.net/colton_null 作者:喝酒不骑马 Colton_Null from CSDN
前一段在工作中,我碰到了一个惹人烦的功能。当时用的是Jfinal框架的Model类对数据库进行持久化操作。同样的界面,分10种情况,分别对应十个表。也就是说在Java中对应十个Model类。
假设每个Model类中都有一个queryInfo()这个方法,十种情况分别要调用十次queryInfo()这个方法。
比如:
如果其中业务有变动,那就要同时改十个地方。当时对反射还不是很懂,所以就很笨拙的写了十套差不多的代码。后来想用反射重写一下,一直也没有时间和机会。再后来就不在那个项目组了……(也不知道那个项目现在如何了,若是谁维护时发现了我那段青涩的代码,怕是要骂死我吧)
本文记录了最近一段时间学习反射的一些理解,研究还不够透彻,文笔粗略,各位看官请多包涵。
我的理解是,Java反射可以允许开发人员以字符串形式传入类名、方法名等,就可以生成该类或者调用某个方法。这样会使代码及其灵活,利于维护。
还是利用我上面说到的那个例子。如果通过用户每次进入不同类型的界面时,根据界面类别传入”Model1”、”Model2”等这样的字符串,那么我就可以直接用反射机制实例化该类并调用其中queryInfo()方法。这样,之前十套代码的工作量就可以浓缩到一套代码中完成了。如果要修改queryInfo相关业务,那么只需要修改一处即可。
新建如下4个类,Animals.java,Dog.java, Cat.java, Monkey.java。其中Dog, Cat, Monkey类继承Animals。
Animals.java
Cat.java
Dog.java
Monkey.java
再创建一个测试类TestDemo.java
这里,我通过遍历包含三个类名全路径的数组,动态的实例化三个类,并分别执行makeNoise()方法。最终得到的结果为
也就是说,现在有三种动物,如果我再添加10种动物,只需将这十种动物的类名传入,就可以实例化相应的对象了。如果按照传统做法,分别需要new出来10个对象,再分别调用makeNoise方法。如果是100种动物呢?传统做法的弊端就显露出来了。而利用反射,我无需在编译的时候创建那么多对象,只需要传入相应的类名,就可以在运行时生成想要创建的对象。
这也就是对于反射概念中提到的“程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载”。用户选择哪个动物在编译时是完全不知道的,运行起来后用户才会有选择动物的操作。那么不论用户选择哪种动物,通过反射可以在程序运行时动态生成和使用相对应的对象,这就是反射的意义。
这里还要简单提一下Annotation注解。注解也是和反射密不可分的。通过反射,可以获取到某个类、某个参数、属性、方法等上的注解的信息,这样可以根据注解信息进行相关的操作。可以理解为,注解即为一个标识,遇到什么标识就该做什么标识的事。
以上就是我对Java反射机制的浅析,文笔粗略,请多包涵。如果有理解有误的地方,欢迎指正!
前言
一直想要研究一下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反射机制的浅析,文笔粗略,请多包涵。如果有理解有误的地方,欢迎指正!
相关文章推荐
- 浅析Python中yield关键词的作用与用法
- 浅析Python中yield关键词的作用与用法
- Intent有关的作用用法
- 解析一个有关sizeof用法的题目--sizeof(i++)
- 浅析python 中__name__ = '__main__' 的作用
- 关于mongodb的索引的作用和用法--mongodb
- auto_ptr用法浅析
- 爬虫学习——爬虫之soup.select()用法浅析
- static用法浅析
- 在C#中global关键字的作用及其用法
- mvc@helper 的用法和作用
- ifndef/define/endif作用和用法
- 有关VA_LIST的用法--变参函数的实现
- C# Eval在asp.net中的用法及作用
- AFNetwork作用和用法详解
- js_document有关getElementsByName 定义和用法
- .NET Attribute(特性)的作用与用法
- Maven介绍,包括作用、核心概念、用法、常用命令、扩展及配置
- PACKAGE-INFO.JAVA 作用及用法详解