美文网首页码农翻身--Java系列
「转载」给小白的Java EE生存指南(6):Java 反射

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

作者: liwei_happyman | 来源:发表于2018-01-11 12:17 被阅读362次

    转载自 微信公众号 码农翻身 不用于商业宣传 版权归原作者所有 侵权删

    本文是给小白的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> 
    
    1. 定义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>
    
    1. 定义Event 类,如下所示:
    public class Event {
        private Long id;
        private String title;
        private Date date;
        ...... 为了节省篇幅, 每个属性的getter /setter 方法略...
    }
    
    1. 查询, 你可以用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>
    
    1. 写一个Java 文件
    public  class   HelloWorld
    {
        private String message;
        public void setMessage(String message){
            this.message  = message;
        }
        public void getMessage(){
            System.out.println("My Message : "+ message);
        }
    }
    
    1. 调用
    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倍嘛!


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

    长按二维码, 关注码农翻身

    相关文章

      网友评论

        本文标题:「转载」给小白的Java EE生存指南(6):Java 反射

        本文链接:https://www.haomeiwen.com/subject/wwpznxtx.html