微信号:coderising

介绍:工作15年的IBM软件架构师分享编程和职场的经验和教训, 不容错过.

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

2016-01-19 20:03 老刘

本文是给小白的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" , 回复的人多了, 我可以专门开一个主题讲讲 :-)  

-----------------------------------------------------------------------------------

码农翻身公共号(coderising) : 由工作15年的IBM架构师创建,分享编程和职场的经验教训。

回复下面的关键字,查看更多文章

神一样的程序员 键盘侠 留学 读书 调试 烂代码 小白 培训

数据结构 代码腐化 无聊 迷茫 刚工作 死锁 简历  真实项目



加入码农翻身QQ群:135769418 每周日晚9点至10点定期授课,不容错过。


长按二维码, 关注"coderising"


 
码农翻身 更多文章 从无聊的工作中寻找价值 聊一聊那些神一样的程序员们(上) 聊一聊那些神一样的程序员们(中) 那些永不过时的书,你看过几本吗? 为什么你无法坚持自学编程?
猜您喜欢 浅析 Linux 初始化 init 系统(1):sysvinit 十个经典Android开源APP项目 5个常识4个陷阱,让“A轮死”不再发生 智能手表DIY-名家大讲堂4.29日首讲【在线讲座】 Docker 传奇之 dotCloud