2010年8月2日

Java注解使用的几个小技巧:

  最近经常用到注解,总结了几个注解使用的小技巧,现整理如下:

 一、使用默认的注解参数:

使用default关键字可以指定注解的默认参数:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface SQL {

String sql() default "";

}

如果我们在使用注解@SQL的时候没有显式的去指定sql参数,那么就默认采取default关键字所指定的值

二、用value指定默认值

我们经常看到很多注解是这种形式,例如:@SQL("select email from user")

这个注解里面的参数为什么没有带名称呢?其实它的注解是这样定义的:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface SQL {

String value() default "";

}

三、在注解中使用数组参数

如果你用过Struts2的注解来配置Action,那么你会看到如下形式的注解:

@Results({   

       @Result(name="success",value=xxx

.jsp",type=org.apache.struts2.dispatcher.ServletRedirectResult.class),   

       @Result(name="input",value="/xxx

.jsp",type=org.apache.struts2.dispatcher.ServletRedirectResult.class)   

})   

怎么来创建这种注解呢?

其实可以把@Results的参数看作是一个@Result的数组

定义如下:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Result {

}

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Results {

Result[] value();   //没什么特别的,只是接受了一个数组参数而已

}

四、让注解可以去修饰多个Target类型

我么使用@Target注解来指定某个注解可以修饰的类型,实际上,同上面一样,@Target接受的是一个数组参数,利用这一特性我们可以让注解来修饰多个类型。

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target({ElementType.PARAMETER,ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

public @interface Param {

}

OK!这个注解既可以修饰参数也可以修饰类的属性

posted @ 2010-08-02 14:31 迟宏泽 阅读(3790) | 评论 (0)编辑 收藏

2010年8月1日

使用Java来开发JSP标记
    最近复习JSP中。我们知道JSP是Java WEB的一个规范。在这个规范之内我们可以自己去做很多事情。JSP API提供了接口允许我们自己去自定义JSP标签和EL表达式。自定义JSP标签的大致思想是这样的,实现一个包含标签参数和行为逻辑的类,这个类我们称它为标记处理器类,它实质上是一个Bean,我们设置标签的属性实际上是调用它的setter方法。接下来通过使用标记库描述文件(TLD)来描述标签的相关信息,以便在JSP页面中使用taglib指令时能顺利的找到它。下面我们来看看详细应该怎么去做。
    从JSP2.0开始引入了一个简单标记处理器接口——javax.servlet.jsp.tagext.SimpleTag接口,这个接口取代了原先JSP1.0提供的3个标记处理器接口,我们只需要使用SimpleTarget接口就可以实现所有类型的JSP标记。之所以称之为简单,是因为这样设计让开发者在编写程序上省了不少力气,但是它可以实现很复杂的功能,并不是说它的功能很简单。
    该接口包含了如下5个方法:
    void doTag();  //该方法包含了标签执行的业务逻辑
    JspTag getParent();  //获取该标签的父标签
    setJspBody(JspFragment body); //设置JSP标签体,关于JspFragment类我们稍后再讨论
    setJspContext(JspContext ctx);//设置JSP上下文
    setParent(JspTag parent);  //设置父标签
但是多数情况下我们不需要去亲自实现这个接口,因为作为开发者,我们关心的是业务逻辑,也就是doTag()方法的内容,这种情况我们只需要继承javax.servlet.jsp.tagext.SimpleTagSupport类即可,这个类为我们实现了SimpleTag接口,我们只需要去重写它的doTag()方法。下面我们来实现一个最简单的标签:给标签传递一个参数,让它在页面上显示出来.
首先我们要做的是写一个标记处理器类:
package test.jsp.tag.simple;

import java.io.IOException;

import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport{
   
    private String name;
   
    public void setName(String name){
        this.name = name;
    }

    @Override
    public void doTag()throws IOException{
        this.getJspContext().getOut().print("Hello! "+name);
    }
}

嗯,这是一个继承了SimpleTagSupport类的Bean,一样拥有属性、setter方法。唯一特别的是重写了父类的doTag()方法。在这个doTag()中,我们通过获得一个JSP上下文对象来向页面输出一句话:Hello!XXX。

下面是该介绍JspContext对象的时候了,这是一个抽象类,它唯一的子类是PageContext。这么说刚才所返回的实际对象是PageContext类型的。我们可以对其进行强制转换:PageContext pageCtx = (PageContext)this.getJspContext();

通过PageContext对象我们就可以访问到Request和Response对象了,如:

HttpServletRequest request = (HttpServletRequest)pageCtx.getRequest();
HttpServletResponse response = (HttpServletResponse)pageCtx.getResponse();

通过这种方式我们就可以在标签处理器中操作WEB应用各个作用域的数据了。

我们都知道在使用JSP标签时要在JSP页面开头使用taglib指令进行声明,如:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
taglib指令通过URL来对标签进行定位。但是它是如何做到的呢?这就要靠TLD了,TLD就是标记库描述文件(Tag Library Descriptor,TLD),我们看JSTL的JAR包中,META-INF文件夹下每个库都有一个对应的部署描述文件。TLD实际上是一个XML文件,里面描述了标记库和其中标记的信息。一个完整的TLD文件结构如下所示:


<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
   
  <tlib-version>1.1</tlib-version>
  <short-name>test</short-name>
  <uri>http://test</uri>
 
  <tag>
    <name>hello</name>
    <tag-class>test.jsp.tag.simple.HelloTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
      <name>name</name>
      <required>true</required>
    </attribute>
  </tag>
 
  <tag>
    <name>if</name>
    <tag-class>test.jsp.tag.simple.IfTag</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
      <name>test</name>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
  <tag>
    <name>foreach</name>
    <tag-class>test.jsp.tag.simple.ForeachTag</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
      <name>items</name>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
    <attribute>
      <name>var</name>
    </attribute>
  </tag>

  <function>
   <description>
      Just for test
   </description>
   <name>add</name>
   <function-class>test.jsp.el.AddEl</function-class>
   <function-signature>int add(int,int)</function-signature>
  </function>
</taglib>

起始标签是taglib,<tlib-version>定义了taglib的版本号,<short-name>定义了标签库的简称,<uri>定义了URI标识,taglib就是通过此URI来找到相关的标记库的。
接下来的<tag>标签便是定义JSP标签的属性了:
以下是必须的属性
<name>标签名称
<tag-class>标记器类
<body-content>动作体类型,JSP2.0支持如下动作体:
empty 空标记
jsp:可以包含标签、EL和Scriptlet
scriptless:不允许Java脚本内容
tagdependent:对体不进行处理
<attribute>标签定义了标签参数。
其中:
<name>标签指定了参数的名称。
<required>指定了参数是否是必要的。true是必要,false是不必要
<rtexprvalue>指定了是否允许使用表达式(包括EL表达式和Java表达式),true为允许,false为不允许

<funtion>标签定义了EL表达式,我们稍后再做介绍。


如何执行动作体?
我们在编写JSP页面时经常会用到<c:if>和<c:forEach>之类的标签
<c:if test="true">
  <p>条件是真的,执行!</p>
</c:if>
只要满足条件,就会去执行动作体,那么我们怎么在自定义JSP标记中去执行动作体呢?
首先我们先要来研究一个叫做JspFragment的类。JspFragment代表了JSP标签的动作体,我们通过它的invoke方法就可以执行动作体,下面我们来实现一个类似于<c:if>的标签:
package test.jsp.tag.simple;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class IfTag extends SimpleTagSupport{

    private boolean test;
   
    public void setTest(boolean test){
        this.test = test;
    }
   
        @Override
    public void doTag()throws IOException,JspException{
        JspFragment body = getJspBody();
        //如果成立则执行相应逻辑
        if(test){
            body.invoke(null);
        }
    }
}
只要满足了条件,标签动作体就会执行。
invoke方法接受一个java.io.Writer对象,使用这个对象将内容输出到页面,如果不输出则可以给它传递一个null参数。

JSP中引入了EL表达式给我们的开发带来了很大的方便,同自定义标签一样,允许开发者去自定义EL表达式函数,定义EL表达式函数要比定义标签简单多了,我们甚至不需要去实现任何接口,假设我们要实现一个简单的EL表达式函数来计算两个数的和,就可以这样:

package test.jsp.tag.simple;

public class Calculator {

    public static int add(int a,int b){
        return a+b;
    }
   
    public static int minus(int a,int b){
        return a-b;
    }
   
    public static int multiply(int a,int b){
        return a*b;
    }
   
    public static int divide(int a,int b){
        return a/b;
    }
}

这个类中全部是static方法,这是一个工具类。
然后我们只需要在TLD声明即可:

<function>
   <description>
      Just for test
   </description>
   <name>add</name>
   <function-class>test.jsp.el.AddEl</function-class>
   <function-signature>int add(int,int)</function-signature>
</function>

function-signature类似于C语言中的函数声明,通过这个找到到底调用类中哪个工具方法。
使用的时候只需要这样就可以了:${test:add(1,2)}运行时输出结果为3


posted @ 2010-08-01 15:10 迟宏泽 阅读(2980) | 评论 (1)编辑 收藏

2010年7月31日

戏说Java泛型 
Sun在Java 5之后的版本中引入了泛型技术(Generic).泛型是指具有一个或多个类型参数的类或者是接口.泛型技术其实在C++的STL中早已广泛使用,在Java中泛型主要是用来构建安全的集合,我们在使用JCF(Java Collections Framework)时经常碰到它们的身影.当然泛型还有很多用处,它可以大大提高程序的可复用性。但是至少有90%的Java程序员都只在构建集合时使用这种技术.在Java中引入泛型技术之前,我们使用集合时,可以向其中加入任何对象,这就造成了很多不安全的因素,例如: 

//in the main() 
          …… 
List intList=new ArrayList(); 

intList.add(new Integer(10)); 
intList.add(new Integer(20)); 
intList.add(new Integer(30)); 
intList.add(new String("Sam")); 

int sum=getSum(intList); 

         …… 


public static int getSum(List intListIn){ 
   Iterator i=intList.iterator();     //获取迭代器对象 
   int sum=0; 
   while(i.hasNext()){ 
     Integer num=(Integer)i.next();  //将集合中得到的对象强制转换为Integer,Note:这里最容易出现问题! 
     sum+=num.intValue();            /*进行拆箱操作,当然在Java5之后的版本这个动作可以自动完成,我们这里模拟的是Java1.4,研究泛型我们需要复古~ 
   }                                  *这是一门新的科学,叫做代码考古学^_^ */ 
   return sum; 


这个程序会编译成功!并且不会出现警告!但是在运行时会抛出ClassCastException这种运行时异常,String不能通过Integer的instanceof测试,并且我们从集合中取出元素时,因为返回的类型都是Object,我们必须对其进行强制转换,嗯~这个操作是我最讨厌的——不但麻烦,多敲了好几下键盘,而且非常不安全,我不敢确定这个Object是我所希望转换成的类型,如果真要做这种操作,希望大家都先进行一下instanceof测试,安全第一,受罪第二,不过现在确保安全是为了以后受更少的罪!将来在维护程序时,你甚至可能会因为这个小小的原因而一怒之下产生想重写整个程序的冲动!嗯~实不相瞒,我就这样做过~血的教训! 

但是有了泛型一切都好了起来,我们可以告诉编译器每个集合中接受哪些对象类型,编译器会自动为你做转换工作,这样以来我们在编译时就知道是否向集合中插入了类型错误的元素. 

List<Integer> intList=new ArrayList<Integer>(); 

现在intList这个集合就只能够接受Integer类型的对象: 
intList.add(new Integer(12)); 
如果我们向其中加入一个非Integer的对象,那么编译器将报错! 


考虑这种情况: 
List<E> list=new ArrayList<E>; 
如果F是E的子类型,list中能添加F类型的对象吗?当然可以!同数组一样,子类都可以加入到父类型的集合中,另外对于接口也是如此,比如Bird类和Plane类都实现了一个Flyable的接口,我们想要在一个集合中放置所有"具有飞行能力"的对象,就可以很简单的这样做: 
List<Flyable> flyerList=new ArrayList<Flyable>(); 
flyerList.add(new Bird()); 
flyerList.add(new Plane()); 
这样很和谐,不是吗?像List<Object>这种形式的集合当然就可以容纳天下所有类型的对象了! 

您可能会疑问,List<Object>能够容纳所有的对象,那么它不就与原生态(我们将不带任何泛型信息的类型成为原生态类型,原生态是一个很时髦的东西,但是在Java中我们要避免它)的List相同了吗?也就是说: 
List list=new ArrayList(); 
List<Object> objList=new ArayList<Object>(); 
上述这两行代码所产生的东西是完全一样的吗?其实它们是有差距的,我们通过一个小程序来验证: 

        public static void main(String[] args){ 
List<String>names=new ArrayList<String>(); 
names.add("Sam"); 
names.add("Jack"); 
names.add("James"); 
names.add("Lucy"); 
sayAllNames(names); 


private static void sayAllNames(List list){               //我们在这里用的是原生态集合参数 
Iterator iterator=list.iterator(); 
while(iterator.hasNext()){ 
System.out.println(iterator.next()); 




程序运行的非常顺利!它可以喊出集合中包含的所有名字,同时我们也看到,我们可以将List<String>传递给一个接收原生态List的方法.如果你擅长向编译器"找茬"的话,可能会发现这个地方似乎有一个漏洞,然后你会尝试写出类似如下的代码: 

       public static void main(String[] args){ 
List<String>names=new ArrayList<String>(); 
names.add("Sam"); 
names.add("Jack"); 
names.add("James"); 
names.add("Lucy"); 
addElement(names); 


private static void addElement(List list){ 
list.add(new Integer(10)); 


我们将一个Integer对象插入到一个String类型的集合中.测试一下,程序能成功编译,但是会产生警告,如果你是在命令行下使用的javac命令进行编译,就可以看到这条警告:使用了未经检查或不安全的操作.如果使用现代IDE,例如Eclipse,也会看到用使人很不舒服的黄色曲线标识着list.add(new Integer(10))这条语句.可见编译器非常不希望我们这样做!但是之所以能通过编译,主要是为了保持移植兼容性,与过去的Java代码保持兼容. 


泛型最初加入到Java中并不受欢迎.Sun在Java中引入泛型技术最大的挑战就是做到使具有类型安全的泛型类和原来的原生态类能够协同工作,然而这样就会如上述代码所示,引起许多棘手的代码安全问题,编译器就只能就这个问题发出警告,提醒程序员最好不要这样做.这些安全隐患我们总是在程序运行时才能发现,JVM对于泛型这种东西毫无概念!泛型概念对于编译器而言是严格的,编译器在编译具有泛型信息的代码时,会对泛型类型进行验证,然后执行一个类型擦除过程,也就是从类字节码中去掉这些信息!当JVM在运行时就看不到所谓的泛型了,这个就是编译器的"泛型阴谋",我们有必要了解这个事实. 


下面我们将原生态的List换成List<Object>,情况会怎么样呢? 

         public static void main(String[] args){ 
List<String>names=new ArrayList<String>(); 
names.add("Sam"); 
names.add("Jack"); 
names.add("James"); 
names.add("Lucy"); 
addElement(names); 


private static void addElement(List<Object> list){ 
list.add(new Integer(10)); 



嗯~它会出错!我们一再强调泛型是安全的,我们不能把一个List<String>传递给一个接受List<Object>参数的方法!如果您对Java数组比较熟悉,此时可能会产生疑问,您可能会经常碰见这样的代码: 
        
        Object[] objects=new Integer[]{1,2,3,4,5,6}; 

我们可以很正常的将一个子类型的数组赋给一个超类型的数组的引用,但是我们绝对不可以这样做: 
        List<Object>objList=new ArrayList<String>(); 
编译器是坚决不会让你通过的!嗯~你感到泛型不够人性化是不是?它要是能跟数组一样就方便了~但是你看一看下面的程序,也许你就不会这么想了,相反,你或许还会认为Java中的数组存在缺陷: 

        public static void main(String[] args){ 
Integer[] intArray=new Integer[]{1,2,3,4,5}; 
changeElement(intArray); 


public static void changeElement(Object[] objects){ 
objects[0]=new String("I Love Java7");       //Hi!问题出在这儿! 

嗯~你是不是发现同上面的某个程序类似?它同样可以通过编译,但是我们在运行时发现它会产生一个叫做ArrayStoreException的异常!顾名思义,我们向这个数组当中放入了它所不能接受的东西.这一切的祸根都是源于Java允许我们可以将一个子类型的数组赋给一个超类型的数组的引用~然而使用泛型就不会发生这种情况,编译器坚决阻止这种情况的存在! 

数组同泛型似乎是水火不相容,数组是具体化的对象,只有在程序运行时才能搞清楚它们的类型,而对于泛型,我们前面说过,它仅仅在编译的时候才会存在!基于这个原因我们不可以创建具有泛型信息的数组:例如new E[] new List<Object>[] 这些做法都是错误的!一定要注意! 


然而有一种神奇的方法能够将泛型为子类型的集合创递给泛型为父类型的引用,我们在Windows下查找文件时经常会用到通配符,比如"*.jpg"表示所有JPG类型的文件,在Java中也有一套适用于泛型的通配符,下面是一个运用了泛型通配符的程序: 


import java.util.*; 
public class Test { 

public static void main(String[] args){ 
List<Knight>knights=new ArrayList<Knight>(); 

knights.add(new Knight()); 
knights.add(new Knight()); 
knights.add(new Knight()); 

doAttack(knights); 


public static void doAttack(List<? extends Rpg>rpgs){ 
Iterator<? extends Rpg>iterator=rpgs.iterator(); 
while(iterator.hasNext()){ 
iterator.next().commonAttack();  //这里next()方法返回的是Rpg 





class abstract Rpg{ 
  
        public abstract void commonAttack(); 


class Knight extends Rpg{ 
    
       public void commonAttack(){ 
           System.out.println("The common attack of the knight is very strong!"); 
       } 


class Magician extends Rpg{ 

       public void commonAttack(){ 
            System.out.println("The common attack of the magician is very weak"); 
       } 


我们在上述程序中简单的构建了几个游戏的角色类,抽象类Rpg及它的子类Knight(骑士)和Magician(术士),所有的游戏角色都会进行普通攻击,然而攻击的能力各不相同,骑士的攻击强度比较大,而术士的普通攻击相对要弱一些,他们主要依靠高科技的魔法攻击~基于这个原则,我们在Rpg的子类中重写了这个方法.现在我们要做的是让一群骑士和术士进行战斗.用doAttack()方法来号召他们战斗.您或许早就注意了doAttack()的参数很奇怪:List<? extends Rpg> 

这个就是前面我们所说的泛型通配符,<? extends Rpg>表示可以接受泛型类型是Rpg或Rpg子类型的集合,并且,很重要的一点,千万不要向这些集合中添加任何元素,否则会引起编译错误,原因很简单,如果允许添加元素的话,很有可能将一个术士插入到一群骑士的队伍当中!有一个例外,你可以向其中添加null元素 

通配符?后的extends不仅代表着子类也代表的接口的实现,比如<? extends Serializable>表示所有实现泛型类型实现Serializable接口的集合.嗯~我没写错,的确是extends,尽管这是一个接口,记住,没有<? implements Serializable>这种形式,这就是语法,我们必须遵守 

除了extends,另外还有一个泛型通配符关键字super,关于它的作用,请阅读下面的程序: 


import java.util.*; 
public class Test { 

public static void main(String[] args){ 
List<Knight>knights=new ArrayList<Knight>(); 

knights.add(new Knight()); 
knights.add(new Knight()); 
knights.add(new Knight()); 

List<Rpg>rpgs=new ArrayList<Rpg>(); 

addKnight(rpgs); 



public static void addKnight(List<? super Knight>list){ 
list.add(new Knight()); 





class abstract Rpg{ 
  
        public abstract void commonAttack(); 


class Knight extends Rpg{ 
    
       public void commonAttack(){ 
           System.out.println("The common attack of the knight is very strong!"); 
       } 


class Magician extends Rpg{ 

       public void commonAttack(){ 
            System.out.println("The common attack of the magician is very weak"); 
       } 


你一眼就会注意到,我们终于可以用亲爱的add()方法来添加不是null的东西了!这都是super的功劳,List<? super Knight>的意思是凡是泛型类型为Knight和Knight超类的集合都能够接受,在程序中你可以看到。我们可以将新的骑士加入到骑士的队伍当中,或者是将骑士加入到混合角色的队伍当中,不会发生将骑士插入到专业的术士队伍当中这样的错误,因为编译器会阻止术士类型的集合传递到这个增加骑士的方法中 

然而当我们什么关键字都不使用呢?就像这样List<?> 你认为它等同于List<Object>吗?那么你错了,我们前面说过,为了安全,List<Object>只能接受泛型类型为Object类型的集合,而List<?>可以接受泛型类型为任何类型的集合!List<?>和List<? extends Object>是等同的,大家可以自己写程序测试一下!可以继续把我的骑士和术士的故事讲下去~ 

使用通配符时要注意,通配符只是针对引用声明使用,使用new生成对象时不可以使用通配符! 
List<? extends Rpg> rpgs=new ArrayList<Knight>();  //这是正确的 
List<? extends Rpg> magicians=new ArrayList<? super Magician>();  //这是错误的! 

我们说过,应用泛型可以大大提高程序的可复用性,下面我们将学习如何创建我们自己的泛型类,比上面的要简单,至少没有那么多的编译错误和异常,呵呵,可以把心情放松一下,下面的例子,我们构造一个使用泛型的链表节点: 

class Node<T>{                               
private T value;                    //节点所包含的值 
private Node<T> nextNode;           //节点所指向的下一个节点 

public Node(T valueIn){ 
value=valueIn; 
nextNode=null; 


public void setValue(T valueIn){ 
value=valueIn; 

public T getValue(){ 
return value; 


public void setNextNode(Node<T> nodeIn){ 
nextNode=nodeIn; 

public Node<T> getNextNode(){ 
return nextNode; 




如你所见,T就是泛型的标识符,然后在类中,我们可以像使用正常类一样使用泛型标识符,在类定义中可以使用多个泛型标识符: 
class Map<K,V>{ 
   …… 

我们也可以使用通配符来指定泛型所允许的范围: 
class RpgHolder<T extends Rpg>{       //只允许RPG及其子类 
  …… 


有时候我们不需要使用一个泛型类,我们只需要在普通类中简单的定义一个支持泛型的方法: 
public  <T extends Rpg> void makeRpgList(T t){ 

   List<T> rpgList=new ArrayList<T>(); 
   rpgList.add(t); 



首先我们要声明方法的泛型标识符<T extends Rpg>,然后像泛型类那样在方法中使用泛型 

Java 5出现之后,学习变得越来越困难,泛型技术是主要的困难因素之一,之所以困难主要是因为它要与以前的代码保持兼容,这就大大的增加了复杂性,您也看到了,前面的那一大堆问题~但是学习泛型技术是很有用的,增加了代码的可复用性,以及类型安全.本文主要阐述了一些简单的理论,大家平时要多练习,有很多事可以做,比如可以尝试用泛型去重新实现一些数据结构,优化一些常用的工具类,你会发现这是件非常有趣的事! 

下面是我自己写的一个类似List的集合——Tiny,当然比起JCF来在实际应用中性能不是很好,但是包含了基本的集合操作,习惯了JCF,很多数据结构的具体实现都忘得差不多了~得复习了~哈哈~闲着没事练手~练手~这个是使用一个长度可变的数组来实现的,大家可以尝试一下用链表来实现它的另一个版本LinkedTiny,这样可以省去变化数组长度的麻烦~ 
/*-------------- Iterator.java--------*/ 
package sam.adt; 

public interface Iterator<T> { 

boolean hasNext(); 
T next(); 


/*--------------- Tiny.java---------*/ 
package sam.adt; 
import java.io.*; 
public interface Tiny<T> extends Serializable{ 
void add(T valueIn);                    //将值添加到集合的尾部 
void remove();                          //删除集合的最后一个元素 
void removeFirst();                     //删除集合中的第一个元素 
void addToHead(T valueIn);              //将值添加到集合的头部 
boolean remove(T valueIn);              //删除值为valueIn的元素 
boolean add(int indexIn,T valueIn);     //在指定索引处添加值 
boolean remove(int indexIn);            //删除指定索引处的元素 
T get(int indexIn);                     //得到指定索引处的元素 
int indexOf(T valueIn);                 //获取指定值的索引 
boolean replace(int indexIn,T valueIn); //将index处的值替换为value 
int size();                             //获取集合中当前元素数目 
void clear();                           //清除整个集合中的元素 
boolean contain(T valueIn);             //测试集合中是否包含值为valueIn的元素 
Iterator<T> iterator();                 //返回该集合的迭代器对象 


/*-------------- ArrayTiny.java------*/ 
package sam.adt; 

public class ArrayTiny<T> implements Tiny<T>{ 

private static final long serialVersionUID=19891107000000001L; 

private final int INIT_SIZE=10;              //默认初始化数组大小 
private Object[] elements;                   //用来存储数据的数组 
private int size;                            //集合的逻辑大小 

//适合ArrayTiny的迭代器 
private class ArrayIterator implements Iterator<T>{ 

private int currentPos;    //记录迭代的位置索引 

public ArrayIterator(){ 
currentPos=0; 


public boolean hasNext() { 
return currentPos<size; 


public T next() { 
T value=(T)elements[currentPos]; 
currentPos++; 
return value; 




public ArrayTiny(){ 
super(); 
elements=new Object[INIT_SIZE]; 
size=0; 


public ArrayTiny(Iterator<T> iterator){ 
this(); 
while(iterator.hasNext()){ 
addArrayLength(); 
this.add(iterator.next()); 
size++; 



//增加内部数组的长度 
private void addArrayLength(){ 
if(size==elements.length){ 
int newSize=size*2; 
Object[] tempArray=new Object[newSize]; 
for(int i=0;i<size;i++){ 
tempArray[i]=elements[i]; 

elements=null; 
System.gc(); 
elements=tempArray; 


//减小内部数组的长度 
private void reduceArrayLength(){ 
if(size<elements.length/4&&size>INIT_SIZE){ 
int newSize=Math.max(size*2,INIT_SIZE); 
Object[] tempArray=new Object[newSize]; 
for(int i=0;i<size;i++){ 
tempArray[i]=elements[i]; 

elements=null; 
System.gc(); 
elements=tempArray; 



public boolean add(int indexIn, T valueIn) { 
if(indexIn>=0&&indexIn<size){ 
addArrayLength(); 
for(int i=size;i>indexIn;i--){ 
elements[i]=elements[i-1]; 

elements[indexIn]=valueIn; 
size++; 

return false; 


public void add(T valueIn) { 
addArrayLength(); 
elements[size]=valueIn; 
size++; 


public void addToHead(T valueIn) { 
add(0,valueIn); 


public void clear() { 
elements=null; 
elements=new Object[INIT_SIZE]; 


public boolean contain(T valueIn) { 
int index=indexOf(valueIn); 
if(index!=-1)return true; 
return false; 


public T get(int indexIn) { 
if(indexIn>=0&&indexIn<size){ 
return (T)elements[indexIn]; 

return null; 


public int indexOf(T valueIn){ 
for(int i=0;i<size;i++){ 
if(elements[i].equals(valueIn))return i; 

return -1; 


public Iterator<T> iterator() { 
return new ArrayIterator(); 


public void remove() { 
elements[size-1]=null; 
size--; 
reduceArrayLength(); 


public boolean remove(int indexIn) { 
if(indexIn>=0&&indexIn<size){ 
for(int i=indexIn;i<size;i++){ 
elements[i]=elements[i+1]; 

size--; 
reduceArrayLength(); 
return true; 

return false; 


public boolean remove(T valueIn) { 
int index=indexOf(valueIn); 
return remove(index); 


public void removeFirst() { 
remove(0); 


public boolean replace(int indexIn, T valueIn) { 
if(indexIn>=0&&indexIn<size){ 
elements[indexIn]=valueIn; 

return false; 


public int size() { 
return size; 



posted @ 2010-07-31 16:04 迟宏泽 阅读(2330) | 评论 (0)编辑 收藏
仅列出标题  

导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜