陈宇博客

Softworks对日软件人才培训高级讲师专栏

用户操作
[即时聊天] [发私信] [加为好友]
小宇ID:frank_softworks
22926次访问,排名5192好友1人,关注者10
,主要从事,系统设计,软件开发和新人培训 ,5年以上软件开发经验,10个以上C/S和B/S软件系统开发经验,
熟练使用VB.net,C++,C#,ASP.net,JSP,JS,VBS,Java2,
熟悉J2EE企业级开发及架构,
熟练使用SQL Server,Oracle,DB2数据库,
受过专业软件管理培训,深刻了解企业级软件管理及实施
frank_softworks的文章
原创 7 篇
翻译 0 篇
转载 0 篇
评论 49 篇
最近评论
shijie1983:测厚仪 
氧化铁
测厚仪
hfgayy:是不是也可以理解为:

用TigerTank类的构造器去初始化Tank的一个对象呢?
lishengwei:不太懂呵呵呵
wugui89:没看明白
yychaorenlaile:前辈,能和您认识吗?我也有一腔为中国软件事业的腾飞而奋斗的热血。您QQ是多少?我的是496953164
文章分类
    收藏
      相册
      存档
      软件项目交易
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      原创 反射机制与系统耦合实例详解收藏

      新一篇: 事件驱动模型实例详解(Java篇) | 

      反映射技术(以下简称:反射)的概念是由Smith1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。

      我--陈宇,作为Softworks对日软件人才培训中心的资深教员对于该技术有比较深入的研究,也正好借此与各位读者深入探讨一下。

      在如今程序语言的设计领域中,几乎每种OO语言都专门设计了支持反射技术的API,不管是Microsoft公司的.Net框架还是SUN公司的Java语言都是如此,本技术帖就以Java为例来进行探讨。

      Java编程语言中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码链接。这样一来整个系统的耦合性就会降低并可以大大增加系统的灵活度。反射机制被大量运用在系统架构的设计层次上,并且在编写公共类和系统基盘的时候也起到了举足轻重的作用,甚至有人提出这门技术是一个程序员转型成为系统架构师的必经之路。

      反射机制是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期取得任何已知名称之 class 的內部信息,包括、父类、接口、内部类、属性结构体方法,並可于执行期生成实体、变更 字段內容或唤起 方法

      有了反射机制,我们可以:

      1.判断某个对象所属的类型(Class)

      2.取得类型(Class)的属性,方法,构造体和父类的相关信息。

      3.找出接口中的常量和方法定义。

      4.为一个执行期才得知名称的类产生对象。

       

      Java类反射中的主要方法:

      对于类而言构造函数、字段和方法是最为重要的内容java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。

      以下是用于查找构造函数的一组反射调用:

      方法

      说明

      Constructor getConstructor(Class[] params)

      获得使用特殊的参数类型的

      公共构造函数

      Constructor[] getConstructors()

      获得类的所有公共构造函数

      Constructor getDeclaredConstructor(Class[] params)

      获得使用特定参数类型的构造函数(与访问级别无关)

      Constructor[] getDeclaredConstructors()

      获得类的所有构造函数(与访问级别无关)

       

      获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:

      方法

      说明

      Field getField(String name)

      获得指定的公共字段

      Field[] getFields()

      获得类的所有公共字段

      Field getDeclaredField(String name)

      获得指定的字段

      Field[] getDeclaredFields()

      获得类声明的所有字段

       

      用于获得方法信息函数:

      方法

      说明

      Method getMethod(String name, Class[] params)

      使用特定的参数类型,获得命名的公共方法

      Method[] getMethods()

      获得类的所有公共方法

      Method getDeclaredMethod(String name, Class[] params)

      使用特定的参数类型,获得类声明的命名的方法

      Method[] getDeclaredMethods()

      获得类声明的所有方法

       

      开始使用反射机制:

      用于反射机制的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。

      下面就是获得一个 Class 对象的方法之一:

      Class c = Class.forName ("java.lang.Integer");

      这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:

      Class c = Integer.class;

      或者

      Class c = Integer.TYPE;

      它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 ( Integer) 中预先定义好的 TYPE 字段。

      第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。

      一旦取得这个信息,就可以进行第三步了。

      第三步是使用 reflection API 来操作这些信息,如下面这段代码:

      Class c = Class.forName("java.lang.String");

      Method m[] = c.getDeclaredMethods();

      System.out.println(m[0].toString());

       

       

       

       

       


      它将以文本方式打印出 String 中定义的第一个方法的原型。

      反射经常由框架型代码使用,由于这一点,我们可能希望框架能够全面接入代码,无需考虑常规的接入限制。例如当代码在不值得信任的代码共享的环境中运行时。

      假设有以下这个类的声明:

      class DemoReflection
      {
             private String name = null;
       
             private void doPrint() {
                    System.out.println("print.....");
             }
      };

      可以肯定的是,这个类中的属性name和方法doPrint都是无法对外展示的,但是使用了反射以后就可以办到。

      package cn.softworks.demo;
       
      import java.lang.reflect.*;
       
      publicclass TestReflection {
          publicstaticvoid main(String args[]) {
       
              try {
                  // 通过反映射技术得到DemoReflection的类型
                  Class cls = Class.forName("DemoReflection");
                  // 动态创建DemoReflection类的实力
                  Object instance = cls.newInstance();
                  // 通过反映射技术得到DemoReflection的非公有方法doPrint
                  Method m = cls.getDeclaredMethod("doPrint",
                          new Class[] { String.class });
                  // 表示可以随意访问该类中的方法
                  m.setAccessible(true);
                  // 调用doPrint方法
                  m.invoke(instance, new Object[] { "Softworks" });
              } catch (Exception ex) {
                  System.out.println(ex);
              }
       
          }
      };

       

      在该代码中,读者可能看到了一个比较陌生的方法setAccessible,这个方法非常重要,如果它不被设置成true那么所有非公有方法仍然无法调用,所以在调用非公有方法的时候需要注意这点。

      Private属性的访问方式和方法的访问方式类似。

      反射的实际应用

             在之前的介绍中,我们已经了解了反射机制的重要性,也已经了解到了反射经常被运用到系统框架设计和系统解耦中,现在就以一个真实的项目开发案例来探讨一下反射机制的重要性。

             1.什么叫系统耦合:

             或许很少有读者会在电影结束后做在电影院中观看电影的幕后工作者,但是可以肯定的是,如果没有那么多幕后工作者,那么就不会有诸如《指环王》等大片的出现了。但是如果真的要拍摄出像《指环王》这样的大片,光依靠强大的拍摄团队还不够,团队中的每个成员都必须要相互合作和交流,那么这种行为就被称为团队人员和团队人员的耦合关系。

             软件系统就如同刚才的电影拍摄团队,是有大量的类(工作人员)所组成的,那么这些类之间如果没有交互的话,整个软件系统就不可能正确合理的工作,因此软件系统中的耦合就来自于类与类之间的通讯,例如以下代码:

      Cleaner clearner = new Cleaner(“Chen.yu”);

      Broom broom = new Broom();

      cleaner.clear(broom);

       

       

          通过这段代码,我们可以看出,Cleaner类和Broom之间有相互交互的关系,也就是说这两个类之间产生了耦合关系。请设想,代码如果有一个人来开发的话,耦合关系是不会影响到系统开发的。因为一个开发员可以理所当然的先开发Broom类再开发Clear类。可以软件工程是一个复杂的过程,一个人是不可能完成所有开发任务的,现在假设我们系统中的Cleaner类和Broom类一定要有两个人分别开发的话,那么问题就暴露出来了。因为根据代码可知,要想开发Cleaner类就必须先开发Broom类,Cleaner类中的方法clear需要使用Broom类的信息。那么究竟应该如何解决这个问题呢?      

             2.利用工厂模式解决耦合关系:

      现在我把代码改写成以下的状态:

        

      package cn.softworks.test;

       

      import cn.softworks.demo.BeanFactory;

      import cn.softworks.demo.Cleaner;

      import cn.softworks.demo.IClearEquipment;

       

      /**

       * 反射机制的测试类

       *

       * @version 1.0

       *

       * @author Chen.yu

       *

       * 上海Softworks对日软件人才培训中心版权所有

       */

      public class TestClient {

         

          public static void main(String args[]) {

             

              //通过工厂创建指定的清洁工具类

              IClearEquipment eq =

                  (IClearEquipment) BeanFactory.newInstance().creator("ClearEquipment");

             

              if(eq == null)

                  return;

             

              //创建清洁工对象

              Cleaner cleaner = new Cleaner();

             

              //清洁工人开始清洁

              cleaner.clear(eq);

          }

      }

       

       代码虽然多了,但是可以肯定的是在代码中我们只使用了Cleaner类,并没有使用到Broom类,那么Broom类上哪里去了呢?我们看到了IClearEquipment接口,Broom类正好实现了该接口,并且我们利用工厂模式BeanFactory完全隐藏了Broom类的创建细节,以此来解决Broom类与Cleaner类之间的耦合关系,现在Broom类和Cleaner类就可以交给2个开发员并行开发了。

      以下是IClearEquipment接口的代码片断:

      package cn.softworks.demo;

       

      /**

       *

       * 该接口是的作用是用来定义清洁设备的标准<br>

       * 换句话说,如果要想成为清洁设备,那么就必须要具有清洁能力

       *