反射[reflection]在java中的使用

来源:互联网 发布:求质数算法 编辑:程序博客网 时间:2024/04/29 04:00

最近在学习j2ee和设计模式两门课时同时遇到这个技术,按道理讲是挺基础的一个概念,但是在以前的编程中没有应用到,所以也没有注意,现在设计模式刚刚入门,发现反射技术真的是非常的好用。

首先看一个实体类

public class Admin {// Fieldprivate int id = 13416123;private String name = "xujingfeng";// Constructorpublic Admin(){System.out.println("Admin.Admin()");}public Admin(String name){System.out.println("Admin.Admin()" + name);}// Methodpublic int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

其实一个类说的详细点主要是由以下几个部分组成:Class = Field + Constructor + Method (这里说主要由这几个组成,其实还可以有注解Annotation之类生僻的概念,不做讨论了)

Field 就是常说的 属性+属性值,这里其实可以这样理解,属性主要是针对类的,属性值主要是针对对象的。为了方便解释反射,直接在类中定义了默认初始值,让类也具备了属性值。

Constructor 包括了有参构造和无参构造...这些构造方法。

Method就是那些有参,无参,又返回,无返回的各类方法。

这样我们就讲类总结了,主要由这些部分组成,看完组成,我还没有提到反射呢,不急。

想想我们平时主要是怎么使用类的?是不是拿到一个类,然后new一个对象,就像这样:

Admin admin = new Admin();

再总结一下,我们面向对象编程,把所有东西抽象成类,但是实际使用的大部分是对象,也就是 类 -> 对象。(当然,还有静态类的概念,不要纠结...我是为了讲出我想讲的所以这么讲的)

这个时候我说,在项目有中有些时候你不能这么新建对象!为什么呢?这样也可以得到admin对象啊?问题就在于

这样把对象生成的方式写死了,如果我现在要new的是一个Student对象,你就必须要修改源代码

Student student = new Student();

你可能会想,拜托,这不就是一句话改一下吗,有必要使用一个新的技术吗?再说了这是两个完全不同的对象,需求肯定也不一样啊,改就改了啊!

先解决第一个疑惑,没错,这的确只是生成了一个对象,改起来比较简单,但是,如果我项目中有1000个这样类似需要修改的地方,你都要一个个找出来手动改掉?显然,我们程序需要一个通用的方法,来新建对象,这样只需要修改一个地方,大家就都改了。

再来解决第二个疑惑,其实项目设计中,大多会讲究设计模式的一些原则,里式替换原则等等,所以你程序的正确写法可能变成这样。

Object admin = new Admin();

好像不就是多态吗?那是面向对象的特性叫多态,但是这么写的好处,需要学习设计模式才能体会,不在今天的讨论范围。也就是说,左边是固定的,右边是可变的,这个时候,王晖大大的教诲就会萦绕在我们耳边:面向对象和设计模式同时需要我们做一个事,那就是把可变的东西东西封装起来。

怎么封装?那就要用到反射了,讲到这儿终于回到正题了。

还是先看看,如果我们用反射,怎么新建一个对象吧。

public void test() throws Exception {// 类全名String className = "cczu.xu.Admin";// 得到类字节码Class clazz = Class.forName(className);// 创建对象1: 默认构造函数简写Admin admin = (Admin) clazz.newInstance();}

这样通过反射就可以实现创建对象了,和之前直接new对象最本质的区别就是,现在的className是字符串,可以通过读取配置文件来获取,当然我这里为了省事是写死的,完全可以把字符串写在配置文件中,然后可以结合泛型,动态的创建配置文件中对象。

在这个过程中其实不难发现,Class.forName通过类全名获取类字节码,实际上我们干了一件和以往不同的事,那就是,我们由 类名 -> 类字节码 -> 对象 不一样要使用new关键字了。

知道对象也得到Class,也就是 对象 -> 类

Class clazz = admin.getClass();
Class clazz = admin.Class;

所以,反射机制解决一个问题就是,动态生成对象,说的通俗一点就是说,现在我们可以在配置文件里面写上类的全名,然后用反射机制,封装成方法,这样就可以直接生成对象了。通过上面的例子,还可以发现打破我们以往有类得到对象习惯,最起码让你知道了,由对象还可以得到类。

反射还有其他作用,我们获取到类之后,往往不仅仅需要他的实例,可能还需要得到他内部的东西(内部东西,就是在最前面讲的那三个东西Field + Constructor + Method)

再简单说下另一个作用吧,其实还有其他作用的。

// 反射技术public class App {// 1. 创建对象@Testpublic void testInfo() throws Exception {// 类全名String className = "cczu.xu.Admin";// 得到类字节码Class<?> clazz = Class.forName(className);// 创建对象1: 默认构造函数简写//Admin admin = (Admin) clazz.newInstance();// 创建对象2: 通过带参数构造器创建对象Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);Admin admin = (Admin) constructor.newInstance("Jack");}@Test//2. 获取属性名称、值public void testField() throws Exception {// 类全名String className = "cczu.xu.Admin";// 得到类字节码Class<?> clazz = Class.forName(className);// 对象Admin admin =  (Admin) clazz.newInstance();// 获取所有的属性名称Field[]  fs =  clazz.getDeclaredFields();// 遍历:输出每一个属性名称、值for (Field f : fs) {// 设置强制访问f.setAccessible(true);// 名称String name = f.getName();// 值Object value = f.get(admin);System.out.println(name + value);}}@Test//3. 反射获取方法public void testMethod() throws Exception {// 类全名String className = "<span style="font-family: Arial, Helvetica, sans-serif;">cczu.xu.Admin</span><span style="font-family: Arial, Helvetica, sans-serif;">";</span>// 得到类字节码Class<?> clazz = Class.forName(className);// 对象Admin admin =  (Admin) clazz.newInstance();// 获取方法对象    public int getId() {Method m = clazz.getDeclaredMethod("getId");// 调用方法Object r_value = m.invoke(admin);System.out.println(r_value);}}
其中注意一个细节
clazz.getDeclaredXX()
拿到的是全部的方法,属性,如果是

clazz.getXX()
拿到的就是一些受限制的,例如getMethod()就只有公共方法才能被获取到

有了反射,我们就可以洞悉一个类了。至于反射的具体应用,我现在只在工厂模式,BeanUtils组件,重写BaseServlet,BaseDao这些地方用到,但至少可以明晰一点,在
大量的公用方法,工具类,以及框架的书写中,会大量使用到反射的。先记录这么多吧。



0 0