<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    2010年8月2日

    Java注解使用的幾個(gè)小技巧:

      最近經(jīng)常用到注解,總結(jié)了幾個(gè)注解使用的小技巧,現(xiàn)整理如下:

     一、使用默認(rèn)的注解參數(shù):

    使用default關(guān)鍵字可以指定注解的默認(rèn)參數(shù):

    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 "";

    }

    如果我們?cè)谑褂米⒔?#64;SQL的時(shí)候沒有顯式的去指定sql參數(shù),那么就默認(rèn)采取default關(guān)鍵字所指定的值

    二、用value指定默認(rèn)值

    我們經(jīng)常看到很多注解是這種形式,例如:@SQL("select email from user")

    這個(gè)注解里面的參數(shù)為什么沒有帶名稱呢?其實(shí)它的注解是這樣定義的:

    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 "";

    }

    三、在注解中使用數(shù)組參數(shù)

    如果你用過Struts2的注解來配置Action,那么你會(huì)看到如下形式的注解:

    @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)   

    })   

    怎么來創(chuàng)建這種注解呢?

    其實(shí)可以把@Results的參數(shù)看作是一個(gè)@Result的數(shù)組

    定義如下:

    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();   //沒什么特別的,只是接受了一個(gè)數(shù)組參數(shù)而已

    }

    四、讓注解可以去修飾多個(gè)Target類型

    我么使用@Target注解來指定某個(gè)注解可以修飾的類型,實(shí)際上,同上面一樣,@Target接受的是一個(gè)數(shù)組參數(shù),利用這一特性我們可以讓注解來修飾多個(gè)類型。

    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!這個(gè)注解既可以修飾參數(shù)也可以修飾類的屬性

    posted @ 2010-08-02 14:31 遲宏澤 閱讀(3802) | 評(píng)論 (0)編輯 收藏

    2010年8月1日

    使用Java來開發(fā)JSP標(biāo)記
        最近復(fù)習(xí)JSP中。我們知道JSP是Java WEB的一個(gè)規(guī)范。在這個(gè)規(guī)范之內(nèi)我們可以自己去做很多事情。JSP API提供了接口允許我們自己去自定義JSP標(biāo)簽和EL表達(dá)式。自定義JSP標(biāo)簽的大致思想是這樣的,實(shí)現(xiàn)一個(gè)包含標(biāo)簽參數(shù)和行為邏輯的類,這個(gè)類我們稱它為標(biāo)記處理器類,它實(shí)質(zhì)上是一個(gè)Bean,我們?cè)O(shè)置標(biāo)簽的屬性實(shí)際上是調(diào)用它的setter方法。接下來通過使用標(biāo)記庫描述文件(TLD)來描述標(biāo)簽的相關(guān)信息,以便在JSP頁面中使用taglib指令時(shí)能順利的找到它。下面我們來看看詳細(xì)應(yīng)該怎么去做。
        從JSP2.0開始引入了一個(gè)簡單標(biāo)記處理器接口——javax.servlet.jsp.tagext.SimpleTag接口,這個(gè)接口取代了原先JSP1.0提供的3個(gè)標(biāo)記處理器接口,我們只需要使用SimpleTarget接口就可以實(shí)現(xiàn)所有類型的JSP標(biāo)記。之所以稱之為簡單,是因?yàn)檫@樣設(shè)計(jì)讓開發(fā)者在編寫程序上省了不少力氣,但是它可以實(shí)現(xiàn)很復(fù)雜的功能,并不是說它的功能很簡單。
        該接口包含了如下5個(gè)方法:
        void doTag();  //該方法包含了標(biāo)簽執(zhí)行的業(yè)務(wù)邏輯
        JspTag getParent();  //獲取該標(biāo)簽的父標(biāo)簽
        setJspBody(JspFragment body); //設(shè)置JSP標(biāo)簽體,關(guān)于JspFragment類我們稍后再討論
        setJspContext(JspContext ctx);//設(shè)置JSP上下文
        setParent(JspTag parent);  //設(shè)置父標(biāo)簽
    但是多數(shù)情況下我們不需要去親自實(shí)現(xiàn)這個(gè)接口,因?yàn)樽鳛殚_發(fā)者,我們關(guān)心的是業(yè)務(wù)邏輯,也就是doTag()方法的內(nèi)容,這種情況我們只需要繼承javax.servlet.jsp.tagext.SimpleTagSupport類即可,這個(gè)類為我們實(shí)現(xiàn)了SimpleTag接口,我們只需要去重寫它的doTag()方法。下面我們來實(shí)現(xiàn)一個(gè)最簡單的標(biāo)簽:給標(biāo)簽傳遞一個(gè)參數(shù),讓它在頁面上顯示出來.
    首先我們要做的是寫一個(gè)標(biāo)記處理器類:
    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);
        }
    }

    嗯,這是一個(gè)繼承了SimpleTagSupport類的Bean,一樣擁有屬性、setter方法。唯一特別的是重寫了父類的doTag()方法。在這個(gè)doTag()中,我們通過獲得一個(gè)JSP上下文對(duì)象來向頁面輸出一句話:Hello!XXX。

    下面是該介紹JspContext對(duì)象的時(shí)候了,這是一個(gè)抽象類,它唯一的子類是PageContext。這么說剛才所返回的實(shí)際對(duì)象是PageContext類型的。我們可以對(duì)其進(jìn)行強(qiáng)制轉(zhuǎn)換:PageContext pageCtx = (PageContext)this.getJspContext();

    通過PageContext對(duì)象我們就可以訪問到Request和Response對(duì)象了,如:

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

    通過這種方式我們就可以在標(biāo)簽處理器中操作WEB應(yīng)用各個(gè)作用域的數(shù)據(jù)了。

    我們都知道在使用JSP標(biāo)簽時(shí)要在JSP頁面開頭使用taglib指令進(jìn)行聲明,如:
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    taglib指令通過URL來對(duì)標(biāo)簽進(jìn)行定位。但是它是如何做到的呢?這就要靠TLD了,TLD就是標(biāo)記庫描述文件(Tag Library Descriptor,TLD),我們看JSTL的JAR包中,META-INF文件夾下每個(gè)庫都有一個(gè)對(duì)應(yīng)的部署描述文件。TLD實(shí)際上是一個(gè)XML文件,里面描述了標(biāo)記庫和其中標(biāo)記的信息。一個(gè)完整的TLD文件結(jié)構(gòu)如下所示:


    <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>

    起始標(biāo)簽是taglib,<tlib-version>定義了taglib的版本號(hào),<short-name>定義了標(biāo)簽庫的簡稱,<uri>定義了URI標(biāo)識(shí),taglib就是通過此URI來找到相關(guān)的標(biāo)記庫的。
    接下來的<tag>標(biāo)簽便是定義JSP標(biāo)簽的屬性了:
    以下是必須的屬性
    <name>標(biāo)簽名稱
    <tag-class>標(biāo)記器類
    <body-content>動(dòng)作體類型,JSP2.0支持如下動(dòng)作體:
    empty 空標(biāo)記
    jsp:可以包含標(biāo)簽、EL和Scriptlet
    scriptless:不允許Java腳本內(nèi)容
    tagdependent:對(duì)體不進(jìn)行處理
    <attribute>標(biāo)簽定義了標(biāo)簽參數(shù)。
    其中:
    <name>標(biāo)簽指定了參數(shù)的名稱。
    <required>指定了參數(shù)是否是必要的。true是必要,false是不必要
    <rtexprvalue>指定了是否允許使用表達(dá)式(包括EL表達(dá)式和Java表達(dá)式),true為允許,false為不允許

    <funtion>標(biāo)簽定義了EL表達(dá)式,我們稍后再做介紹。


    如何執(zhí)行動(dòng)作體?
    我們?cè)诰帉慗SP頁面時(shí)經(jīng)常會(huì)用到<c:if>和<c:forEach>之類的標(biāo)簽
    <c:if test="true">
      <p>條件是真的,執(zhí)行!</p>
    </c:if>
    只要滿足條件,就會(huì)去執(zhí)行動(dòng)作體,那么我們?cè)趺丛谧远xJSP標(biāo)記中去執(zhí)行動(dòng)作體呢?
    首先我們先要來研究一個(gè)叫做JspFragment的類。JspFragment代表了JSP標(biāo)簽的動(dòng)作體,我們通過它的invoke方法就可以執(zhí)行動(dòng)作體,下面我們來實(shí)現(xiàn)一個(gè)類似于<c:if>的標(biāo)簽:
    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();
            //如果成立則執(zhí)行相應(yīng)邏輯
            if(test){
                body.invoke(null);
            }
        }
    }
    只要滿足了條件,標(biāo)簽動(dòng)作體就會(huì)執(zhí)行。
    invoke方法接受一個(gè)java.io.Writer對(duì)象,使用這個(gè)對(duì)象將內(nèi)容輸出到頁面,如果不輸出則可以給它傳遞一個(gè)null參數(shù)。

    JSP中引入了EL表達(dá)式給我們的開發(fā)帶來了很大的方便,同自定義標(biāo)簽一樣,允許開發(fā)者去自定義EL表達(dá)式函數(shù),定義EL表達(dá)式函數(shù)要比定義標(biāo)簽簡單多了,我們甚至不需要去實(shí)現(xiàn)任何接口,假設(shè)我們要實(shí)現(xiàn)一個(gè)簡單的EL表達(dá)式函數(shù)來計(jì)算兩個(gè)數(shù)的和,就可以這樣:

    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;
        }
    }

    這個(gè)類中全部是static方法,這是一個(gè)工具類。
    然后我們只需要在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語言中的函數(shù)聲明,通過這個(gè)找到到底調(diào)用類中哪個(gè)工具方法。
    使用的時(shí)候只需要這樣就可以了:${test:add(1,2)}運(yùn)行時(shí)輸出結(jié)果為3


    posted @ 2010-08-01 15:10 遲宏澤 閱讀(3015) | 評(píng)論 (1)編輯 收藏

    2010年7月31日

    戲說Java泛型 
    Sun在Java 5之后的版本中引入了泛型技術(shù)(Generic).泛型是指具有一個(gè)或多個(gè)類型參數(shù)的類或者是接口.泛型技術(shù)其實(shí)在C++的STL中早已廣泛使用,在Java中泛型主要是用來構(gòu)建安全的集合,我們?cè)谑褂肑CF(Java Collections Framework)時(shí)經(jīng)常碰到它們的身影.當(dāng)然泛型還有很多用處,它可以大大提高程序的可復(fù)用性。但是至少有90%的Java程序員都只在構(gòu)建集合時(shí)使用這種技術(shù).在Java中引入泛型技術(shù)之前,我們使用集合時(shí),可以向其中加入任何對(duì)象,這就造成了很多不安全的因素,例如: 

    //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();     //獲取迭代器對(duì)象 
       int sum=0; 
       while(i.hasNext()){ 
         Integer num=(Integer)i.next();  //將集合中得到的對(duì)象強(qiáng)制轉(zhuǎn)換為Integer,Note:這里最容易出現(xiàn)問題! 
         sum+=num.intValue();            /*進(jìn)行拆箱操作,當(dāng)然在Java5之后的版本這個(gè)動(dòng)作可以自動(dòng)完成,我們這里模擬的是Java1.4,研究泛型我們需要復(fù)古~ 
       }                                  *這是一門新的科學(xué),叫做代碼考古學(xué)^_^ */ 
       return sum; 


    這個(gè)程序會(huì)編譯成功!并且不會(huì)出現(xiàn)警告!但是在運(yùn)行時(shí)會(huì)拋出ClassCastException這種運(yùn)行時(shí)異常,String不能通過Integer的instanceof測(cè)試,并且我們從集合中取出元素時(shí),因?yàn)榉祷氐念愋投际荗bject,我們必須對(duì)其進(jìn)行強(qiáng)制轉(zhuǎn)換,嗯~這個(gè)操作是我最討厭的——不但麻煩,多敲了好幾下鍵盤,而且非常不安全,我不敢確定這個(gè)Object是我所希望轉(zhuǎn)換成的類型,如果真要做這種操作,希望大家都先進(jìn)行一下instanceof測(cè)試,安全第一,受罪第二,不過現(xiàn)在確保安全是為了以后受更少的罪!將來在維護(hù)程序時(shí),你甚至可能會(huì)因?yàn)檫@個(gè)小小的原因而一怒之下產(chǎn)生想重寫整個(gè)程序的沖動(dòng)!嗯~實(shí)不相瞞,我就這樣做過~血的教訓(xùn)! 

    但是有了泛型一切都好了起來,我們可以告訴編譯器每個(gè)集合中接受哪些對(duì)象類型,編譯器會(huì)自動(dòng)為你做轉(zhuǎn)換工作,這樣以來我們?cè)诰幾g時(shí)就知道是否向集合中插入了類型錯(cuò)誤的元素. 

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

    現(xiàn)在intList這個(gè)集合就只能夠接受Integer類型的對(duì)象: 
    intList.add(new Integer(12)); 
    如果我們向其中加入一個(gè)非Integer的對(duì)象,那么編譯器將報(bào)錯(cuò)! 


    考慮這種情況: 
    List<E> list=new ArrayList<E>; 
    如果F是E的子類型,list中能添加F類型的對(duì)象嗎?當(dāng)然可以!同數(shù)組一樣,子類都可以加入到父類型的集合中,另外對(duì)于接口也是如此,比如Bird類和Plane類都實(shí)現(xiàn)了一個(gè)Flyable的接口,我們想要在一個(gè)集合中放置所有"具有飛行能力"的對(duì)象,就可以很簡單的這樣做: 
    List<Flyable> flyerList=new ArrayList<Flyable>(); 
    flyerList.add(new Bird()); 
    flyerList.add(new Plane()); 
    這樣很和諧,不是嗎?像List<Object>這種形式的集合當(dāng)然就可以容納天下所有類型的對(duì)象了! 

    您可能會(huì)疑問,List<Object>能夠容納所有的對(duì)象,那么它不就與原生態(tài)(我們將不帶任何泛型信息的類型成為原生態(tài)類型,原生態(tài)是一個(gè)很時(shí)髦的東西,但是在Java中我們要避免它)的List相同了嗎?也就是說: 
    List list=new ArrayList(); 
    List<Object> objList=new ArayList<Object>(); 
    上述這兩行代碼所產(chǎn)生的東西是完全一樣的嗎?其實(shí)它們是有差距的,我們通過一個(gè)小程序來驗(yàn)證: 

            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){               //我們?cè)谶@里用的是原生態(tài)集合參數(shù) 
    Iterator iterator=list.iterator(); 
    while(iterator.hasNext()){ 
    System.out.println(iterator.next()); 




    程序運(yùn)行的非常順利!它可以喊出集合中包含的所有名字,同時(shí)我們也看到,我們可以將List<String>傳遞給一個(gè)接收原生態(tài)List的方法.如果你擅長向編譯器"找茬"的話,可能會(huì)發(fā)現(xiàn)這個(gè)地方似乎有一個(gè)漏洞,然后你會(huì)嘗試寫出類似如下的代碼: 

           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)); 


    我們將一個(gè)Integer對(duì)象插入到一個(gè)String類型的集合中.測(cè)試一下,程序能成功編譯,但是會(huì)產(chǎn)生警告,如果你是在命令行下使用的javac命令進(jìn)行編譯,就可以看到這條警告:使用了未經(jīng)檢查或不安全的操作.如果使用現(xiàn)代IDE,例如Eclipse,也會(huì)看到用使人很不舒服的黃色曲線標(biāo)識(shí)著list.add(new Integer(10))這條語句.可見編譯器非常不希望我們這樣做!但是之所以能通過編譯,主要是為了保持移植兼容性,與過去的Java代碼保持兼容. 


    泛型最初加入到Java中并不受歡迎.Sun在Java中引入泛型技術(shù)最大的挑戰(zhàn)就是做到使具有類型安全的泛型類和原來的原生態(tài)類能夠協(xié)同工作,然而這樣就會(huì)如上述代碼所示,引起許多棘手的代碼安全問題,編譯器就只能就這個(gè)問題發(fā)出警告,提醒程序員最好不要這樣做.這些安全隱患我們總是在程序運(yùn)行時(shí)才能發(fā)現(xiàn),JVM對(duì)于泛型這種東西毫無概念!泛型概念對(duì)于編譯器而言是嚴(yán)格的,編譯器在編譯具有泛型信息的代碼時(shí),會(huì)對(duì)泛型類型進(jìn)行驗(yàn)證,然后執(zhí)行一個(gè)類型擦除過程,也就是從類字節(jié)碼中去掉這些信息!當(dāng)JVM在運(yùn)行時(shí)就看不到所謂的泛型了,這個(gè)就是編譯器的"泛型陰謀",我們有必要了解這個(gè)事實(shí). 


    下面我們將原生態(tài)的List換成List<Object>,情況會(huì)怎么樣呢? 

             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)); 



    嗯~它會(huì)出錯(cuò)!我們一再強(qiáng)調(diào)泛型是安全的,我們不能把一個(gè)List<String>傳遞給一個(gè)接受List<Object>參數(shù)的方法!如果您對(duì)Java數(shù)組比較熟悉,此時(shí)可能會(huì)產(chǎn)生疑問,您可能會(huì)經(jīng)常碰見這樣的代碼: 
            
            Object[] objects=new Integer[]{1,2,3,4,5,6}; 

    我們可以很正常的將一個(gè)子類型的數(shù)組賦給一個(gè)超類型的數(shù)組的引用,但是我們絕對(duì)不可以這樣做: 
            List<Object>objList=new ArrayList<String>(); 
    編譯器是堅(jiān)決不會(huì)讓你通過的!嗯~你感到泛型不夠人性化是不是?它要是能跟數(shù)組一樣就方便了~但是你看一看下面的程序,也許你就不會(huì)這么想了,相反,你或許還會(huì)認(rèn)為Java中的數(shù)組存在缺陷: 

            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!問題出在這兒! 

    嗯~你是不是發(fā)現(xiàn)同上面的某個(gè)程序類似?它同樣可以通過編譯,但是我們?cè)谶\(yùn)行時(shí)發(fā)現(xiàn)它會(huì)產(chǎn)生一個(gè)叫做ArrayStoreException的異常!顧名思義,我們向這個(gè)數(shù)組當(dāng)中放入了它所不能接受的東西.這一切的禍根都是源于Java允許我們可以將一個(gè)子類型的數(shù)組賦給一個(gè)超類型的數(shù)組的引用~然而使用泛型就不會(huì)發(fā)生這種情況,編譯器堅(jiān)決阻止這種情況的存在! 

    數(shù)組同泛型似乎是水火不相容,數(shù)組是具體化的對(duì)象,只有在程序運(yùn)行時(shí)才能搞清楚它們的類型,而對(duì)于泛型,我們前面說過,它僅僅在編譯的時(shí)候才會(huì)存在!基于這個(gè)原因我們不可以創(chuàng)建具有泛型信息的數(shù)組:例如new E[] new List<Object>[] 這些做法都是錯(cuò)誤的!一定要注意! 


    然而有一種神奇的方法能夠?qū)⒎盒蜑樽宇愋偷募蟿?chuàng)遞給泛型為父類型的引用,我們?cè)赪indows下查找文件時(shí)經(jīng)常會(huì)用到通配符,比如"*.jpg"表示所有JPG類型的文件,在Java中也有一套適用于泛型的通配符,下面是一個(gè)運(yùn)用了泛型通配符的程序: 


    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"); 
           } 


    我們?cè)谏鲜龀绦蛑泻唵蔚臉?gòu)建了幾個(gè)游戲的角色類,抽象類Rpg及它的子類Knight(騎士)和Magician(術(shù)士),所有的游戲角色都會(huì)進(jìn)行普通攻擊,然而攻擊的能力各不相同,騎士的攻擊強(qiáng)度比較大,而術(shù)士的普通攻擊相對(duì)要弱一些,他們主要依靠高科技的魔法攻擊~基于這個(gè)原則,我們?cè)赗pg的子類中重寫了這個(gè)方法.現(xiàn)在我們要做的是讓一群騎士和術(shù)士進(jìn)行戰(zhàn)斗.用doAttack()方法來號(hào)召他們戰(zhàn)斗.您或許早就注意了doAttack()的參數(shù)很奇怪:List<? extends Rpg> 

    這個(gè)就是前面我們所說的泛型通配符,<? extends Rpg>表示可以接受泛型類型是Rpg或Rpg子類型的集合,并且,很重要的一點(diǎn),千萬不要向這些集合中添加任何元素,否則會(huì)引起編譯錯(cuò)誤,原因很簡單,如果允許添加元素的話,很有可能將一個(gè)術(shù)士插入到一群騎士的隊(duì)伍當(dāng)中!有一個(gè)例外,你可以向其中添加null元素 

    通配符?后的extends不僅代表著子類也代表的接口的實(shí)現(xiàn),比如<? extends Serializable>表示所有實(shí)現(xiàn)泛型類型實(shí)現(xiàn)Serializable接口的集合.嗯~我沒寫錯(cuò),的確是extends,盡管這是一個(gè)接口,記住,沒有<? implements Serializable>這種形式,這就是語法,我們必須遵守 

    除了extends,另外還有一個(gè)泛型通配符關(guān)鍵字super,關(guān)于它的作用,請(qǐng)閱讀下面的程序: 


    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"); 
           } 


    你一眼就會(huì)注意到,我們終于可以用親愛的add()方法來添加不是null的東西了!這都是super的功勞,List<? super Knight>的意思是凡是泛型類型為Knight和Knight超類的集合都能夠接受,在程序中你可以看到。我們可以將新的騎士加入到騎士的隊(duì)伍當(dāng)中,或者是將騎士加入到混合角色的隊(duì)伍當(dāng)中,不會(huì)發(fā)生將騎士插入到專業(yè)的術(shù)士隊(duì)伍當(dāng)中這樣的錯(cuò)誤,因?yàn)榫幾g器會(huì)阻止術(shù)士類型的集合傳遞到這個(gè)增加騎士的方法中 

    然而當(dāng)我們什么關(guān)鍵字都不使用呢?就像這樣List<?> 你認(rèn)為它等同于List<Object>嗎?那么你錯(cuò)了,我們前面說過,為了安全,List<Object>只能接受泛型類型為Object類型的集合,而List<?>可以接受泛型類型為任何類型的集合!List<?>和List<? extends Object>是等同的,大家可以自己寫程序測(cè)試一下!可以繼續(xù)把我的騎士和術(shù)士的故事講下去~ 

    使用通配符時(shí)要注意,通配符只是針對(duì)引用聲明使用,使用new生成對(duì)象時(shí)不可以使用通配符! 
    List<? extends Rpg> rpgs=new ArrayList<Knight>();  //這是正確的 
    List<? extends Rpg> magicians=new ArrayList<? super Magician>();  //這是錯(cuò)誤的! 

    我們說過,應(yīng)用泛型可以大大提高程序的可復(fù)用性,下面我們將學(xué)習(xí)如何創(chuàng)建我們自己的泛型類,比上面的要簡單,至少?zèng)]有那么多的編譯錯(cuò)誤和異常,呵呵,可以把心情放松一下,下面的例子,我們構(gòu)造一個(gè)使用泛型的鏈表節(jié)點(diǎn): 

    class Node<T>{                               
    private T value;                    //節(jié)點(diǎn)所包含的值 
    private Node<T> nextNode;           //節(jié)點(diǎn)所指向的下一個(gè)節(jié)點(diǎn) 

    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就是泛型的標(biāo)識(shí)符,然后在類中,我們可以像使用正常類一樣使用泛型標(biāo)識(shí)符,在類定義中可以使用多個(gè)泛型標(biāo)識(shí)符: 
    class Map<K,V>{ 
       …… 

    我們也可以使用通配符來指定泛型所允許的范圍: 
    class RpgHolder<T extends Rpg>{       //只允許RPG及其子類 
      …… 


    有時(shí)候我們不需要使用一個(gè)泛型類,我們只需要在普通類中簡單的定義一個(gè)支持泛型的方法: 
    public  <T extends Rpg> void makeRpgList(T t){ 

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



    首先我們要聲明方法的泛型標(biāo)識(shí)符<T extends Rpg>,然后像泛型類那樣在方法中使用泛型 

    Java 5出現(xiàn)之后,學(xué)習(xí)變得越來越困難,泛型技術(shù)是主要的困難因素之一,之所以困難主要是因?yàn)樗c以前的代碼保持兼容,這就大大的增加了復(fù)雜性,您也看到了,前面的那一大堆問題~但是學(xué)習(xí)泛型技術(shù)是很有用的,增加了代碼的可復(fù)用性,以及類型安全.本文主要闡述了一些簡單的理論,大家平時(shí)要多練習(xí),有很多事可以做,比如可以嘗試用泛型去重新實(shí)現(xiàn)一些數(shù)據(jù)結(jié)構(gòu),優(yōu)化一些常用的工具類,你會(huì)發(fā)現(xiàn)這是件非常有趣的事! 

    下面是我自己寫的一個(gè)類似List的集合——Tiny,當(dāng)然比起JCF來在實(shí)際應(yīng)用中性能不是很好,但是包含了基本的集合操作,習(xí)慣了JCF,很多數(shù)據(jù)結(jié)構(gòu)的具體實(shí)現(xiàn)都忘得差不多了~得復(fù)習(xí)了~哈哈~閑著沒事練手~練手~這個(gè)是使用一個(gè)長度可變的數(shù)組來實(shí)現(xiàn)的,大家可以嘗試一下用鏈表來實(shí)現(xiàn)它的另一個(gè)版本LinkedTiny,這樣可以省去變化數(shù)組長度的麻煩~ 
    /*-------------- 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();                          //刪除集合的最后一個(gè)元素 
    void removeFirst();                     //刪除集合中的第一個(gè)元素 
    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();                             //獲取集合中當(dāng)前元素?cái)?shù)目 
    void clear();                           //清除整個(gè)集合中的元素 
    boolean contain(T valueIn);             //測(cè)試集合中是否包含值為valueIn的元素 
    Iterator<T> iterator();                 //返回該集合的迭代器對(duì)象 


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

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

    private static final long serialVersionUID=19891107000000001L; 

    private final int INIT_SIZE=10;              //默認(rèn)初始化數(shù)組大小 
    private Object[] elements;                   //用來存儲(chǔ)數(shù)據(jù)的數(shù)組 
    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++; 



    //增加內(nèi)部數(shù)組的長度 
    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; 


    //減小內(nèi)部數(shù)組的長度 
    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 遲宏澤 閱讀(2344) | 評(píng)論 (0)編輯 收藏
    僅列出標(biāo)題  

    導(dǎo)航

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    統(tǒng)計(jì)

    常用鏈接

    留言簿

    隨筆檔案

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 亚洲一区免费视频| 亚洲综合精品香蕉久久网97| 亚洲欧美日韩中文字幕一区二区三区 | 亚洲一区二区三区电影| 91视频免费观看| 亚洲人JIZZ日本人| 丝瓜app免费下载网址进入ios | 亚洲国产精品综合久久一线| 精品国产亚洲一区二区三区在线观看| 欧美最猛性xxxxx免费| 亚洲中文无码亚洲人成影院| 妞干网在线免费观看| 91麻豆国产免费观看| 亚洲网址在线观看你懂的| 91香蕉在线观看免费高清| 亚洲精品无码久久久久久久| 久久久高清免费视频| 学生妹亚洲一区二区| 日本媚薬痉挛在线观看免费| 黄色a三级三级三级免费看| 久久久亚洲精品蜜桃臀| 七色永久性tv网站免费看| 亚洲精品综合久久中文字幕| 免费看大黄高清网站视频在线| 美女露隐私全部免费直播| 亚洲色婷婷综合久久| 亚州免费一级毛片| 亚洲丰满熟女一区二区哦| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 日韩欧毛片免费视频| 精品国产亚洲AV麻豆| 日韩va亚洲va欧洲va国产| 中文字幕无码免费久久99| 美女视频黄.免费网址| 国产成A人亚洲精V品无码| 我的小后妈韩剧在线看免费高清版 | 久久久亚洲欧洲日产国码aⅴ | 亚洲人妖女同在线播放| 亚洲国产精品激情在线观看| 午夜精品一区二区三区免费视频| 亚洲国产系列一区二区三区 |