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

 泡牛吧!

                                       希望越来越多的光棍能够泡到牛

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

共 62篇 前 10 页: 10    每页5篇 上一页   下一页  

兴趣新闻

板桥里人 http://www.jdon.com 2002/10/07(转载请保留)

模式实战书籍《Java实用系统开发指南》

工厂模式定义:提供创建对象的接口.

为何使用?
工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

我们以类Sample为例, 如果我们要创建Sample的实例对象:

Sample sample=new Sample();

可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。

首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:

Sample sample=new Sample(参数);

但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。

为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。

在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。

这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:

Sample mysample=new MySample();
Sample hissample=new HisSample();

随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.

但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.

工厂方法
你会建立一个专门生产Sample实例的工厂:

public class Factory{

  public static Sample creator(int which){

  //getClass 产生Sample 一般可使用动态类装载装入类。
  if (which==1)
    return new SampleA();
  else if (which==2)
    return new SampleB();

  }

}


那么在你的程序中,如果要实例化Sample时.就使用

Sample sampleA=Factory.creator(1);

这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.

使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:



进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。

抽象工厂
工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).

这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.

这里假设:Sample有两个concrete类SampleA和SamleB,而Sample2也有两个concrete类Sample2A和SampleB2

那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂:

public abstract class Factory{

  public abstract Sample creator();

  public abstract Sample2 creator(String name);

}

public class SimpleFactory extends Factory{

  public Sample creator(){
    .........
    return new SampleA
  }

  public Sample2 creator(String name){
    .........
    return new Sample2A
  }

}

public class BombFactory extends Factory{

  public Sample creator(){
    ......
    return new SampleB
  }

  public Sample2 creator(String name){
    ......
    return new Sample2B
  }

}




从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2?

抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。


在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,

举例

我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式:

public abstract class ForumFactory {

  private static Object initLock = new Object();
  private static String className = "com.jivesoftware.forum.database.DbForumFactory";
  private static ForumFactory factory = null;

  public static ForumFactory getInstance(Authorization authorization) {
    //If no valid authorization passed in, return null.
    if (authorization == null) {
      return null;
    }
    //以下使用了Singleton 单态模式
    if (factory == null) {
      synchronized(initLock) {
        if (factory == null) {
            ......

          try {
              //动态转载类
              Class c = Class.forName(className);
              factory = (ForumFactory)c.newInstance();
          }
          catch (Exception e) {
              return null;
          }
        }
      }
    }

    //Now, 返回 proxy.用来限制授权对forum的访问
    return new ForumFactoryProxy(authorization, factory,
                    factory.getPermissions(authorization));
  }

  //真正创建forum的方法由继承forumfactory的子类去完成.
  public abstract Forum createForum(String name, String description)
  throws UnauthorizedException, ForumAlreadyExistsException;

  ....

}






因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:

private static String className = "com.jivesoftware.forum.database.DbForumFactory";

你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.

在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果.

看看Java宠物店中的CatalogDAOFactory:

public class CatalogDAOFactory {

  /**

  * 本方法制定一个特别的子类来实现DAO模式。
  * 具体子类定义是在J2EE的部署描述器中。
  */

  public static CatalogDAO getDAO() throws CatalogDAOSysException {

    CatalogDAO catDao = null;

    try {

      InitialContext ic = new InitialContext();
      //动态装入CATALOG_DAO_CLASS
      //可以定义自己的CATALOG_DAO_CLASS,从而在无需变更太多代码
      //的前提下,完成系统的巨大变更。

      String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);

      catDao = (CatalogDAO) Class.forName(className).newInstance();

    } catch (NamingException ne) {

      throw new CatalogDAOSysException("
        CatalogDAOFactory.getDAO: NamingException while
          getting DAO type : \n" + ne.getMessage());

    } catch (Exception se) {

      throw new CatalogDAOSysException("
        CatalogDAOFactory.getDAO: Exception while getting
          DAO type : \n" + se.getMessage());

    }

    return catDao;

  }

}






CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。

由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。
haohao   2005-04-16 18:54:52 阅读:913  评论:0  引用:0
java精品学习资料,学习必备工具,点击进入...
不详
2005-03-25 16:36:25
4412 次浏览




Java的白皮书为我们提出了Java语言的11个关键特性

  (1)Easy:Java的语法比C++的相对简单,另一个方面就是Java能使软件在很小的机器上运行,基础解释其和类库的支持的大小约为40kb,增加基本的标准库和线程支持的内存需要增加125kb。

  (2)分布式:Java带有很强大的TCP/IP协议族的例程库,Java应用程序能够通过URL来穿过网络来访问远程对象,由于servlet机制的出现,使Java编程非常的高效,现在许多的大的web server都支持servlet。

  (3)OO:面向对象设计是把重点放在对象及对象的接口上的一个编程技术.其面向对象和C++有很多不同,在与多重继承的处理及Java的原类模型。

  (4)健壮特性:Java采取了一个安全指针模型,能减小重写内存和数据崩溃的可能型。

  (5)安全:Java用来设计网路和分布系统,这带来了新的安全问题,Java可以用来构建防病毒和防攻击的System.事实证明Java在防毒这一方面做的比较好。

  (6)中立体系结构:Java编译其生成体系结构中立的目标文件格式可以在很多处理器上执行,编译器产生的指令字节码(Javabytecode)实现此特性,此字节码可以在任何机器上解释执行。

  (7)可移植性:Java中对基本数据结构类型的大小和算法都有严格的规定所以可移植性很好。

  (8)多线程:Java处理多线程的过程很简单,Java把多线程实现交给底下操作系统或线程程序完成.所以多线程是Java作为服务器端开发语言的流行原因之一。

  (9)Applet和servlet:能够在网页上执行的程序叫Applet,需要支持Java的浏览器很多,而applet支持动态的网页,这是很多其他语言所不能做到的。

  基本概念

  1.OOP中唯一关系的是对象的接口是什么,就像计算机的销售商她不管电源内部结构是怎样的,他只关系能否给你提供电就行了,也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的,不同的对象的访问通过函数调用来完成,对象间所有的交流都是通过方法调用,通过对封装对象数据,很大限度上提高复用率。

  2.OOP中最重要的思想是类,类是模板是蓝图,从类中构造一个对象,即创建了这个类的一个实例(instance)。

  3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程,一个对象中的数据叫他的实例字段(instance field)。

  4.通过扩展一个类来获得一个新类叫继承(inheritance),而所有的类都是由Object根超类扩展而得,根超类下文会做介绍。

  5.对象的3个主要特性

  behavior---说明这个对象能做什么.

  state---当对象施加方法时对象的反映.

  dentity---与其他相似行为对象的区分标志.

  每个对象有唯一的indentity 而这3者之间相互影响.

  6.类之间的关系:

  use-a :依赖关系

  has-a :聚合关系

  is-a :继承关系--例:A类继承了B类,此时A类不仅有了B类的方法,还有其自己的方法.(个性存在于共性中)

  7.构造对象使用构造器:构造器的提出,构造器是一种特殊的方法,构造对象并对其初始化。

  例:Data类的构造器叫Data

  new Data()---构造一个新对象,且初始化当前时间.

  Data happyday=new Data()---把一个对象赋值给一个变量happyday,从而使该对象能够多次使用,此处要声明的使变量与对象变量二者
是不同的.new返回的值是一个引用。

  构造器特点:构造器可以有0个,一个或多个参数

  构造器和类有相同的名字

  一个类可以有多个构造器

  构造器没有返回值

  构造器总是和new运算符一起使用.  
  8.重载:当多个方法具有相同的名字而含有不同的参数时,便发生重载.编译器必须挑选出调用哪个方法。

  9.包(package)Java允许把一个或多个类收集在一起成为一组,称作包,以便于组织任务,标准Java库分为许多包.java.lang java.util java,net等,包是分层次的所有的java包都在java和javax包层次内。

  10.继承思想:允许在已经存在的类的基础上构建新的类,当你继承一个已经存在的类时,那么你就复用了这个类的方法和字段,同时你可以在新类中添加新的方法和字段。

  11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。

  12.多态:在java中,对象变量是多态的.而java中不支持多重继承。

  13.动态绑定:调用对象方法的机制。

  (1)编译器检查对象声明的类型和方法名。

  (2)编译器检查方法调用的参数类型。

  (3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。

  (4)当程序运行并且使用动态绑定来调用一个方法时,那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。

  (5)动态绑定:是很重要的特性,它能使程序变得可扩展而不需要重编译已存代码。

  14.final类:为防止他人从你的类上派生新类,此类是不可扩展的。

  15.动态调用比静态调用花费的时间要长。

  16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。

  例: public abstract string getDescripition

  17.Java中的每一个类都是从Object类扩展而来的。

  18.object类中的equal和toString方法。

  equal用于测试一个对象是否同另一个对象相等。

  toString返回一个代表该对象的字符串,几乎每一个类都会重载该方法,以便返回当前状态的正确表示.

  (toString 方法是一个很重要的方法)

  19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。

  20.数组列表:ArrayList动态数组列表,是一个类库,定义在java.uitl包中,可自动调节数组的大小。

  21.class类 object类中的getclass方法返回ckass类型的一个实例,程序启动时包含在main方法的类会被加载,虚拟机要加载他需要的所有类,每一个加载的类都要加载它需要的类。

  22.class类为编写可动态操纵java代码的程序提供了强大的功能反射,这项功能为JavaBeans特别有用,使用反射Java能支持VB程序员习惯使用的工具。

  能够分析类能力的程序叫反射器,Java中提供此功能的包叫Java.lang.reflect反射机制十分强大.

  1.在运行时分析类的能力。

  2.在运行时探察类的对象。

  3.实现通用数组操纵代码。

  4.提供方法对象。
  而此机制主要针对是工具者而不是应用及程序。

  反射机制中的最重要的部分是允许你检查类的结构.用到的API有:

  java.lang.reflect.Field 返回字段.

  java.reflect.Method 返回方法.

  java.lang.reflect.Constructor 返回参数.

  方法指针:java没有方法指针,把一个方法的地址传给另一个方法,可以在后面调用它,而接口是更好的解决方案。

  23.接口(Interface)说明类该做什么而不指定如何去做,一个类可以实现一个或多个interface。

  24.接口不是一个类,而是对符合接口要求的类的一套规范。

  若实现一个接口需要2个步骤: 

  1.声明类需要实现的指定接口。

  2.提供接口中的所有方法的定义。

  声明一个类实现一个接口需要使用implements 关键字

  class actionB implements Comparable 其actionb需要提供CompareTo方法,接口不是类,不能用new实例化一个接口.

  25.一个类只有一个超类,但一个类能实现多个接口。Java中的一个重要接口:Cloneable

  26.接口和回调.编程一个常用的模式是回调模式,在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。

  例:ActionListener 接口监听.

  类似的API有:java.swing.JOptionPane

  java.swing.Timer

  java.awt.Tookit

  27.对象clone:clone方法是object一个保护方法,这意味着你的代码不能简单的调用它。

  28.内部类:一个内部类的定义是定义在另一个内部的类。

  原因是:

  1.一个内部类的对象能够访问创建它的对象的实现,包括私有数据。

  2.对于同一个包中的其他类来说,内部类能够隐藏起来。

  3.匿名内部类可以很方便的定义回调。

  4.使用内部类可以非常方便的编写事件驱动程序。

  29.代理类(proxy):

  1.指定接口要求所有代码

  2.object类定义的所有的方法(toString equals)

  30.数据类型:Java是强调类型的语言,每个变量都必须先申明它都类型,java中总共有8个基本类型.4种是整型,2种是浮点型,一种是字符型,被用于Unicode编码中的字符,布尔型。
haohao   2005-04-16 18:42:37 阅读:805  评论:0  引用:0

原创:morgan83 2002年11月20日



Object类是所有类的超类,也就是说,Java中的每一个类都是由Object扩展而来的。因而每当你创建一个对象,它都将拥有Object类中的全部方法。让我们先来看看java.lang.Object的中的主要方法有哪些:

public class Object{
//公共构造函数
public Object();
//公共实例方法
public boolean equals(Object obj);
public native int hashCode();
public final native Class getClass();
public String toString();
public final native void notify();
public final native void notifyAll();
public final void wait() throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
//保护实例方法
protected native Object clone();
protected void finalize() throws Throwable; }

方法equals测试的是两个对象是否相等,方法clone进行对象拷贝,方法getClass返回和当前对象相关的Class对象,方法notify,notifyall,wait都是用来对给定对象进行线程同步的。

然而Object类所提供的只是一些基本的方法,我们在编写自己的类时经常需要覆盖这些方法,一方面是加强功能,另一方面也是为了适应当前的情况。这就引出了本文的主题――

如何编写自己的equals方法
首先要明确一个问题,Object类中的equals方法是怎样判断两个对象是否相等的呢?让我们来看看它的源代码:

public boolean equals(Object obj)
{
return (this == obj);
}


不难看出,它仅仅判断了两个对象的引用是否相等,这在很多情况下并没有太大的实际意义。比如,我们需要一个比较两个字符串是否相等,在String类中就覆盖了这个方法。标准的java类库中有超过150个equals方法的实现。然而java语言规范要求equals方法具有以下性质:

自反性:对于任何非空引用x, x.equals(x)返回true。
对称性:对于任何非空引用x和y, 当且仅当y.equals(x)返回true时,x.equals(y)返回true。
传递性:对于任何引用x,y和z,如果x.equals(y)返回true并且y.equals(z)也返回true,那么x.equals(z)应该返回true。
一致性:如果x和y引用的对象没有改变,那么x.equals(y)的重复调用应该返回同一结果。
对于任何非空引用x, x.equals(null)应该返回false。
这5条规则显示良好的逻辑性,想要写好自己的equals方法,必须满足它们。那么如何才能做到这些规则呢?

假设当前equals方法传进来的参数名为otherObject,

测试this是否与otherObject相等。
测试otherObject是否为null,如果是,一定要返回false。
测试this通otherObject是否同属于一个类。这是为了满足规则2。
把otherObject强制转换为另一个变量(这里称为other):Classname other = (Classname) otherObject。然后比较所有的字段。使用 == 比较基本类型字段,使用equals方法比较对象字段。如果所有的字段都匹配返回true,否则返回false。
下面举一个简单的例子:

class TestEqual{
int number;
String s;
public boolean equals(Object otherObject){
//首先看看这两个对象引用是否相等
if (this == otherObject)
return true;
//这是为了满足规则5
if(otherObject == null)
return false;
//如果两个对象所属类型不同,它们不可能相等
if(getClass() != otherObject.getClass())
return false;
TestEqual other = (TestEqual)otherObject;
//比较所有字段
return s.equals(otherObject.s) && number == otherObject.number;
}
}

这个例子虽然很简单,但却清晰地反映出了以上所说的要点。

还有一个需要注意的地方是,在子类中,首先要调用超类的equals方法,如果这项测试无法通过,那么两个对象不可能相等。也就是:

if(!super.equals(otherObject)) return false;


到这里相信大家已经对编写自己的equals方法有了一定的认识,只要在平时写程序的时候多注意一下,写好它并不难。

再来看看本文的第二大主题:



如何编写自己的clone方法
类似equals方法,我们先来讨论一下Object的clone方法效果是怎样的。不过在此之前,有个问题要提一下,所有使用clone方法的类,不论是继承Object.clone()还是覆盖它,都必须实现一个名为cloneable的接口。如果你打开java类库查一下,会发现这个接口里什么也没有,它仅仅是表明某个类具有被clone的能力。

用一个例子来研究一下Object.clone()的复制效果:

public class TestClone1 implements Cloneable{
int count;
TestClone1 next;

public TestClone1(int count) {
this.count=count;
if(count>0)
next=new TestClone1(count-1);
}

void add(){
count++;
if(next!=null)
next.count++;
}

public String toString(){
String s=String.valueOf(count)+" ";
if(next!=null)
s+=next.toString();
return s;
}

public Object clone(){
Object o=null;
//如果没有实现cloneable,将会抛出CloneNotSupported异常
try{
o=super.clone();
}
catch(CloneNotSupportedException e){
System.err.println("cannot clone");
}
return o;
}

public static void main(String[] args){
TestClone1 t=new TestClone1(1);
System.out.println("t="+t);
TestClone1 t1=(TestClone1)t.clone();
System.out.println("t1="+t1);
t.add();
System.out.println("after added\nt t="+t+"\nt1="+t1)
}
}


在这个例子中创建t相当于两个相连的TestClone1实例,而在调用了t的add方法之后,意想不到的结果出现了:
t=1 0
t1=1 0
after added
t t=2 1
t1=1 1
t1也发生了改变,这点也许有些出乎你的意料。实际上Object.clone()进行的复制有着"bitwise"原则,也就是逐位复制。对于一个对象中定义的对象,它只是简单的复制这个对象的引用。这也就是常说的浅层拷贝(shallow copy),明白了这一点,想要执行深层拷贝(deep copy)也就不难了。

只需要在TestClone1 t1=(TestClone1)t.clone();后面加上t1.next=(TestClone1)t.next.clone();就能得到:

t=1 0
t1=1 0
after added
t t=2 1
t1=1 0
这个正确的结果。

接下来要介绍的是,如何控制clone能力。从前面可以知道,clone()是protected的,你必须覆盖并实现cloneable接口并处理必要的异常才能使用它。这样,你在设计自己的类时,有以下几种风格来控制clone能力:

不理会clone(),也就是在你的类中不涉及任何有关clone()的内容,如果有人想实现clone功能,必须写一个子类并完成刚才所说的要求。这适合于Object.clone()可以胜任复制你的类中全部字段的情况。
支持clone()。你在你的类中实现cloneable接口并且覆盖clone方法以及捕获相应的异常。
有条件的支持clone()。这种情况有些特殊,也就是你的类中保存着一些可能不能被复制的对象的引用(这里是指它们没有实现cloneable)。这时候你只是在clone()中复制所有这些对象,如果遇到异常,抛出所有这些异常给使用你的类的人。举个例子,假设你在写一个类似于ArrayList的类,你不知道别人会在把什么样的对象保存在你的ArrayList里,所以你也不知道他们是否能够被复制。
不实现cloneable接口但覆盖clone方法为protected。这样你虽然让你的类不能被复制,但却提供了正确的clone方法,这样如果有人想复制你的类,写一个子类实现cloneable接口就行了。
不实现cloneable接口并覆盖clone方法直接抛出异常来防止你的类被复制。不过这并不是根本的解决方法,只能对付那些直接调用你的clone方法或是在子类中调用super.clone()的人。
使用final修饰你的类来防止你的类被复制。如果你的超类中有覆盖clone方法的,再重新覆盖clone方法并直接抛出异常。这也是最彻底的办法。

haohao   2005-04-16 18:36:53 阅读:921  评论:0  引用:0

侯捷观点

Java反射机制



摘要

Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。本文借由实例,大面积示范Reflection APIs。



关于本文:

读者基础:具备Java 语言基础。

本文适用工具:JDK1.5



关键词:

Introspection(内省、内观)

Reflection(反射)





有时候我们说某个语言具有很强的动态性,有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定(dynamic binding)、动态链接(dynamic linking)、动态加载(dynamic loading)等。然而“动态”一词其实没有绝对而普遍适用的严格定义,有时候甚至像对象导向当初被导入编程领域一样,一人一把号,各吹各的调。



一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。



尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods1。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。



Java如何能够做出上述的动态特性呢?这是一个深远话题,本文对此只简单介绍一些概念。整个篇幅最主要还是介绍Reflection APIs,也就是让读者知道如何探索class的结构、如何对某个“运行时才获知名称的class”生成一份实体、为其fields设值、调用其methods。本文将谈到java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等等classes。



“Class”class

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被改写的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。



Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()),不能够!因为Class并没有public constructor(见图1)。本文最后我会拨一小块篇幅顺带谈谈Java标准库源码的改动办法。



Class是Reflection故事起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs。这些APIs将在稍后的探险活动中一一亮相。



#001 public final

#002 class Class<T> implements java.io.Serializable,

#003 java.lang.reflect.GenericDeclaration,

#004 java.lang.reflect.Type,

#005 java.lang.reflect.AnnotatedElement {

#006 private Class() {}

#007 public String toString() {

#008 return ( isInterface() ? "interface " :

#009 (isPrimitive() ?
"" : "class "))

#010 + getName();

#011 }

...

图1:Class class片段。注意它的private empty ctor,意指不允许任何人经由编程方式产生Class object。是的,其object 只能由JVM 产生。



“Class” object的取得途径

Java允许我们从多种管道为一个class生成对应的Class object。图2是一份整理。

Class object 诞生管道
示例

运用getClass()

注:每个class 都有此函数
String str =
"abc";

Class c1 = str.getClass();

运用

Class.getSuperclass()2
Button b = new Button();

Class c1 = b.getClass();

Class c2 = c1.getSuperclass();

运用static method

Class.forName()

(最常被使用)
Class c1 = Class.forName (
"java.lang.String");

Class c2 = Class.forName (
"java.awt.Button");

Class c3 = Class.forName (
"java.util.LinkedList$Entry");

Class c4 = Class.forName (
"I");

Class c5 = Class.forName (
"[I");

运用

.class 语法
Class c1 = String.class;

Class c2 = java.awt.Button.class;

Class c3 = Main.InnerClass.class;

Class c4 = int.class;

Class c5 = int[].class;

运用

primitive wrapper classes

的TYPE 语法


Class c1 = Boolean.TYPE;

Class c2 = Byte.TYPE;

Class c3 = Character.TYPE;

Class c4 = Short.TYPE;

Class c5 = Integer.TYPE;

Class c6 = Long.TYPE;

Class c7 = Float.TYPE;

Class c8 = Double.TYPE;

Class c9 = Void.TYPE;



图2:Java 允许多种管道生成Class object。



Java classes 组成分析

首先容我以图3的java.util.LinkedList为例,将Java class的定义大卸八块,每一块分别对应图4所示的Reflection API。图5则是“获得class各区块信息”的程序示例及执行结果,它们都取自本文示例程序的对应片段。



package java.util;
//(1)

import java.lang.*;
//(2)

public class LinkedList<E>
//(3)(4)(5)

extends AbstractSequentialList<E>
//(6)

implements List<E>, Queue<E>,

Cloneable, java.io.Serializable
//(7)

{

private static class Entry<E> { … }
//(8)

public LinkedList() { … }
//(9)

public LinkedList(Collection<? extends E> c) { … }

public E getFirst() { … }
//(10)

public E getLast() { … }

private transient Entry<E> header = …;
//(11)

private transient int size = 0;

}

图3:将一个Java class 大卸八块,每块相应于一个或一组Reflection APIs(图4)。



Java classes 各成份所对应的Reflection APIs

图3的各个Java class成份,分别对应于图4的Reflection API,其中出现的Package、Method、Constructor、Field等等classes,都定义于java.lang.reflect。

Java class 内部模块(参见图3)
Java class 内部模块说明
相应之Reflection API,多半为Class methods。
返回值类型(return type)

(1) package
class隶属哪个package
getPackage()
Package

(2) import
class导入哪些classes
无直接对应之API。

解决办法见图5-2。


(3) modifier
class(或methods, fields)的属性


int getModifiers()

Modifier.toString(int)

Modifier.isInterface(int)
int

String

bool

(4) class name or interface name
class/interface
名称getName()
String

(5) type parameters
参数化类型的名称
getTypeParameters()
TypeVariable <Class>[]

(6) base class
base class(只可能一个)
getSuperClass()
Class

(7) implemented interfaces
实现有哪些interfaces
getInterfaces()
Class[]



(8) inner classes
内部classes
getDeclaredClasses()
Class[]

(8') outer class
如果我们观察的class 本身是inner classes,那么相对它就会有个outer class
getDeclaringClass()
Class

(9) constructors
构造函数getDeclaredConstructors()
不论 publicprivate 或其它access level,皆可获得。另有功能近似之取得函数。
Constructor[]

(10) methods
操作函数getDeclaredMethods()
不论 publicprivate 或其它access level,皆可获得。另有功能近似之取得函数。
Method[]

(11) fields
字段(成员变量)
getDeclaredFields()不论 publicprivate 或其它access level,皆可获得。另有功能近似之取得函数。
Field[]



图4:Java class大卸八块后(如图3),每一块所对应的Reflection API。本表并非

Reflection APIs 的全部。



Java Reflection API 运用示例

图5示范图4提过的每一个Reflection API,及其执行结果。程序中出现的tName()是个辅助函数,可将其第一自变量所代表的“Java class完整路径字符串”剥除路径部分,留下class名称,储存到第二自变量所代表的一个hashtable去并返回(如果第二自变量为null,就不储存而只是返回)。



#001 Class c = null;

#002 c = Class.forName(args[0]);

#003

#004 Package p;

#005 p = c.getPackage();

#006

#007 if (p != null)

#008 System.out.println(
"package "+p.getName()+";");



执行结果(例):

package java.util;

图5-1:找出class 隶属的package。其中的c将继续沿用于以下各程序片段。



#001 ff = c.getDeclaredFields();

#002 for (int i = 0; i < ff.length; i++)

#003 x = tName(ff<i>.getType().getName(), classRef);

#004

#005 cn = c.getDeclaredConstructors();

#006 for (int i = 0; i < cn.length; i++) {

#007 Class cx[] = cn<i>.getParameterTypes();

#008 for (int j = 0; j < cx.length; j++)

#009 x = tName(cx[j].getName(), classRef);

#010 }

#011

#012 mm = c.getDeclaredMethods();

#013 for (int i = 0; i < mm.length; i++) {

#014 x = tName(mm<i>.getReturnType().getName(), classRef);

#015 Class cx[] = mm<i>.getParameterTypes();

#016 for (int j = 0; j < cx.length; j++)

#017 x = tName(cx[j].getName(), classRef);

#018 }

#019 classRef.remove(c.getName());
//不必记录自己(不需import 自己)



执行结果(例):

import java.util.ListIterator;

import java.lang.Object;

import java.util.LinkedList$Entry;

import java.util.Collection;

import java.io.ObjectOutputStream;

import java.io.ObjectInputStream;

图5-2:找出导入的classes,动作细节详见内文说明。



#001 int mod = c.getModifiers();

#002 System.out.print(Modifier.toString(mod));
//整个modifier

#003

#004 if (Modifier.isInterface(mod))

#005 System.out.print(
" "); //关键词 "interface" 已含于modifier

#006 else

#007 System.out.print(
" class "); //关键词 "class"

#008 System.out.print(tName(c.getName(), null));
//class 名称



执行结果(例):

public class LinkedList

图5-3:找出classinterface 的名称,及其属性(modifiers)。



#001 TypeVariable<Class>[] tv;

#002 tv = c.getTypeParameters();
//warning: unchecked conversion

#003 for (int i = 0; i < tv.length; i++) {

#004 x = tName(tv<i>.getName(), null);
//例如 E,K,V...

#005 if (i == 0)
//第一个

#006 System.out.print(
"<" + x);

#007 else
//非第一个

#008 System.out.print(
"," + x);

#009 if (i == tv.length-1)
//最后一个

#010 System.out.println(
"]");

#011 }



执行结果(例):

public abstract interface Map<K,V>

public class LinkedList<E>

图5-4:找出parameterized types 的名称



#001 Class supClass;

#002 supClass = c.getSuperclass();

#003 if (supClass != null)
//如果有super class

#004 System.out.print(
" extends" +

#005 tName(supClass.getName(),classRef));



执行结果(例):

public class LinkedList<E>

extends AbstractSequentialList,

图5-5:找出base class。执行结果多出一个不该有的逗号于尾端。此非本处重点,为简化计,不多做处理。



#001 Class cc[];

#002 Class ctmp;

#003
//找出所有被实现的interfaces

#004 cc = c.getInterfaces();

#005 if (cc.length != 0)

#006 System.out.print(
", \r\n" + " implements "); //关键词

#007 for (Class cite : cc)
//JDK1.5 新式循环写法

#008 System.out.print(tName(cite.getName(), null)+
", ");



执行结果(例):

public class LinkedList<E>

extends AbstractSequentialList,

implements List, Queue, Cloneable, Serializable,

图5-6:找出implemented interfaces。执行结果多出一个不该有的逗号于尾端。此非本处重点,为简化计,不多做处理。



#001 cc = c.getDeclaredClasses();
//找出inner classes

#002 for (Class cite : cc)

#003 System.out.println(tName(cite.getName(), null));

#004

#005 ctmp = c.getDeclaringClass();
//找出outer classes

#006 if (ctmp != null)

#007 System.out.println(ctmp.getName());



执行结果(例):

LinkedList$Entry

LinkedList$ListItr

图5-7:找出inner classes 和outer class



#001 Constructor cn[];

#002 cn = c.getDeclaredConstructors();

#003 for (int i = 0; i < cn.length; i++) {

#004 int md = cn<i>.getModifiers();

#005 System.out.print(
" " + Modifier.toString(md) + " " +

#006 cn<i>.getName());

#007 Class cx[] = cn<i>.getParameterTypes();

#008 System.out.print(
"(");

#009 for (int j = 0; j < cx.length; j++) {

#010 System.out.print(tName(cx[j].getName(), null));

#011 if (j < (cx.length - 1)) System.out.print(
", ");

#012 }

#013 System.out.print(
")");

#014 }



执行结果(例):

public java.util.LinkedList(Collection)

public java.util.LinkedList()

图5-8a:找出所有constructors



#004 System.out.println(cn<i>.toGenericString());



执行结果(例):

public java.util.LinkedList(java.util.Collection<? extends E>)

public java.util.LinkedList()

图5-8b:找出所有constructors。本例在for 循环内使用toGenericString(),省事。



#001 Method mm[];

#002 mm = c.getDeclaredMethods();

#003 for (int i = 0; i < mm.length; i++) {

#004 int md = mm<i>.getModifiers();

#005 System.out.print(
" "+Modifier.toString(md)+" "+

#006 tName(mm<i>.getReturnType().getName(), null)+
" "+

#007 mm<i>.getName());

#008 Class cx[] = mm<i>.getParameterTypes();

#009 System.out.print(
"(");

#010 for (int j = 0; j < cx.length; j++) {

#011 System.out.print(tName(cx[j].getName(), null));

#012 if (j < (cx.length - 1)) System.out.print(
", ");

#013 }

#014 System.out.print(
")");

#015 }



执行结果(例):

public Object get(int)

public int size()

图5-9a:找出所有methods



#004 System.out.println(mm<i>.toGenericString());



public E java.util.LinkedList.get(int)

public int java.util.LinkedList.size()

图5-9b:找出所有methods。本例在for 循环内使用toGenericString(),省
haohao   2004-12-25 21:44:27 阅读:1754  评论:1  引用:0
http://www.jjhou.com/javatwo-2004-reflection-and-generics-in-jdk15-sample.ZIP
http://editblog.csdn.net/programmer/archive/2004/10/27/806.aspx
haohao   2004-12-21 20:50:30 阅读:1507  评论:0  引用:0
dao
使用 Hibernate 将 Java 对象持久保存到 IBM DB2 通用数据库中
http://www900.ibm.com/developerWorks/cn/dmdd/library/techarticles/0306bhogal/0306bhogal.shtml
Hibernate中文网
http://www.hibernate.org.cn/
haohao   2004-12-16 19:48:46 阅读:1380  评论:0  引用:0

names.java
import java.util.*;
public class names implements Comparable
{
private String firstName;
public names(String firstName)
{
if(firstName==null)
{
throw new NullPointerException();
}
this.firstName=firstName;

}
}
_______________
NameSort.java
import java.util.*;
class NameSort
{
public static void main(String args[])
{
names n=new names(
"niaho");
System.out.println(n);
}
}
运行结果 names@480457
haohao   2004-11-23 20:13:07 阅读:1509  评论:0  引用:0

Name.java
import java.util.*;
public class Name implements Comparable
{
private String firstName,lastName;
public Name(String firstName,String lastName)
{
if(firstName==null || lastName==null)
{
throw new NullPointerException();
}
this.firstName=firstName;
this.lastName=lastName;
}
public String firstName()
{
return firstName;
}
public String lastName()
{
return lastName;
}
public boolean equals(Object o)
{
if(!(o instanceof Name))
return false;
Name n=(Name)o;
return n.firstName.equals(firstName)&& n.lastName.equals(lastName);
}
public int hashcode()
{
return 31*firstName.hashCode()+lastName.hashCode();
}
public String toString()
{
return firstName+
""+lastName;
}
public int compareTo(Object o)
{
Name n=(Name)o;
int lastCmp=lastName.compareTo(n.lastName);
return (lastCmp!=0 ? lastCmp :firstName.compareTo(n.firstName));
}
}
_________________________
NameSort.java
import java.util.*;
class NameSort
{
public static void main(String args[])
{
Name n=new Name(
"niaho","sirigeleng");
System.out.println(n);
}
}
运行结果 niahosirig
haohao   2004-11-23 20:07:59 阅读:1500  评论:2  引用:0

作者: Builder.com

如果需要在Java中使用排序方法,那么就需要实现Comparable接口。

public interface java.lang.Comparable {
public int compareTo(Object o);
}



Comparable接口很简单,因为它只有一个方法;然而如果你需要根据多个属性对对象进行排序,那么实现compareTo方法就会变得很复杂(尤其是在你的类有很多这样的属性的时候)。

如果需要实现Comparable接口,可以让BeanUtils替你完成这个工作。BeanComparator是BeanUtils API中的一个类,使用它可以很简单地根据任何属性对Bean类进行排序。

下面是一个完整的例子。注意,代码创建了BeanComparator并实现了compareTo方法。方法main只是简单地构建一个需要排序的对象列表并显示排序前和排序后的结果。

在创建BeanComparator对象的时候,我们必须告诉它需要根据类中的哪个属性进行排序。在提供的例子中,根据属性name用来排序。

private BeanComparator comparator = new BeanComparator(
"name");

完成所有工作的代码都在compareTo方法中。

return this.comparator.compare(this, o);

BeanComparator的compare方法接收两个参数,对参数进行对比之后返回一个整数代表小于、等于或者大于。

如果你正在实现一个comparator来动态地比较属性(例如,根据用户选择的某一列对一个Web页面上的一个表中的所有行进行排序),然后你可以推迟构建comparator直到你知道哪个属性被选择用来进行排序。这是BeanComparator真正发光的地方。以往实现这个行为需要撰写的大量代码在使用BeanComparator以后锐减为几行:

import java.util.Collections;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Random;
import org.apache.commons.beanutils.BeanComparator;

public class CompareTipA implements Comparable {
private int id;
private String name;
private BeanComparator comparator = new BeanComparator(
"name");

private static String []names =
{
"John", "Malik", "Susan", "Chaquitha", "Cheryl", "Mike", "Henri",
"Jason", "Eric", "Jason", "Vivek", "Jakob", "Revathy", "Jim",
"Sterling",
"Dana", "Jill", "Amrita", "Heather", "Jack", "David", "Bethany",
"Karol",
"Phil", "Margaret", "Betty", "Perry", "Scott", "Dexter"};

public static void main(String []args) {
int count = 30;
Random rand = new Random(System.currentTimeMillis());
ArrayList list = new ArrayList();

for (int i = 0; i < count; i++) {
int id = rand.nextInt(10000);
String name = names[rand.nextInt((names.length))];

list.add(new CompareTipA(id, name));
}

System.out.println(
"unsorted:");
Iterator tor = list.iterator();
while (tor.hasNext()) {
CompareTipA ct = (CompareTipA) tor.next();
System.out.println(ct);
}

Collections.sort(list);

System.out.println(
"\nsorted:");
tor = list.iterator();
while (tor.hasNext()) {
CompareTipA ct = (CompareTipA) tor.next();
System.out.println(ct);
}
}

public CompareTipA(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return this.id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public int compareTo(Object o) {
return this.comparator.compare(this, o);
}

public String toString() {
return
"[id=" + this.id + ",name=" + this.name + "]";
}
}


haohao   2004-11-23 19:15:26 阅读:1446  评论:0  引用:0

testFace.java
public interface testFace
{
public void getName();
}
test.java
class test implements testFace
{
public test(String a)
{
System.out.println(a);
}
public void getName()
{
System.out.println(
"nihao");

}
public static void main(String args[])
{
testFace l=new test(
"xiexie");
}
}

接口是用定义类型的
haohao   2004-11-18 19:37:37 阅读:1280  评论:1  引用:0

一切版权属于个人(转载例外)