当前位置: 代码迷 >> J2EE >> 给小白的Java EE生涯指南(6) :Java 反射
  详细解决方案

给小白的Java EE生涯指南(6) :Java 反射

热度:500   发布时间:2016-04-17 22:55:59.0
给小白的Java EE生存指南(6) :Java 反射
本文是给小白的Java EE生存指南的第6篇, 讲点稍微有深度的:反射。 

这里不定义什么叫反射,先来看个例子,假设我给你一个Java 类: 
package com.example;
public class HelloWorld {
    public HelloWorld(){
    }
    public void sayHello(){
        System.out.println("hello world!");
    }
}
现在要求: 
(1) 你不能使用 HelloWorld hw = new HelloWorld() , 但是要构建一个HelloWorld的实例来. 
(2) 调用sayHello() 方法, 但是不能直接用 HelloWorld实例的 hw.sayHello()方法  , 说起来怪拗口的 :-)

用Java的反射功能, 可以很轻松的完成上面的要求:

//第一步, 先把HelloWorld的类装载进来
Class cls = Class.forName("com.example.HelloWorld");
//第二步, 创建一个HelloWorld的实例, 注意, 这里并没有用强制转型把obj转成HelloWorld
Object obj = cls.newInstance();
//第三步, 得到这个类的方法, 注意, 一个类的方法也是对象啊
Method m = cls.getDeclaredMethod("sayHello"); 
//第四部, 方法调用, 输出"hello world"
m.invoke(obj);    

可能有人要问了, 为什么不直接new 出来呢?  通过反射来创建对象,调用方法多费劲啊 ?
这是个好问题,关键点就是: 很多时候我们并不能事先知道要new 什么对象,  相反,我们可能只知道一个类的名称和方法名, 很多时候这些名称都是写在XML配置当中的。 
 
为了更好的说明问题, 来看看几个SSH的例子:

【Struts的例子】
1. 在XML配置文件中定义Action
<action name="HelloWorld" class="example.HelloWorld">        
        <result>/hello.jsp</result>  
</action> 
2. 定义Java 类
public class HelloWorld extends ExampleSupport {  
    public String execute() throws Exception {    
        ......
        return SUCCESS;                           
    }  
    .......
}
Struts 框架的作者事先肯定不知道你会配置一个HelloWorld的Action 。
不过他可以这么做, Struts 在启动以后,解析你配置XML配置文件, 发现名称为HelloWorld的Action, 找到相对于的类名example.HelloWorld, 然后就可以通过反射去实例化这个类。 等到有人调用这个action 的时候, 可以通过反射来调用HelloWorld的execute() 方法。 

【Hibernate的例子】
1.  定义Java类和表之间映射, 类名叫Event, 对应的表名是EVENTS 。
<hibernate-mapping package="org.hibernate.tutorial.hbm">
    <class name="Event" table="EVENTS">
        <id name="id" column="EVENT_ID">
            <generator class="increment"/>
        </id>
        <property name="date" type="timestamp" column="EVENT_DATE"/>
        <property name="title"/>
    </class>
</hibernate-mapping>

2. 定义Event 类,如下所示:
public class Event {
    private Long id;

    private String title;
    private Date date;
    ...... 为了节省篇幅, 每个属性的getter /setter 方法略...
}

3. 查询, 你可以用Hibernate 这么查询表中的数据了:
List result = session.createQuery( "from Event" ).list();
        for ( Event event : (List<Event>) result ) {
            System.out.println( "Event (" + event.getDate() + ") : " + event.getTitle() );
}
Struts 的作者事先也不知道你会配置一个叫Event的类。 
不过他会这么处理: 类名(Event)-> 数据库表名(EVENTS) -> 发出SELECT查询表数据 -> 通过反射创建Event的实例 -> 通过反射调用实例的setter方法把数据库的值设置进去

【Spring的例子】
1. 配置一个Bean
<beanid="helloWorld"class="example.HelloWorld">
    <propertyname="message"value="Hello World!"/>
</bean>

2. 写一个Java 文件
public  class   HelloWorld
{
    private String message;
    public void setMessage(String message){
        this.message  = message;
    }
    public void getMessage(){
        System.out.println("My Message : "+ message);
    }
}

3. 调用

ApplicationContext context =newClassPathXmlApplicationContext("Beans.xml");
HelloWorld hw=(HelloWorld) context.getBean("helloWorld");
hw.getMessage();

我都懒得解释了, 无非是根据类的名称通过反射创建一个类HelloWorld的实例, 然后再通过反射调用setMessage方法, 这样当你getMessage就有值了。 

所以反射是很重要的, 在Java EE世界里, 反射最大的用途就是支持声明式的方法(在XML中)来描述应用的行为,   是Struts, Hibernate , Spring 的最核心的技术之一。   

简单的来讲, 反射能让你在运行时而不是编程时做下面的事情:
(1) 获取一个类的内部结构信息(或者成为元数据), 包括包名,类名, 类所有的方法, 
(2) 运行时对一个Java对象进行操作, 包括创建这个类的实例, 设置一个属性的值, 调用这个类的方法等等。 

这篇文章只是介绍了反射的一点皮毛和用途, 具体的细节还是等待你自己去发掘吧。 

【元编程】
等等,还有一个小问题:为什么叫反射呢?
我想可能是Java程序在运行时能够看到自己的结构和行为吧, 就像看到镜子当中的自己一样, 反射了出来 。

如果扩展一点, 这种用代码来生成代码的方式, 其实叫做“元编程”。
C语言就不具备这样的能力, 经过编译以后, C语言中的struct 名称 , 数组名等信息都已经消失了, 基本上就是指针了。  你可以这么试一试:写个程序,在运行时打印一下一个struct的名称, 看看能不能实现。

但是像其他一些语言, 例如Ruby , 程序在运行时不但能检视自己, 还能动态的修改自己, 比如:给自己加上一个方法, 这种开放的能力给Ruby 编程来了巨大的飞跃, LISP的元编程能力更加强悍, 仅仅使用LISP自己就能定义一个新语言出来。 
利用这种能力, 人们可以针对某个领域编写领域特定语言(Domain specific Language, 简称DSL), 然后使用DSL这个语言来进行应用编程, 那效率可不是一般的高, 像Ruby on Rail 不就号称开发速度是Java的10倍嘛!

DSL是另外一个主题了, 你要是感兴趣的话请回复 "想听DSL" , 回复的人多了, 我可以专门开一个主题讲讲 :-)  

------------------------------------------------------------------------------------------------------------------------------- 
QQ群:135769418  每周日听工作15年的IBM架构师定期分享编程和职场的经验教训。

关注"码农翻身" 微信公共号, 更多精彩文章。


  相关解决方案