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

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

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

    初識Java內部類
        提起Java內部類(Inner Class)可能很多人不太熟悉,實際上類似的概念在C++里也有,那就是嵌套類(Nested Class),關于這兩者的區別與聯系,在下文中會有對比。內部類從表面上看,就是在類中又定義了一個類(下文會看到,內部類可以在很多地方定義),而實際上并沒有那么簡單,乍看上去內部類似乎有些多余,它的用處對于初學者來說可能并不是那么顯著,但是隨著對它的深入了解,你會發現Java的設計者在內部類身上的確是用心良苦。學會使用內部類,是掌握Java高級編程的一部分,它可以讓你更優雅地設計你的程序結構。下面從以下幾個方面來介紹:
        ·第一次見面
    public interface Contents {
        int value();
    }
    public interface Destination {
        String readLabel();
    }
    public class Goods {
        private class Content implements Contents {
            private int i = 11;
            public int value() {
                return i;
            }
        }
        protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel() {
                return label;
            }
        }
        public Destination dest(String s) {
            return new GDestination(s);
        }
        public Contents cont() {
            return new Content();
        }
    }
    class TestGoods {
        public static void main(String[] args) {
            Goods p = new Goods();
            Contents c = p.cont();
            Destination d = p.dest("Beijing");
        }
    }
        在這個例子里類Content和GDestination被定義在了類Goods內部,并且分別有著protected和private修飾符來控制訪問級別。Content代表著Goods的內容,而GDestination代表著Goods的目的地。它們分別實現了兩個接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d進行操作,你甚至連這兩個內部類的名字都沒有看見!這樣,內部類的第一個好處就體現出來了——隱藏你不想讓別人知道的操作,也即封裝性。
        同時,我們也發現了在外部類作用范圍之外得到內部類對象的第一個方法,那就是利用其外部類的方法創建并返回。上例中的cont()和dest()方法就是這么做的。那么還有沒有別的方法呢?當然有,其語法格式如下:
        outerObject=new outerClass(Constructor Parameters);
        outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
        注意在創建非靜態內部類對象時,一定要先創建起相應的外部類對象。至于原因,也就引出了我們下一個話題——
        ·非靜態內部類對象有著指向其外部類對象的引用
        對剛才的例子稍作修改:
    public class Goods {
        private valueRate=2;
        private class Content implements Contents {
            private int i = 11*valueRate;
            public int value() {
                return i;
            }
        }
        protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel() {
                return label;
            }
        }
        public Destination dest(String s) {
            return new GDestination(s);
        }
        public Contents cont() {
            return new Content();
        }
    }
        修改的部分用藍色顯示了。在這里我們給Goods類增加了一個private成員變量valueRate,意義是貨物的價值系數,在內部類Content的方法value()計算價值時把它乘上。我們發現,value()可以訪問valueRate,這也是內部類的第二個好處——一個內部類對象可以訪問創建它的外部類對象的內容,甚至包括私有變量!這是一個非常有用的特性,為我們在設計時提供了更多的思路和捷徑。要想實現這個功能,內部類對象就必須有指向外部類對象的引用。Java編譯器在創建內部類對象時,隱式的把其外部類對象的引用也傳了進去并一直保存著。這樣就使得內部類對象始終可以訪問其外部類對象,同時這也是為什么在外部類作用范圍之外向要創建內部類對象必須先創建其外部類對象的原因。
        有人會問,如果內部類里的一個成員變量與外部類的一個成員變量同名,也即外部類的同名成員變量被屏蔽了,怎么辦?沒事,Java里用如下格式表達外部類的引用:
    outerClass.this
        有了它,我們就不怕這種屏蔽的情況了。
        ·靜態內部類
        和普通的類一樣,內部類也可以有靜態的。不過和非靜態內部類相比,區別就在于靜態內部類沒有了指向外部的引用。這實際上和C++中的嵌套類很相像了,Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用這一點上,當然從設計的角度以及以它一些細節來講還有區別。
        除此之外,在任何非靜態內部類中,都不能有靜態數據,靜態方法或者又一個靜態內部類(內部類的嵌套可以不止一層)。不過靜態內部類中卻可以擁有這一切。這也算是兩者的第二個區別吧。
        ·局部內部類
        是的,Java內部類也可以是局部的,它可以定義在一個方法甚至一個代碼塊之內。
    public class Goods1 {
         public Destination dest(String s) {
              class GDestination implements Destination {
                   private String label;
                   private GDestination(String whereTo) {
                        label = whereTo;
                   }
                   public String readLabel() { return label; }
              }
              return new GDestination(s);
         }
         public static void main(String[] args) {
              Goods1 g= new Goods1();
              Destination d = g.dest("Beijing");
         }
    }
        上面就是這樣一個例子。在方法dest中我們定義了一個內部類,最后由這個方法返回這個內部類的對象。如果我們在用一個內部類的時候僅需要創建它的一個對象并創給外部,就可以這樣做。當然,定義在方法中的內部類可以使設計多樣化,用途絕不僅僅在這一點。
        下面有一個更怪的例子:
    public class Goods2{
         private void internalTracking(boolean b) {
              if(b) {
                   class TrackingSlip {
                        private String id;
                        TrackingSlip(String s) {
                             id = s;
                        }
                        String getSlip() { return id; }
                   }
                   TrackingSlip ts = new TrackingSlip("slip");
                   String s = ts.getSlip();
              }
         }
         public void track() { internalTracking(true); }
         public static void main(String[] args) {
              Goods2 g= new Goods2();
              g.track();
         }
    }
        你不能在if之外創建這個內部類的對象,因為這已經超出了它的作用域。不過在編譯的時候,內部類TrackingSlip和其他類一樣同時被編譯,只不過它由它自己的作用域,超出了這個范圍就無效,除此之外它和其他內部類并沒有區別。
        ·匿名內部類
        java的匿名內部類的語法規則看上去有些古怪,不過如同匿名數組一樣,當你只需要創建一個類的對象而且用不上它的名字時,使用內部類可以使代碼看上去簡潔清楚。它的語法規則是這樣的:
        new interfacename(){......}; 或 new superclassname(){......};
        下面接著前面繼續舉例子:
    public class Goods3 {
         public Contents cont(){
              return new Contents(){
                   private int i = 11;
                   public int value() {
                        return i;
                   }
              };
         }
    }
        這里方法cont()使用匿名內部類直接返回了一個實現了接口Contents的類的對象,看上去的確十分簡潔。
        在java的事件處理的匿名適配器中,匿名內部類被大量的使用。例如在想關閉窗口時加上這樣一句代碼:
    frame.addWindowListener(new WindowAdapter(){
         public void windowClosing(WindowEvent e){
              System.exit(0);
         }
    });
        有一點需要注意的是,匿名內部類由于沒有名字,所以它沒有構造函數(但是如果這個匿名內部類繼承了一個只含有帶參數構造函數的父類,創建它的時候必須帶上這些參數,并在實現的過程中使用super關鍵字調用相應的內容)。如果你想要初始化它的成員變量,有下面幾種方法:
        1.如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明為final。
        2.將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有構造函數了。
        3.在這個匿名內部類中使用初始化代碼塊。
        ·為什么需要內部類?
        java內部類有什么好處?為什么需要內部類?
        首先舉一個簡單的例子,如果你想實現一個接口,但是這個接口中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎么辦?這時候,你可以建一個內部類實現這個接口。由于內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直接實現這個接口的功能。
        不過你可能要質疑,更改一下方法的不就行了嗎?
        的確,以此作為設計內部類的理由,實在沒有說服力。
        真正的原因是這樣的,java中的內部類和接口加在一起,可以的解決常被C++程序員抱怨java中存在的一個問題——沒有多繼承。實際上,C++的多繼承設計起來很復雜,而java通過內部類加上接口,可以很好的實現多繼承的效果。

    posted @ 2006-03-02 20:54 killvin| 編輯 收藏

    引言
    在使用response的過程中經常會遇到跳轉頁面的事情,這個時候有兩種情況供你選擇
    1。就是調用
    ServletContext.getRequestDispatcher(java.lang.String).forward(request ,
    response) ;
    2。就是調用response.setRedirect(),可是這兩個方法有什么不同呢?


    看看TSS上關于這個問題的解釋:
    Difference request.forward() and response.sendRedirect .
    Posted by: Kapil Israni on August 24, 2000 in response to Message #2253 1
    replies in this thread
    i suppose u r talking bout requestDispatcher.forward() here.

    well basically both method calls redirect u to new resource/page/servlet.

    the difference between the two is that sendRedirect always sends a header
    back to the client/browser. this header then contains the
    resource(page/servlet) which u wanted to be redirected. the browser uses
    this header to make another fresh request. thus sendRedirect has a overhead
    as to the extra remort trip being incurred. its like any other Http request
    being generated by ur browser. the advantage is that u can point to any
    resource(whether on the same domain or some other domain). for eg if
    sendRedirect was called at www.mydomain.com then it can also be used to
    redirect a call to a resource on www.theserverside.com.

    where as in case of forward() call, the above is not true. resources from
    the server, where the fwd. call was made, can only be requested for. but
    the major diff between the two is that forward just routes the request to
    the new resources which u specify in ur forward call. that means this route
    is made by the servlet engine at the server level only. no headers r sent
    to the browser which makes this very eficient. also the request and
    response objects remain the same both from where the forward call was made
    and the resource which was called.

    i hope i have hit ur question right.

    posted @ 2006-03-02 20:54 killvin| 編輯 收藏

    Java Language Keywords

    Here's a list of keywords in the Java language. These words are reserved — you cannot use any of these words as names in your programs. true, false, and null are not keywords but they are reserved words, so you cannot use them as names in your programs either.

    abstract    |   continue    |   for    |    new     |   switch
    assert***  |   default     |   goto*     |   package     |   synchronized
    boolean    |   do     |   if     |   private     |   this
    break         |   double     |   implements    |   protected    |   throw
    byte            |   else     |   import     |   public  throws
    case          |   enum****    |   instanceof    |   return     |   transient 
    catch         |   extends     |   int     |   short     |   try
    char           |   final     |   interface    |   static     |   void
    class         |   finally     |   long     |   strictfp**    |   volatile
    const*       |   float     |   native     |   super     |   while


    *   not used
    **   added in 1.2
    ***   added in 1.4 
    ****   added in 5.0                                    

     

    Key: strictfp**

    使用對象:類、方法

    自Java2以來,Java語言增加了一個關鍵字strictfp,雖然這個關鍵字在大多數場合比較少用,但是還是有必要了解一下。

    strictfp的意思是FP-strict,也就是說精確浮點的意思。在Java虛擬機進行浮點運算時,如果沒有指定strictfp關鍵字時,Java的編譯器以及運行環境在對浮點運算的表達式是采取一種近似于我行我素的行為來完成這些操作,以致于得到的結果往往無法令你滿意。而一旦使用了strictfp來聲明一個類、接口或者方法時,那么所聲明的范圍內Java的編譯器以及運行環境會完全依照浮點規范IEEE-754來執行。因此如果你想讓你的浮點運算更加精確,而且不會因為不同的硬件平臺所執行的結果不一致的話,那就請用關鍵字strictfp。

    你可以將一個類、接口以及方法聲明為strictfp,但是不允許對接口中的方法以及構造函數聲明strictfp關鍵字,例如下面的代碼:

    1. 合法的使用關鍵字strictfp

    strictfp interface A {}

    public strictfp class FpDemo1 {
        strictfp void f() {}
    }

    2. 錯誤的使用方法

    interface A {
        strictfp void f();
    }

    public class FpDemo2 {
        strictfp FpDemo2() {}
    }

    一旦使用了關鍵字strictfp來聲明某個類、接口或者方法時,那么在這個關鍵字所聲明的范圍內所有浮點運算都是精確的,符合IEEE-754規范的。例如一個類被聲明為strictfp,那么該類中所有的方法都是strictfp的。

     

    Keys: volatile

    使用對象:字段

    介紹:因為異步線程可以訪問字段,所以有些優化操作是一定不能作用在字段上的。volatile有時

    可以代替synchronized。

     

    Keys:transient

      使用對象:字段

      介紹:字段不是對象持久狀態的一部分,不應該把字段和對象一起串起。

    posted @ 2006-03-02 20:53 killvin| 編輯 收藏

    引言

     記得當初參與某公司的ERP項目中,接觸過異常框架這個概念,可是似乎并沒有感覺到當時技術經理提出這個概念的意義,而且他也對這個概念似乎很"保守",雖然按照他的思路去執行,但沒有理解的概念再實施起來的時候總是覺得很"別扭",而如今面對自己要設計咚咚了,不得不重新審視異常這個概念,JAVA異常的介紹文章在網絡上非常的少,而對于如何構件J2EE的異常處理框架更顯的稀少,于是就促使自己寫下了這樣的文章。

     本文只是自己的一些愚見,希望和大家相互學習。Email:Killvin@hotmail.com

    概念

    什么是異常?

    異常(exception)應該是異常事件(exceptional event)的縮寫。
    異常定義:異常是一個在程序執行期間發生的事件,它中斷正在執行的程序的正常的指令流。
    當在一個方法中發生錯誤的時候,這個方法創建一個對象,并且把它傳遞給運行時系統。這個對象被叫做異常對象,它包含了有關錯誤的信息,這些信息包括錯誤的類型和在程序發生錯誤時的狀態。創建一個錯誤對象并把它傳遞給運行時系統被叫做拋出異常。
    一個方法拋出異常后,運行時系統就會試著查找一些方法來處理它。這些處理異常的可能的方法的集合是被整理在一起的方法列表,這些方法能夠被發生錯誤的方法調用。這個方法列表被叫做堆棧調用(call stack)

    運行時系統搜尋包含能夠處理異常的代碼塊的方法所請求的堆棧。這個代碼塊叫做異常處理器,搜尋首先從發生的方法開始,然后依次按著調用方法的倒序檢索調用堆棧。當找到一個相應的處理器時,運行時系統就把異常傳遞給這個處理器。一個異常處理器要適當地考濾拋出的異常對象的類型和異常處理器所處理的異常的類型是否匹配。異常被捕獲以后,異常處理器關閉。如果運行時系統搜尋了這個方法的所有的調用堆棧,而沒有找到相應的異常處理器。

     

    怎么設計異常框架

    任何的異常都是Throwable類(為何不是接口??),并且在它之下包含兩個字類Error / Exception,而Error僅在當在Java虛擬機中發生動態連接失敗或其它的定位失敗的時候,Java虛擬機拋出一個Error對象。典型的簡易程序不捕獲或拋出Errors對象,你可能永遠不會遇到需要實例化Error的應用,那就讓我們關心一下Exception

    Exception中比較重要的就是RuntimeException-運行時異常(當然這個名字是存在爭議的,因為任何的異常都只會發生在運行時),為什么說這個類時很重要的呢?因為它直接關系到你的異常框架的設計,仔細看RuntimeException

    A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught.

    -可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在 throws 子句中進行聲明。

    也就是說你的應用應該不去“關心”(說不關心是不服責任的,但只是你不應該試圖實例化它的字類)RuntimeException,就如同你不應該關心Error的產生與處理一樣!RuntimeException描述的是程序的錯誤引起來的,因該由程序負擔這個責任!(<B>從責任這個角度看Error屬于JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任</B>)

    那就有人會問,那我該關心什么!答案就是除了Error與RuntimeException,其他剩下的異常都是你需要關心的,而這些異常類統稱為Checked Exception,至于Error與RuntimeException則被統稱為Unchecked Exception.


    異常的概念就這些了,即使你在網絡上搜索也就不過如此,是不是感覺到有點清晰又有點模糊?那么怎么該如何在這樣單薄而模糊的概念下設計J2EE的異常框架呢?


    解決方案:J2EE異常框架

    我們拿一個模擬的例子來說明異常框架的設計過程,比如我們要對外提供doBusiness()這個業務方法

    public void doBusiness() throws xxxBusinessException

    當客戶端調用這樣的方法的時候應該這樣處理異常(包括處理RuntimeException , checked exception)
    <B>記住,無論如何我們都不希望或者確切的說是不應該將RuntimeException這樣的異常暴露給客戶的,因為他們沒有解決這個問題的責任!</B>
    我們暫時將Struts中的某個Action看作時客戶端,其中doExecute(....)要調用doBusiness()這個方法

    public void doAction(......)
    {
     try
     {

      xxx.doBusiness();
     }
     catch(Exception e)
     {
       if(e instanceof RuntimeException) 
       {
        // catch runtime exception
        // 你可以在這里將捕獲到的RuntimeException
        // 將異常通知給某個負責此程序的程序員,讓他知道他
        // 自己犯了多么低級的錯誤!


       }else
       {
        //checked exception such as xxxBusinessException
        //將這樣的異常暴露給客戶顯示    

       }

     }
    }

    我們可以這樣設計xxxBusinessException

    public class xxxBusinessException extends ApplicationException
    {
        public xxxBusinessException(String s){
            super(s);

    };

    import java.io.PrintStream;
    import java.io.PrintWriter;
    public class ApplicationException extends Exception {
           /** A wrapped Throwable */
           protected Throwable cause;
           public ApplicationException() {
               super("Error occurred in application.");
           }
           public ApplicationException(String message)  {
               super(message);
           }
           public ApplicationException(String message, Throwable cause)  {
               super(message);
               this.cause = cause;
           }
           // Created to match the JDK 1.4 Throwable method.
           public Throwable initCause(Throwable cause)  {
               this.cause = cause;
               return cause;
           }
           public String getMessage() {
               // Get this exception's message.
               String msg = super.getMessage();
               Throwable parent = this;
               Throwable child;
               // Look for nested exceptions.
               while((child = getNestedException(parent)) != null) {
                   // Get the child's message.
                   String msg2 = child.getMessage();
                   // If we found a message for the child exception,
                   // we append it.
                   if (msg2 != null) {
                       if (msg != null) {
                           msg += ": " + msg2;
                       } else {
                           msg = msg2;
                       }
                   }
                   // Any nested ApplicationException will append its own
                   // children, so we need to break out of here.
                   if (child instanceof ApplicationException) {
                       break;
                   }
                   parent = child;
               }
               // Return the completed message.
               return msg;
           }
           public void printStackTrace() {
               // Print the stack trace for this exception.
               super.printStackTrace();
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       System.err.print("Caused by: ");
                       child.printStackTrace();
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public void printStackTrace(PrintStream s) {
               // Print the stack trace for this exception.
               super.printStackTrace(s);
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       s.print("Caused by: ");
                       child.printStackTrace(s);
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public void printStackTrace(PrintWriter w) {
               // Print the stack trace for this exception.
               super.printStackTrace(w);
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       w.print("Caused by: ");
                       child.printStackTrace(w);
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public Throwable getCause()  {
               return cause;
           }
    }

    而"聰明"的讀者肯定要問我那doBusiness()這個業務方法該如何包裝異常呢?

     public void doBusiness() throw xxxBusinessException
     {
       try
       {
         execute1(); // if it throw exception1

         exexute2(); // if it throw exception 2

         .... .....

       }
       catch (exception1 e1)
       {
        throw new xxxBusinessException(e1);
       }
       catch(exception2 e2)
       {
        throw new xxxBusinessException(e2);
       }
       ........
     }

     也可以這樣

     public void doBusiness() throw xxxBusinessException
     {
       try
       {
         execute1(); // if it throw exception1

         exexute2(); // if it throw exception 2

         .... .....

       }
       catch (Exception e)
       {
        // 注意很多應用在這里根本不判斷異常的類型而一股腦的采用
        // throw new xxxBusinessException(e);
        // 而這樣帶來的問題就是xxxBusinessException"吞掉了"RuntimeException
        // 從而將checked excption 與unchecked exception混在了一起!

        // 其實xxxBusinessException屬于checked excpetion ,它根本不應該也不能夠理睬RuntimeException
        if(! e instanceof RuntimeException) throw new xxxBusinessException(e);
       }
     }


    總結
     1。JAVA的異常分為兩類: checked exception & unchecked excpetion
     2。應用開發中產生的異常都應該集成自Exception 但都屬于checked excpetion類型
     3。應用中的每一層在包裝并傳遞異常的時候要過濾掉RuntimeException!
     4。從責任這個角度看Error屬于JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任
     5。無論如何我們都不希望或者確切的說是不應該將RuntimeException這樣的異常暴露給客戶的,因為他們沒有解決這個問題的責任!

    posted @ 2006-03-02 20:50 killvin| 編輯 收藏

    Nasted Class 的介紹,請詳見參考

    今天討論的不是不是內部類的概念,而是具體使用的一個場景-如何在內部類中返回外部對象

    看一段代碼

    import java.util.LinkedList;
    import java.util.List;


    public class OuterClass
    {

     private List listeners = new LinkedList();

     public void addListeners(IListener listener)
     {
      this.listeners.add(listener);
     }


     private OuterClass outer = this;  (1)
     private class InnterClass
     {
      public void publish()
      {
       //將事件發布出去 (2)
       for(int i=0;i < listeners.size();i++)
       {
        IListener listener = (IListener) listeners.get(i);
        listener.receiveEvent(outer);
       }

      }
     }


     public void execute()
     {
      InnterClass in = new InnterClass(); (3)
      in.publish();

     }
    }

    public interface IListener
    {

    public void receiveEvent(OuterClass obj);
    }

    你可能覺得這個例子很別扭,在哪里讓你覺得難受呢?其實問題的關鍵就在于接口IListener的定義,這里需要給receiveEvent方法傳遞的參數是外部對象!(別激動,下面我會說明需要傳遞的一個場景)

    場景

    在一個GUI系統中,我們要在畫板WorkSpace(WorkSpace實現了IListener接口)上產生一顆樹,但樹中的每個節點的產生(繪圖)是我們不知道的算法,系統只為我們提供了一些繪圖的接口,并返回元素的句柄!看來我們需要"包裝"一下這個繪圖的句柄Brush(其實我把它叫做筆刷,因為它只知道如何"刷"出圖像來,就這點本事!)并對外提供節點Node這樣一個通用的類。

    此時Node與Brush的關系就很微妙了,不過我們可以拋開這些外表,看到Node與Brush其實就是外部類與內部類的關系!-第一步完成了:確定了兩者的關系

    然而,事情沒有這么簡單,Node類必須處理一些事件,而這些事件理所當然只有Brush能夠看懂,而Node根本不知道這樣的事件處理過程,現在有兩個辦法:辦法一,讓Node實現Brush所有的事件;辦法二,把Brush返回回去,讓它來處理自己的事件,看來辦法二是個好主意,因為我可以不關心事件的種類!-第二步完成了:確定了事件處理的責任

    還沒完呢,你肯定不希望畫板WorkSpace面對的是繪圖的句柄Brush這樣的對象,相反你只希望WokSpace只知道Node的存在!IListener接口中receiveEvent方法的參數定義為OuterClass 就由此而來!-第三步完成:接口的定義

    public interface IListener
    {

    public void receiveEvent(OuterClass obj);
    }

     既然說清楚了這個問題(應該比較清楚了吧?)那改如何實現這樣一個蹩腳而有無可奈何的設計呢?讓我們回憶一下內部類,內部類擁有訪問外部類的方法與屬性的權限

     private OuterClass outer = this;  - 這個對外部類的引用就是為內部類的訪問準備的

     private class InnterClass
     {
      public void publish()
      {
       //將事件發布出去 

       for(int i=0;i < listeners.size();i++)
       {
        IListener listener = (IListener) listeners.get(i);
        listener.receiveEvent(outer);  - 這里不可以返回this,因為this代表的是內部類自己
       }
      }

     

     

    參考

    Java Nested class   http://blog.csdn.net/Killvin/archive/2006/01/10/574983.aspx

    初識Java內部類    http://blog.csdn.net/killvin/archive/2006/01/10/574991.aspx

    posted @ 2006-03-02 20:49 killvin| 編輯 收藏

    當你顯式的使用session.save()或者session.update()操作一個對象的時候,實際上是用不到unsaved-value的。某些情況下(父子表關聯保存),當你在程序中并沒有顯式的使用save或者update一個持久對象,那么Hibernate需要判斷被操作的對象究竟是一個已經持久化過的持久對象,是一個尚未被持久化過的內存臨時對象。例如:

    java代碼: 

      Session session = ...;
      Transaction tx = ...;
      
      Parent parent = (Parent) session.load(Parent.class, id);
      
      Child child = new Child();
      child.setParent(parent);
      child.setName("sun");
      
     10 parent.addChild(child);
     11 s.update(parent);
     12 
     13 s.flush();
     14 tx.commit();
     15 s.close();



    在上例中,程序并沒有顯式的session.save(child); 那么Hibernate需要知道child究竟是一個臨時對象,還是已經在數據庫中有的持久對象。如果child是一個新創建的臨時對象(本例中就是這種情況),那么Hibernate應該自動產生session.save(child)這樣的操作,如果child是已經在數據庫中有的持久對象,那么Hibernate應該自動產生session.update(child)這樣的操作。

    因此我們需要暗示一下Hibernate,究竟child對象應該對它自動save還是update。在上例中,顯然我們應該暗示Hibernate對child自動save,而不是自動update。那么Hibernate如何判斷究竟對child是save還是update呢?它會取一下child的主鍵屬性 child.getId() ,這里假設id是 java.lang.Integer類型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等,那么Hibernate認為child是新的內存臨時對象,發送save,如果不相等,那么Hibernate認為child是已經持久過的對象,發送update。

    unsaved-value="null" (默認情況,適用于大多數對象類型主鍵 Integer/Long/String/...)

    當Hibernate取一下child的Id,取出來的是null(在上例中肯定取出來的是null),和unsaved-value設定值相等,發送save(child)

    當Hibernate取一下child的id,取出來的不是null,那么和unsaved-value設定值不相等,發送update(child)

    例如下面的情況:

    java代碼: 

      Session session = ...;
      Transaction tx = ...;
      
      Parent parent = (Parent) session.load(Parent.class, id);
      Child child = (Child) session.load(Child.class, childId);
      
      child.setParent(parent);
      child.setName("sun");
      
     10 parent.addChild(child);
     11 s.update(parent);
     12 
     13 s.flush();
     14 tx.commit();
     15 s.close();



    child已經在數據庫中有了,是一個持久化的對象,不是新創建的,因此我們希望Hibernate發送update(child),在該例中,Hibernate取一下child.getId(),和unsave-value指定的null比對一下,發現不相等,那么發送update(child)。

    BTW: parent對象不需要操心,因為程序顯式的對parent有load操作和update的操作,不需要Hibernate自己來判斷究竟是save還是update了。我們要注意的只是child對象的操作。另外unsaved-value是定義在Child類的主鍵屬性中的。

    java代碼: 

     1 <class name="Child" table="child">
     2 <id column="id" name="id" type="integer" unsaved-value="null">
     3   <generator class="identity"/>
     4 </id>
     5 ...
     6 </class>



    如果主鍵屬性不是對象型,而是基本類型,如int/long/double/...,那么你需要指定一個數值型的unsaved-value,例如:

    java代碼: 

     1 unsaved-null="0"



    在此提醒大家,很多人以為對主鍵屬性定義為int/long,比定義為Integer/Long運行效率來得高,認為基本類型不需要進行對象的封裝和解構操作,因此喜歡把主鍵定義為int/long的。但實際上,Hibernate內部總是把主鍵轉換為對象型進行操作的,就算你定義為int/long型的,Hibernate內部也要進行一次對象構造操作,返回給你的時候,還要進行解構操作,效率可能反而低也說不定。因此大家一定要扭轉一個觀點,在Hibernate中,主鍵屬性定義為基本類型,并不能夠比定義為對象型效率來的高,而且也多了很多麻煩,因此建議大家使用對象型的Integer/Long定義主鍵。

    unsaved-value="none"和
    unsaved-value="any"

    主主要用在主鍵屬性不是通過Hibernate生成,而是程序自己setId()的時候。

    在這里多說一句,強烈建議使用Hibernate的id generator,或者你可以自己擴展Hibernate的id generator,特別注意不要使用有實際含義的字段當做主鍵來用!例如用戶類User,很多人喜歡用用戶登陸名稱做為主鍵,這是一個很不好的習慣,當用戶類和其他實體類有關聯關系的時候,萬一你需要修改用戶登陸名稱,一改就需要改好幾張表中的數據。偶合性太高,而如果你使用無業務意義的id generator,那么修改用戶名稱,就只修改user表就行了。

    由這個問題引申出來,如果你嚴格按照這個原則來設計數據庫,那么你基本上是用不到手工來setId()的,你用Hibernate的id generator就OK了。因此你也不需要了解當

    unsaved-value="none"和
    unsaved-value="any"

    究竟有什么含義了。如果你非要用assigned不可,那么繼續解釋一下:

    unsaved-value="none" 的時候,由于不論主鍵屬性為任何值,都不可能為none,因此Hibernate總是對child對象發送update(child)

    unsaved-value="any" 的時候,由于不論主鍵屬性為任何值,都肯定為any,因此Hibernate總是對child對象發送save(child)

    大多數情況下,你可以避免使用assigned,只有當你使用復合主鍵的時候不得不手工setId(),這時候需要你自己考慮究竟怎么設置unsaved-value了,根據你自己的需要來定。

    BTW: Gavin King強烈不建議使用composite-id,強烈建議使用UserType。

    因此,如果你在系統設計的時候,遵循如下原則:

    1、使用Hibernate的id generator來生成無業務意義的主鍵,不使用有業務含義的字段做主鍵,不使用assigned。

    2、使用對象類型(String/Integer/Long/...)來做主鍵,而不使用基礎類型(int/long/...)做主鍵

    3、不使用composite-id來處理復合主鍵的情況,而使用UserType來處理該種情況。


    那么你永遠用的是unsaved-value="null" ,不可能用到any/none/..了。

    posted @ 2006-03-02 20:45 killvin| 編輯 收藏

    先來點概念:

    在Hibernate中,最核心的概念就是對PO的狀態管理。一個PO有三種狀態:

    1、未被持久化的VO
    此時就是一個內存對象VO,由JVM管理生命周期

    2、已被持久化的PO,并且在Session生命周期內
    此時映射數據庫數據,由數據庫管理生命周期

    3、曾被持久化過,但現在和Session已經detached了,以VO的身份在運行
    這種和Session已經detached的PO還能夠進入另一個Session,繼續進行PO狀態管理,此時它就成為PO的第二種狀態了。這種PO實際上是跨了Session進行了狀態維護的。

    在傳統的JDO1.x中,PO只有前面兩種狀態,一個PO一旦脫離PM,就喪失了狀態了,不再和數據庫數據關聯,成為一個純粹的內存VO,它即使進入一個新的PM,也不能恢復它的狀態了。

    Hibernate強的地方就在于,一個PO脫離Session之后,還能保持狀態,再進入一個新的Session之后,就恢復狀態管理的能力,但此時狀態管理需要使用session.update或者session.saveOrUpdate,這就是Hibernate Reference中提到的“requires a slightly different programming model ”

    現在正式進入本話題:

    簡單的來說,update和saveOrUpdate是用來對跨Session的PO進行狀態管理的。

    假設你的PO不需要跨Session的話,那么就不需要用到,例如你打開一個Session,對PO進行操作,然后關閉,之后這個PO你也不會再用到了,那么就不需要用update。

    因此,我們來看看上例:
    java代碼: 

     1 Foo foo=sess.load(Foo.class,id);
     2 foo.setXXX(xxx);
     3 sess.flush();
     4 sess.commit();



    PO對象foo的操作都在一個Session生命周期內完成,因此不需要顯式的進行sess.update(foo)這樣的操作。Hibernate會自動監測到foo對象已經被修改過,因此就向數據庫發送一個update的sql。當然如果你非要加上sess.update(foo)也不會錯,只不過這樣做沒有任何必要。

    而跨Session的意思就是說這個PO對象在Session關閉之后,你還把它當做一個VO來用,后來你在Session外面又修改了它的屬性,然后你又想打開一個Session,把VO的屬性修改保存到數據庫里面,那么你就需要用update了。

    java代碼: 

      // in the first session
      Cat cat = (Cat) firstSession.load(Cat.class, catId);
      Cat potentialMate = new Cat();
      firstSession.save(potentialMate);
      
      // in a higher tier of the application
      cat.setMate(potentialMate);
      
      // later, in a new session
     10 secondSession.update(cat)// update cat
     11 secondSession.update(mate); // update mate
     12 



    cat和mate對象是在第一個session中取得的,在第一個session關閉之后,他們就成了PO的第三種狀態,和Session已經detached的PO,此時他們的狀態信息仍然被保留下來了。當他們進入第二個session之后,立刻就可以進行狀態的更新。但是由于對cat的修改操作:cat.setMate(potentialMate); 是在Session外面進行的,Hibernate不可能知道cat對象已經被改過了,第二個Session并不知道這種修改,因此一定要顯式的調用secondSession.update(cat); 通知Hibernate,cat對象已經修改了,你必須發送update的sql了。

    所以update的作用就在于此,它只會被用于當一個PO對象跨Session進行狀態同步的時候才需要寫。而一個PO對象當它不需要跨Session進行狀態管理的時候,是不需要寫update的。

    再談談saveOrUpdate的用場:

    saveOrUpdate和update的區別就在于在跨Session的PO狀態管理中,Hibernate對PO采取何種策略。

    例如當你寫一個DAOImpl的時候,讓cat對象增加一個mate,如下定義:
    java代碼: 

    1 public void addMate(Cat cat, Mate mate) {
     2     Session session = ...;
     3     Transacton tx = ...;
     4     session.update(cat);
     5     cat.addMate(mate);
     6     tx.commit();
     7     session.close();
     8 };



    顯然你是需要把Hibernate的操作封裝在DAO里面的,讓業務層的程序員和Web層的程序員不需要了解Hibernate,直接對DAO進行調用。

    此時問題就來了:上面的代碼運行正確有一個必要的前提,那就是方法調用參數cat對象必須是一個已經被持久化過的PO,也就是來說,它應該首先從數據庫查詢出來,然后才能這樣用。但是業務層的程序員顯然不知道這種內部的玄妙,如果他的業務是現在增加一個cat,然后再增加它的mate,他顯然會這樣調用,new一個cat對象出來,然后就addMate:

    java代碼: 

     1 Cat cat = new Cat();
     2 cat.setXXX();
     3 daoimpl.addMate(cat,mate);



    但是請注意看,這個cat對象只是一個VO,它沒有被持久化過,它還不是PO,它沒有資格調用addMate方法,因此調用addMate方法不會真正往數據庫里面發送update的sql,這個cat對象必須先被save到數據庫,在真正成為一個PO之后,才具備addMate的資格。

    你必須這樣來操作:

    java代碼: 

     1 Cat cat = new Cat();
     2 cat.setXXX();
     3 daoimpl.addCat(cat);
     4 daoimpl.addMate(cat, mate);



    先持久化cat,然后才能對cat進行其他的持久化操作。因此要求業務層的程序員必須清楚cat對象處于何種狀態,到底是第一種,還是第三種。如果是第一種,就要先save,再addMate;如果是第三種,就直接addMate。

    但是最致命的是,如果整個軟件分層很多,業務層的程序員他拿到這個cat對象也可能是上層Web應用層傳遞過來的cat,他自己也不知道這個cat究竟是VO,沒有被持久化過,還是已經被持久化過,那么他根本就沒有辦法寫程序了。

    所以這樣的DAOImpl顯然是有問題的,它會對業務層的程序員造成很多編程上的陷阱,業務層的程序員必須深刻的了解他調用的每個DAO對PO對象進行了何種狀態管理,必須深刻的了解他的PO對象在任何時候處于什么確切的狀態,才能保證編程的正確性,顯然這是做不到的,但是有了saveOrUpdate,這些問題就迎刃而解了。

    現在你需要修改addMate方法:

    java代碼: 

    1 public void addMate(Cat cat, Mate mate) {
     2     Session session = ...;
     3     Transacton tx = ...;
     4     session.saveOrUpdate(cat);
     5     cat.addMate(mate);
     6     tx.commit();
     7     session.close();
     8 };



    如上,如果業務層的程序員傳進來的是一個已經持久化過的PO對象,那么Hibernate會更新cat對象(假設業務層的程序員在Session外面修改過cat的屬性),如果傳進來的是一個新new出來的對象,那么向數據庫save這個PO對象。

    BTW: Hibernate此時究竟采取更新cat對象,還是save cat對象,取決于unsave-value的設定。

    這樣,業務層的程序員就不必再操心PO的狀態問題了,對于他們來說,不管cat是new出來的對象,只是一個VO也好;還是從數據庫查詢出來的的PO對象也好,全部都是直接addMate就OK了:

    posted @ 2006-03-02 20:44 killvin| 編輯 收藏

    今天同事問我Struts如何解決文件上傳的問題,一時間沒有想起來,就到google查了一下,果然在Struts Wiki上就有非常詳細的解釋,抄錄如下,詳細的請看(http://wiki.apache.org/struts/StrutsFileUpload)



    StrutsFileUpload
    File Upload - Simple Example
    HTML
    This isn't specific to Struts, but gives a simple example of the HTML required to upload a single file.

    Two things are needed in the html page. Firstly, the form needs to specify an enctype of multipart/form-data and secondly an form control of type file.

    JSP
    The above HTML can be generated using the Struts tags in the following way

    <html:form action="/uploadMyFile.do" enctype="multipart/form-data">

    Select File: <html:file property="myFile">

    <html:submit value="Upload File"/>

    </html:form>

    ActionForm
    The ActionForm needs a property of type FormFile.

    Regular ActionForms
    import org.apache.struts.upload.FormFile;

    public class MyActionForm extends ActionForm {

    private FormFile myFile;

    public void setMyFile(FormFile myFile) {
    this.myFile = myfile;
    }

    public FormFile getMyFile() {
    return myFile;
    }
    }

    Dyna ActionForms
    In the struts-config.xml

    <form-bean name="myForm" type="org.apache.struts.action.DynaActionForm">
    <form-property name="myFile" type="org.apache.struts.upload.FormFile"/>
    </form-bean>

    Whats Needed in the Action
    Nothing special really, just retrieve the FormFile from the ActionForm, as you would any other property, and process it as you like. You can get the file name, size and file contents from the FormFile.

    public ActionForward execute(ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response) throws Exception {

    MyActionForm myForm = (MyActionForm)form;

    // Process the FormFile
    FormFile myFile = myForm.getMyFile();
    String contentType = myFile.getContentType();
    String fileName = myFile.getFileName();
    int fileSize = myFile.getFileSize();
    byte[] fileData = myFile.getFileData();
    ...
    }

    File Upload Configuration
    The following parameters can be set in the <controller> element of the struts-config.xml to configure file upload:

    bufferSize - The size (in bytes) of the input buffer used when processing file uploads. Default is 4096.

    maxFileSize - The maximum size (in bytes) of a file to be accepted as a file upload. Can be expressed as a number followed by a "K", "M", or "G", which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. Default is 250M.

    multipartClass - The fully qualified Java class name of the multipart request handler class to be used with this module. Defaults is org.apache.struts.upload.CommonsMultipartRequestHandler.

    tempDir - Temporary working directory to use when processing file uploads.

    Above taken from the Configuration section in the User Guide.

    Plugging in an Alternative File Upload Mechanism
    By default Struts uses Commons File Upload.

    Alternative implementations can be plugged as long as they implement the org.apache.struts.upload.MultipartRequestHandler interface and Struts configured to use that implementation by specifying it in the multipartClass parameter in the <controller> element of the struts-config.xml

    Fair Warning: The MultipartRequestHandler interface is almost certain to change in a Struts 1.3 or higher release.

    posted @ 2006-03-02 20:43 killvin| 編輯 收藏

            以前總是認為php只是一種jsp的翻版,沒有啥意思!不象J2EE的體系結構那樣,有應用服務器的概念,只是一個簡單的服務器腳本,而昨天的看法卻讓我大跌眼鏡,其實php和java、python都處在一個層次上,甚至比java更有優勢!不應該僅僅只是將php和jsp看作是同樣的檔次,這就好比是告訴寫python的程序員,你們開發不了應用程序一樣的讓別人笑掉大牙,甚至懷疑你的腦子有毛病,zope就是一個很好的例子!!優秀的應用服務器,就是用Python開發的!
            
            我們更應該將php看作是一個語言,和python同樣優秀(當然php和python的定位不同,也就是因為這樣的定位,而讓人產生的錯覺!!)如果Php將自己定位如python一樣!并在語言這個級別更貼近面向對象的思維模式,那Python也許就不會如此的火爆!
            
            看來php之父并沒有將語言向python靠攏的意思,也就是說這樣的語言目前的生存空間僅僅只限于服務器端的腳本!這是它的領地!但除非是它閉著眼鏡,不然沒有人會愿意看到html的代碼里寫這<?php>這樣的代碼!這就好比是在沒有webwork 、struts這樣優秀的web框架出現之前,jsp讓人惡心的原因,如果Php的領域沒有這樣的MVC框架出現,那我情愿還是選擇jsp!!出于學習我會選擇Python,但永遠不會選擇php......
            
            告訴我你知道的php世界里優秀的web框架!也許我會改變看法!但我不會選擇重新發明輪子!!

     

    posted @ 2006-03-02 20:26 killvin| 編輯 收藏

    Thinking in Python

    You can download the current version of Thinking in Python here. This includes the BackTalk comment collection system that I built in Zope.

    The page describing this project is here.

    The current version of the book is 0.1. This is a preliminary release; please note that not all the chapters in the book have been translated.

    The source code is in the download package. When you unzip everything (remember to use the -a flag if you're on Unix), the code will be put into subdirectories for each chapter.

    This is not an introductory Python book. This book assumes you've learned the basics of Python elsewhere. I personally like Learning Python by Lutz & Ascher, from O'Reilly. Although it does not cover all the Python 2.0, 2.1 and 2.2 features that I use in this book, most books don't at this point.

    However, Learning Python is not exactly a beginning programmer's book, either (although it's possible if you're dedicated). If you're just getting started with programming you might try the free, downloadable A Byte of Python.

    posted @ 2006-03-02 20:23 killvin| 編輯 收藏

    僅列出標題
    共5頁: 上一頁 1 2 3 4 5 下一頁 
    主站蜘蛛池模板: 亚洲性天天干天天摸| 国产精品四虎在线观看免费| 亚洲一区二区成人| 亚洲狠狠婷婷综合久久久久| 麻豆视频免费播放| 国产成人亚洲综合在线| 亚洲日韩小电影在线观看| 久久久久久久91精品免费观看| 免费人成又黄又爽的视频在线电影| 亚洲高清视频在线播放| 亚洲国产人成精品| 国产黄色片免费看| 亚洲中文字幕无码久久2020| 久久久久亚洲AV综合波多野结衣 | 亚洲色婷婷综合久久| 国产亚洲av人片在线观看| 毛片基地免费视频a| 97超高清在线观看免费视频| 激情综合亚洲色婷婷五月APP| 亚洲乱码日产精品a级毛片久久| 日韩精品电影一区亚洲| 丁香花免费完整高清观看| 中文字幕av免费专区| 美女视频黄a视频全免费网站色 | 亚洲综合激情另类专区| 成人免费看黄20分钟| 99精品视频免费在线观看| 牛牛在线精品免费视频观看| 人成午夜免费大片在线观看| 亚洲爆乳AAA无码专区| 亚洲成在人天堂一区二区| 亚洲高清最新av网站| 亚洲精品亚洲人成在线观看| 久久精品国产亚洲77777| 亚洲jjzzjjzz在线观看| 亚洲AV无码专区在线厂| 一本色道久久88—综合亚洲精品| 亚洲国产成人AV网站| 精品无码一级毛片免费视频观看| 国产成人亚洲综合在线| 国产色爽免费无码视频|