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

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

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

    posts - 51, comments - 17, trackbacks - 0, articles - 9
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    Historical Collection Classes(JDK1.1 之前)
    提供的容器有Arrays,Vector,Stack,Hashtable,Properties,BitSet。其中定義出一種走訪群集內(nèi)各元素的標(biāo)準(zhǔn)方式,稱(chēng)為Enumeration(列舉器)接口,用法如下:
    Vector v=new Vector();
    for (Enumeration enum =v.elements(); enum.hasMoreElements();) {
    Object o 
    = enum.nextElement();
    processObject(o);
    }

    而在JDK1.2版本中引入了Iterator接口,新版本的集合對(duì)象(HashSet,HashMap,WeakHeahMap,ArrayList,TreeSet,TreeMap, LinkedList)是通過(guò)Iterator接口訪問(wèn)集合元素的。
    例如:
    List list=new ArrayList();
    for(Iterator it=list.iterator();it.hasNext();)
    {
        System.out.println(it.next());
    }

    這樣,如果將老版本的程序運(yùn)行在新的Java編譯器上就會(huì)出錯(cuò)。因?yàn)長(zhǎng)ist接口中已經(jīng)沒(méi)有elements(),而只有iterator()了。那么如何可以使老版本的程序運(yùn)行在新的Java編譯器上呢?如果不加修改,是肯定不行的,但是修改要遵循“開(kāi)-閉”原則。
    這時(shí)候我想到了Java設(shè)計(jì)模式中的適配器模式。


    package net.blogjava.lzqdiy;

    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.Iterator;
    import java.util.List;

    public class NewEnumeration implements Enumeration
    {

        Iterator it;
        
    public NewEnumeration(Iterator it)
        
    {
            
    this.it=it;
            
    // TODO Auto-generated constructor stub
        }


        
    public boolean hasMoreElements()
        
    {
            
    // TODO Auto-generated method stub
            return it.hasNext();
        }


        
    public Object nextElement()
        
    {
            
    // TODO Auto-generated method stub
            return it.next();
        }

        
    public static void main(String[] args)
        
    {
            List list
    =new ArrayList();
            list.add(
    "a");
            list.add(
    "b");
            list.add(
    "C");
            
    for(Enumeration e=new NewEnumeration(list.iterator());e.hasMoreElements();)
            
    {
                System.out.println(e.nextElement());
            }

        }

    }

    NewEnumeration是一個(gè)適配器類(lèi),通過(guò)它實(shí)現(xiàn)了從Iterator接口到Enumeration接口的適配,這樣我們就可以使用老版本的代碼來(lái)使用新的集合對(duì)象了。

    posted @ 2007-04-23 10:35 chenweicai 閱讀(164) | 評(píng)論 (0)編輯 收藏

    一、反射的概念 :
    反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問(wèn)、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。這一概念的提出很快引發(fā)了計(jì)算機(jī)科學(xué)領(lǐng)域關(guān)于應(yīng)用反射性的研究。它首先被程序語(yǔ)言的設(shè)計(jì)領(lǐng)域所采用,并在Lisp和面向?qū)ο蠓矫嫒〉昧顺煽?jī)。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射機(jī)制的語(yǔ)言。最近,反射機(jī)制也被應(yīng)用到了視窗系統(tǒng)、操作系統(tǒng)和文件系統(tǒng)中。

    反射本身并不是一個(gè)新概念,它可能會(huì)使我們聯(lián)想到光學(xué)中的反射概念,盡管計(jì)算機(jī)科學(xué)賦予了反射概念新的含義,但是,從現(xiàn)象上來(lái)說(shuō),它們確實(shí)有某些相通之處,這些有助于我們的理解。在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類(lèi)應(yīng)用,它們能夠自描述自控制。也就是說(shuō),這類(lèi)應(yīng)用通過(guò)采用某種機(jī)制來(lái)實(shí)現(xiàn)對(duì)自己行為的描述(self-representation)和監(jiān)測(cè)(examination),并能根據(jù)自身行為的狀態(tài)和結(jié)果,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語(yǔ)義。可以看出,同一般的反射概念相比,計(jì)算機(jī)科學(xué)領(lǐng)域的反射不單單指反射本身,還包括對(duì)反射結(jié)果所采取的措施。所有采用反射機(jī)制的系統(tǒng)(即反射系統(tǒng))都希望使系統(tǒng)的實(shí)現(xiàn)更開(kāi)放??梢哉f(shuō),實(shí)現(xiàn)了反射機(jī)制的系統(tǒng)都具有開(kāi)放性,但具有開(kāi)放性的系統(tǒng)并不一定采用了反射機(jī)制,開(kāi)放性是反射系統(tǒng)的必要條件。一般來(lái)說(shuō),反射系統(tǒng)除了滿足開(kāi)放性條件外還必須滿足原因連接(Causally-connected)。所謂原因連接是指對(duì)反射系統(tǒng)自描述的改變能夠立即反映到系統(tǒng)底層的實(shí)際狀態(tài)和行為上的情況,反之亦然。開(kāi)放性和原因連接是反射系統(tǒng)的兩大基本要素.

    Java中,反射是一種強(qiáng)大的工具。它使您能夠創(chuàng)建靈活的代碼,這些代碼可以在運(yùn)行時(shí)裝配,無(wú)需在組件之間進(jìn)行源代表鏈接。反射允許我們?cè)诰帉?xiě)與執(zhí)行時(shí),使我們的程序代碼能夠接入裝載到JVM中的類(lèi)的內(nèi)部信息,而不是源代碼中選定的類(lèi)協(xié)作的代碼。這使反射成為構(gòu)建靈活的應(yīng)用的主要工具。但需注意的是:如果使用不當(dāng),反射的成本很高。

    二、Java中的類(lèi)反射:
    Reflection 是 Java 程序開(kāi)發(fā)語(yǔ)言的特征之一,它允許運(yùn)行中的 Java 程序?qū)ψ陨磉M(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內(nèi)部屬性。Java 的這一能力在實(shí)際應(yīng)用中也許用得不是很多,但是在其它的程序設(shè)計(jì)語(yǔ)言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒(méi)有辦法在程序中獲得函數(shù)定義相關(guān)的信息。

    1.檢測(cè)類(lèi):

    1.1 reflection的工作機(jī)制

    考慮下面這個(gè)簡(jiǎn)單的例子,讓我們看看 reflection 是如何工作的。

    import java.lang.reflect.*;
    public class DumpMethods {
        public static void main(String args[]) {
            try {
                Class c = Class.forName(args[0]);
                Method m[] = c.getDeclaredMethods();
                for (int i = 0; i < m.length; i++)
                    System.out.println(m[i].toString());
            } catch (Throwable e) {
                System.err.println(e);
            }
        }
    }

    按如下語(yǔ)句執(zhí)行:

    java DumpMethods java.util.Stack

    它的結(jié)果輸出為:

    public java.lang.Object java.util.Stack.push(java.lang.Object)

    public synchronized java.lang.Object java.util.Stack.pop()

    public synchronized java.lang.Object java.util.Stack.peek()

    public boolean java.util.Stack.empty()

    public synchronized int java.util.Stack.search(java.lang.Object)

    這樣就列出了java.util.Stack 類(lèi)的各方法名以及它們的限制符和返回類(lèi)型。

    這個(gè)程序使用 Class.forName 載入指定的類(lèi),然后調(diào)用 getDeclaredMethods 來(lái)獲取這個(gè)類(lèi)中定義了的方法列表。java.lang.reflect.Methods 是用來(lái)描述某個(gè)類(lèi)中單個(gè)方法的一個(gè)類(lèi)。

    1.2 Java類(lèi)反射中的主要方法

    對(duì)于以下三類(lèi)組件中的任何一類(lèi)來(lái)說(shuō) -- 構(gòu)造函數(shù)、字段和方法 -- java.lang.Class 提供四種獨(dú)立的反射調(diào)用,以不同的方式來(lái)獲得信息。調(diào)用都遵循一種標(biāo)準(zhǔn)格式。以下是用于查找構(gòu)造函數(shù)的一組反射調(diào)用:

           Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數(shù)類(lèi)型的公共構(gòu)造函數(shù),

           Constructor[] getConstructors() -- 獲得類(lèi)的所有公共構(gòu)造函數(shù)

           Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數(shù)類(lèi)型的構(gòu)造函數(shù)(與接入級(jí)別無(wú)關(guān))

           Constructor[] getDeclaredConstructors() -- 獲得類(lèi)的所有構(gòu)造函數(shù)(與接入級(jí)別無(wú)關(guān))

    獲得字段信息的Class 反射調(diào)用不同于那些用于接入構(gòu)造函數(shù)的調(diào)用,在參數(shù)類(lèi)型數(shù)組中使用了字段名:

    l         Field getField(String name) -- 獲得命名的公共字段

    l         Field[] getFields() -- 獲得類(lèi)的所有公共字段

    l         Field getDeclaredField(String name) -- 獲得類(lèi)聲明的命名的字段

    l         Field[] getDeclaredFields() -- 獲得類(lèi)聲明的所有字段

    用于獲得方法信息函數(shù):

    l         Method getMethod(String name, Class[] params) -- 使用特定的參數(shù)類(lèi)型,獲得命名的公共方法

    l         Method[] getMethods() -- 獲得類(lèi)的所有公共方法

    l         Method getDeclaredMethod(String name, Class[] params) -- 使用特寫(xiě)的參數(shù)類(lèi)型,獲得類(lèi)聲明的命名的方法

    l         Method[] getDeclaredMethods() -- 獲得類(lèi)聲明的所有方法

     

    1.3開(kāi)始使用 Reflection:

    用于 reflection 的類(lèi),如 Method,可以在 java.lang.relfect 包中找到。使用這些類(lèi)的時(shí)候必須要遵循三個(gè)步驟:第一步是獲得你想操作的類(lèi)的 java.lang.Class 對(duì)象。在運(yùn)行中的 Java 程序中,用 java.lang.Class 類(lèi)來(lái)描述類(lèi)和接口等。

    下面就是獲得一個(gè) Class 對(duì)象的方法之一:

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

    這條語(yǔ)句得到一個(gè) String 類(lèi)的類(lèi)對(duì)象。還有另一種方法,如下面的語(yǔ)句:

    Class c = int.class;

    或者

    Class c = Integer.TYPE;

    它們可獲得基本類(lèi)型的類(lèi)信息。其中后一種方法中訪問(wèn)的是基本類(lèi)型的封裝類(lèi) (如 Integer) 中預(yù)先定義好的 TYPE 字段。

    第二步是調(diào)用諸如 getDeclaredMethods 的方法,以取得該類(lèi)中定義的所有方法的列表。

    一旦取得這個(gè)信息,就可以進(jìn)行第三步了——使用 reflection API 來(lái)操作這些信息,如下面這段代碼:

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

    Method m[] = c.getDeclaredMethods();

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

    它將以文本方式打印出 String 中定義的第一個(gè)方法的原型。

    2.處理對(duì)象:

    如果要作一個(gè)開(kāi)發(fā)工具像debugger之類(lèi)的,你必須能發(fā)現(xiàn)filed values,以下是三個(gè)步驟:

    a.創(chuàng)建一個(gè)Class對(duì)象
    b.通過(guò)getField 創(chuàng)建一個(gè)Field對(duì)象
    c.調(diào)用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對(duì)象就省略;Object是指實(shí)例).

    例如:
    import java.lang.reflect.*;
    import java.awt.*;

    class SampleGet {

       public static void main(String[] args) {
          Rectangle r = new Rectangle(100, 325);
          printHeight(r);

       }

       static void printHeight(Rectangle r) {
          Field heightField;
          Integer heightValue;
          Class c = r.getClass();
          try {
            heightField = c.getField("height");
            heightValue = (Integer) heightField.get(r);
            System.out.println("Height: " + heightValue.toString());
          } catch (NoSuchFieldException e) {
              System.out.println(e);
          } catch (SecurityException e) {
              System.out.println(e);
          } catch (IllegalAcces***ception e) {
              System.out.println(e);
          }
       }
    }

    三、安全性和反射:
    在處理反射時(shí)安全性是一個(gè)較復(fù)雜的問(wèn)題。反射經(jīng)常由框架型代碼使用,由于這一點(diǎn),我們可能希望框架能夠全面接入代碼,無(wú)需考慮常規(guī)的接入限制。但是,在其它情況下,不受控制的接入會(huì)帶來(lái)嚴(yán)重的安全性風(fēng)險(xiǎn),例如當(dāng)代碼在不值得信任的代碼共享的環(huán)境中運(yùn)行時(shí)。

    由于這些互相矛盾的需求,Java編程語(yǔ)言定義一種多級(jí)別方法來(lái)處理反射的安全性?;灸J绞菍?duì)反射實(shí)施與應(yīng)用于源代碼接入相同的限制:

    n         從任意位置到類(lèi)公共組件的接入

    n         類(lèi)自身外部無(wú)任何到私有組件的接入

    n         受保護(hù)和打包(缺省接入)組件的有限接入

    不過(guò)至少有些時(shí)候,圍繞這些限制還有一種簡(jiǎn)單的方法。我們可以在我們所寫(xiě)的類(lèi)中,擴(kuò)展一個(gè)普通的基本類(lèi)java.lang.reflect.AccessibleObject 類(lèi)。這個(gè)類(lèi)定義了一種setAccessible方法,使我們能夠啟動(dòng)或關(guān)閉對(duì)這些類(lèi)中其中一個(gè)類(lèi)的實(shí)例的接入檢測(cè)。唯一的問(wèn)題在于如果使用了安全性管理器,它將檢測(cè)正在關(guān)閉接入檢測(cè)的代碼是否許可了這樣做。如果未許可,安全性管理器拋出一個(gè)例外。

    下面是一段程序,在TwoString 類(lèi)的一個(gè)實(shí)例上使用反射來(lái)顯示安全性正在運(yùn)行:

    public class ReflectSecurity {

        public static void main(String[] args) {

            try {

                TwoString ts = new TwoString("a", "b");

                Field field = clas.getDeclaredField("m_s1");

    //          field.setAccessible(true);

                System.out.println("Retrieved value is " +

                    field.get(inst));

            } catch (Exception ex) {

                ex.printStackTrace(System.out);

            }

        }

    }

    如果我們編譯這一程序時(shí),不使用任何特定參數(shù)直接從命令行運(yùn)行,它將在field .get(inst)調(diào)用中拋出一個(gè)IllegalAcces***ception異常。如果我們不注釋field.setAccessible(true)代碼行,那么重新編譯并重新運(yùn)行該代碼,它將編譯成功。最后,如果我們?cè)诿钚刑砑恿薐VM參數(shù)-Djava.security.manager以實(shí)現(xiàn)安全性管理器,它仍然將不能通過(guò)編譯,除非我們定義了ReflectSecurity類(lèi)的許可權(quán)限。

    四、反射性能:
    反射是一種強(qiáng)大的工具,但也存在一些不足。一個(gè)主要的缺點(diǎn)是對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類(lèi)操作總是慢于只直接執(zhí)行相同的操作。

    下面的程序是字段接入性能測(cè)試的一個(gè)例子,包括基本的測(cè)試方法。每種方法測(cè)試字段接入的一種形式 -- accessSame 與同一對(duì)象的成員字段協(xié)作,accessOther 使用可直接接入的另一對(duì)象的字段,accessReflection 使用可通過(guò)反射接入的另一對(duì)象的字段。在每種情況下,方法執(zhí)行相同的計(jì)算 -- 循環(huán)中簡(jiǎn)單的加/乘順序。

    程序如下:

    public int accessSame(int loops) {

        m_value = 0;

        for (int index = 0; index < loops; index++) {

            m_value = (m_value + ADDITIVE_VALUE) *

                MULTIPLIER_VALUE;

        }

        return m_value;

    }

     

    public int accessReference(int loops) {

        TimingClass timing = new TimingClass();

        for (int index = 0; index < loops; index++) {

            timing.m_value = (timing.m_value + ADDITIVE_VALUE) *

                MULTIPLIER_VALUE;

        }

        return timing.m_value;

    }

     

    public int accessReflection(int loops) throw* **ception {

        TimingClass timing = new TimingClass();

        try {

            Field field = TimingClass.class.

                getDeclaredField("m_value");

            for (int index = 0; index < loops; index++) {

                int value = (field.getInt(timing) +

                    ADDITIVE_VALUE) * MULTIPLIER_VALUE;

                field.setInt(timing, value);

            }

            return timing.m_value;

        } catch (Exception ex) {

            System.out.println("Error using reflection");

            throw ex;

        }

    }

    在上面的例子中,測(cè)試程序重復(fù)調(diào)用每種方法,使用一個(gè)大循環(huán)數(shù),從而平均多次調(diào)用的時(shí)間衡量結(jié)果。平均值中不包括每種方法第一次調(diào)用的時(shí)間,因此初始化時(shí)間不是結(jié)果中的一個(gè)因素。下面的圖清楚的向我們展示了每種方法字段接入的時(shí)間:

    圖 1:字段接入時(shí)間 :


    我們可以看出:在前兩副圖中(Sun JVM),使用反射的執(zhí)行時(shí)間超過(guò)使用直接接入的1000倍以上。通過(guò)比較,IBM JVM可能稍好一些,但反射方法仍舊需要比其它方法長(zhǎng)700倍以上的時(shí)間。任何JVM上其它兩種方法之間時(shí)間方面無(wú)任何顯著差異,但I(xiàn)BM JVM幾乎比Sun JVM快一倍。最有可能的是這種差異反映了Sun Hot Spot JVM的專(zhuān)業(yè)優(yōu)化,它在簡(jiǎn)單基準(zhǔn)方面表現(xiàn)得很糟糕。反射性能是Sun開(kāi)發(fā)1.4 JVM時(shí)關(guān)注的一個(gè)方面,它在反射方法調(diào)用結(jié)果中顯示。在這類(lèi)操作的性能方面,Sun 1.4.1 JVM顯示了比1.3.1版本很大的改進(jìn)。

    如果為為創(chuàng)建使用反射的對(duì)象編寫(xiě)了類(lèi)似的計(jì)時(shí)測(cè)試程序,我們會(huì)發(fā)現(xiàn)這種情況下的差異不象字段和方法調(diào)用情況下那么顯著。使用newInstance()調(diào)用創(chuàng)建一個(gè)簡(jiǎn)單的java.lang.Object實(shí)例耗用的時(shí)間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的兩部。使用Array.newInstance(type, size)創(chuàng)建一個(gè)數(shù)組耗用的時(shí)間是任何測(cè)試的JVM上使用new type[size]的兩倍,隨著數(shù)組大小的增加,差異逐步縮小。

    結(jié)束語(yǔ):
    Java語(yǔ)言反射提供一種動(dòng)態(tài)鏈接程序組件的多功能方法。它允許程序創(chuàng)建和控制任何類(lèi)的對(duì)象(根據(jù)安全性限制),無(wú)需提前硬編碼目標(biāo)類(lèi)。這些特性使得反射特別適用于創(chuàng)建以非常普通的方式與對(duì)象協(xié)作的庫(kù)。例如,反射經(jīng)常在持續(xù)存儲(chǔ)對(duì)象為數(shù)據(jù)庫(kù)、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使類(lèi)和數(shù)據(jù)結(jié)構(gòu)能按名稱(chēng)動(dòng)態(tài)檢索相關(guān)信息,并允許在運(yùn)行著的程序中操作這些信息。Java 的這一特性非常強(qiáng)大,并且是其它一些常用語(yǔ)言,如 C、C++、Fortran 或者 Pascal 等都不具備的。

    但反射有兩個(gè)缺點(diǎn)。第一個(gè)是性能問(wèn)題。用于字段和方法接入時(shí)反射要遠(yuǎn)慢于直接代碼。性能問(wèn)題的程度取決于程序中是如何使用反射的。如果它作為程序運(yùn)行中相對(duì)很少涉及的部分,緩慢的性能將不會(huì)是一個(gè)問(wèn)題。即使測(cè)試中最壞情況下的計(jì)時(shí)圖顯示的反射操作只耗用幾微秒。僅反射在性能關(guān)鍵的應(yīng)用的核心邏輯中使用時(shí)性能問(wèn)題才變得至關(guān)重要。

    許多應(yīng)用中更嚴(yán)重的一個(gè)缺點(diǎn)是使用反射會(huì)模糊程序內(nèi)部實(shí)際要發(fā)生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過(guò)了源代碼的技術(shù)會(huì)帶來(lái)維護(hù)問(wèn)題。反射代碼比相應(yīng)的直接代碼更復(fù)雜,正如性能比較的代碼實(shí)例中看到的一樣。解決這些問(wèn)題的最佳方案是保守地使用反射——僅在它可以真正增加靈活性的地方——記錄其在目標(biāo)類(lèi)中的使用。

    posted @ 2007-04-23 10:27 chenweicai 閱讀(157) | 評(píng)論 (0)編輯 收藏

    public class ServerSocket
    extends Object
    
    

    此類(lèi)實(shí)現(xiàn)服務(wù)器套接字。服務(wù)器套接字等待請(qǐng)求通過(guò)網(wǎng)絡(luò)傳入。它基于該請(qǐng)求執(zhí)行某些操作,然后可能向請(qǐng)求者返回結(jié)果。

    服務(wù)器套接字的實(shí)際工作由 SocketImpl 類(lèi)的實(shí)例執(zhí)行。應(yīng)用程序可以更改創(chuàng)建套接字實(shí)現(xiàn)的套接字工廠來(lái)配置它自身,從而創(chuàng)建適合本地防火墻的套接字。



    public class Socket
    extends Object
    
    

    此類(lèi)實(shí)現(xiàn)客戶(hù)端套接字(也可以就叫“套接字”)。套接字是兩臺(tái)機(jī)器之間的通信端點(diǎn)。

    套接字的實(shí)際工作由 SocketImpl 類(lèi)的實(shí)例執(zhí)行。應(yīng)用程序通過(guò)更改創(chuàng)建套接字實(shí)現(xiàn)的套接字工廠可以配置它自身,以創(chuàng)建適合本地防火墻的套接字。


    /**
     * 為了驗(yàn)證我們的服務(wù)器程序能否正常工作,還必須有一個(gè)客戶(hù)端程序與之通信。
     * 我們也沒(méi)必要每次都要編寫(xiě)客戶(hù)端程序來(lái)測(cè)試。其實(shí), Windows提供的telnet
     * 程序,就是一個(gè)TCP客戶(hù)端程序,我們只要使用telnet程序?qū)ξ覀兊姆?wù)器程序
     * 進(jìn)行測(cè)試,我們只要在運(yùn)行telnet程序時(shí)指定所要連接的服務(wù)器程序的IP地址
     * 和端口號(hào),telnet程序就會(huì)按照指定的參數(shù)去與服務(wù)器程序進(jìn)行連接。建立連接
     * 后,在telnet程序窗口中鍵入的內(nèi)容會(huì)發(fā)送到服務(wù)器,從服務(wù)器端接收到的數(shù)據(jù)
     * 會(huì)顯示在窗口中。
     * 先運(yùn)行TcpServer程序,然后在命令行中輸入 telnet 10.214.16.80 8001
     * 就會(huì)執(zhí)行
     */

    // TCP 服務(wù)端程序
    public class TcpServer {

     public static void main(String[] args){
      
      try{
       //建立一個(gè)在8001端口上等待連接的ServerSocket對(duì)象
       ServerSocket ss = new ServerSocket(8001);
       
       //偵聽(tīng)并接受到此套接字的連接。此方法在進(jìn)行連接之前一直阻塞,返回客戶(hù)端套接字
       Socket s = ss.accept();
       
       InputStream ips = s.getInputStream();//返回此套接字的輸入流
       OutputStream ops = s.getOutputStream();//返回此套接字的輸出流
       
       //將字節(jié)從指定的字節(jié)數(shù)組寫(xiě)入此輸出流
       ops.write("chenweicai...".getBytes());
       
    //   byte[] buf = new byte[1024];
    //   // 從輸入流中讀取一定數(shù)量的字節(jié)并將其存儲(chǔ)在緩沖區(qū)數(shù)組buf中
    //   int len = ips.read(buf);
    //   System.out.println(new String(buf, 0, len));
       
       //由于telent只要輸入就發(fā)送,而不管有沒(méi)有回車(chē),所以只有第一個(gè)字符被發(fā)送。
       //java提供了一個(gè)BufferedReader類(lèi),可以幫助我們按行處理輸入流。
       BufferedReader br = new BufferedReader (new InputStreamReader(ips));
       System.out.println("The message comes form client: " + br.readLine());//輸出客戶(hù)端輸入的內(nèi)容
       
       br.close();//關(guān)閉包裝類(lèi),會(huì)自動(dòng)關(guān)閉包裝類(lèi)中所包裝的底層類(lèi),所以不用調(diào)用ips.close().
    //   ips.close();
       ops.close();
       s.close();
       ss.close();
      }catch (Exception e){
       e.printStackTrace();
      }
     }
    }

    給個(gè)具體的例子

    /**
     * 實(shí)現(xiàn)服務(wù)器和客戶(hù)端之間的對(duì)話功能
     */
    public class Servicer implements Runnable {

     Socket s;
     
     public Servicer (Socket s){
      this.s = s;
     }
     
     public void run(){
      
      try {
       InputStream ips = s.getInputStream();
       OutputStream ops = s.getOutputStream();
       BufferedReader br = new BufferedReader(new InputStreamReader(ips));
       DataOutputStream dos = new DataOutputStream(ops);
       
       while(true){
        String strWord = br.readLine();
        System.out.println("From Client StrWord: " + strWord + strWord.length());
        if(strWord.equalsIgnoreCase("quit"))
         break;
        
        String strEcho = (new StringBuffer(strWord).reverse()).toString();
        dos.writeBytes(strWord + "---->" + strEcho + System.getProperty("line.separator"));
       }
       br.close();
       dos.close();
       s.close();
      } catch (IOException e) {
       e.printStackTrace();
      }
      
     }
    }


    import java.net.ServerSocket;
    import java.net.Socket;

    public class TcpServer {

     public static void main(String[] args){
      
      try{
       ServerSocket ss = new ServerSocket(8001);
       
       while(true){
        Socket s = ss.accept();
        new Thread(new Servicer(s)).start();
       }
      }catch(Exception e){
       
      }
     }
    }



    public class TcpClient {

     public static void main(String[] args){
      
      try{
       if(args.length < 2){
        System.out.println("Usage: java TcpClient ServerIP ServerPort");
        return;
       }
       
       Socket s = new Socket(InetAddress.getByName(args[0]), Integer.parseInt(args[1]));
       InputStream ips = s.getInputStream();
       OutputStream ops = s.getOutputStream();
       
       BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));
       DataOutputStream dos = new DataOutputStream(ops);
       BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
       
       while(true){
        String strWord = brKey.readLine();
        dos.writeBytes(strWord + System.getProperty("ling.separator"));
        if(strWord.equalsIgnoreCase("quit"))
         break;
        else
         System.out.println(brNet.readLine());
       }
       dos.close();
       brNet.close();
       brKey.close();
       s.close();
      }catch (Exception e){
       
      }
     }
    }

    posted @ 2007-04-20 22:54 chenweicai 閱讀(367) | 評(píng)論 (0)編輯 收藏

    public class DatagramSocket
    extends Object

    此類(lèi)表示用來(lái)發(fā)送和接收數(shù)據(jù)報(bào)包的套接字。

    數(shù)據(jù)報(bào)套接字是包投遞服務(wù)的發(fā)送或接收點(diǎn)。每個(gè)在數(shù)據(jù)報(bào)套接字上發(fā)送或接收的包都是單獨(dú)編址和路由的。從一臺(tái)機(jī)器發(fā)送到另一臺(tái)機(jī)器的多個(gè)包可能選擇不同的路由,也可能按不同的順序到達(dá)。

    在 DatagramSocket 上總是啟用 UDP 廣播發(fā)送。為了接收廣播包,應(yīng)該將 DatagramSocket 綁定到通配符地址。在某些實(shí)現(xiàn)中,將 DatagramSocket 綁定到一個(gè)更加具體的地址時(shí)廣播包也可以被接收。

    示例:DatagramSocket s = new DatagramSocket(null); s.bind(new InetSocketAddress(8888)); 這等價(jià)于:DatagramSocket s = new DatagramSocket(8888); 兩個(gè)例子都能創(chuàng)建能夠在 UDP 8888 端口上接收廣播的 DatagramSocket。



    public final class DatagramPacket
    extends Object
    
    

    此類(lèi)表示數(shù)據(jù)報(bào)包。

    數(shù)據(jù)報(bào)包用來(lái)實(shí)現(xiàn)無(wú)連接包投遞服務(wù)。每條報(bào)文僅根據(jù)該包中包含的信息從一臺(tái)機(jī)器路由到另一臺(tái)機(jī)器。從一臺(tái)機(jī)器發(fā)送到另一臺(tái)機(jī)器的多個(gè)包可能選擇不同的路由,也可能按不同的順序到達(dá)。不對(duì)包投遞做出保證。



    給個(gè)具體實(shí)現(xiàn)的例子

    //UDP數(shù)據(jù)的發(fā)送類(lèi)似發(fā)送尋呼一樣的道理。就像尋呼機(jī)必須先處于開(kāi)機(jī)接收狀態(tài)才能接收尋呼一樣的道理
    //我們先要運(yùn)行UDP接收程序,再運(yùn)行UDP發(fā)送程序。UDP數(shù)據(jù)包的接收是過(guò)期作廢的。
    public class UdpSend {

     public static void main(String[] args) throws SocketException, UnknownHostException{
      DatagramSocket ds = new DatagramSocket();//創(chuàng)建用來(lái)發(fā)送數(shù)據(jù)報(bào)包的套接字
      String str = "Hello World,陳偉才";
      DatagramPacket dp = new DatagramPacket(str.getBytes(), str.getBytes().length,
        InetAddress.getByName("10.214.16.80"), 3000);
      //構(gòu)造數(shù)據(jù)報(bào)包,用來(lái)將長(zhǎng)度為 length 的包發(fā)送到指定主機(jī)上的指定端口號(hào)
      
      try {
       ds.send(dp);
      } catch (IOException e) {
       e.printStackTrace();
      }
      ds.close();
     }
    }

    public class UdpRecv {

     public static void main(String[] args) throws Exception{
      DatagramSocket ds = new DatagramSocket(3000);//創(chuàng)建接收數(shù)據(jù)報(bào)套接字并將其綁定到本地主機(jī)上的指定端口
      byte[] buf = new byte[1024];
      DatagramPacket dp = new DatagramPacket(buf, 1024);
      ds.receive(dp);
      String strRecv = new String (dp.getData(), 0, dp.getLength()) + " from "
      + dp.getAddress().getHostAddress() + ":" + dp.getPort();
      System.out.println(strRecv);
      ds.close();
      
     }
    }

    再給個(gè)例子Chat

    public class Chat {

     Frame f = new Frame("我的聊天室");
     
     /*
      * tfIp 是用于輸入IP地址的文本框,在發(fā)送數(shù)據(jù)時(shí), 要取出其中的IP地址,
      * 所以將其定義成員變量,以便發(fā)送消息的程序代碼能訪問(wèn)。
      */
     TextField tfIP = new TextField(15);
     
     /**
      * lst時(shí)用于顯示接收消息的列表框, 在接收到數(shù)據(jù)時(shí),要向其中增加新的記錄項(xiàng)
      * 所以將其定義成員變量,以便發(fā)送消息的程序代碼能訪問(wèn)
      */
     List lst = new List(6);
     
     DatagramSocket ds;
     
     public Chat(){
      try{
       ds = new DatagramSocket(3000);
      }catch(Exception e){
       e.printStackTrace();
      }
      
      new Thread (new Runnable(){
       
       public void run(){
        
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, 1024);
        while (true){
         try{
          ds.receive(dp);
          lst.add(new String(buf, 0 , dp.getLength()) +
            " : form " + dp.getAddress().getHostAddress(), 0);
         }catch(Exception ee){
          ee.printStackTrace();
         }
        }
       }
      }).start();
     }
     
     public void init(){
      f.setSize(300, 300);
      f.add(lst);
      
      Panel p = new Panel();
      p.setLayout(new BorderLayout());
      p.add("West", tfIP);
      TextField tfData = new TextField (20);
      p.add("East", tfData);
      f.add("South", p);
      
      f.setVisible(true);
      f.setResizable(false);//不能改變窗口大小
      
      //增加關(guān)閉窗口的事件處理代碼
      f.addWindowListener(new WindowAdapter(){
       public void windowClosing(WindowEvent e){
        ds.close();//程序退出時(shí),關(guān)閉Socket,釋放相關(guān)的資源
        f.setVisible(false);
        f.dispose();
        System.exit(0);
       }
      });
      
      //增加在消息文本框中按下回車(chē)鍵的事件處理代碼
      tfData.addActionListener(new ActionListener(){
       public void actionPerformed (ActionEvent e){
        //要在這里增加網(wǎng)絡(luò)消息發(fā)送相關(guān)程序代碼
        //下面的語(yǔ)句用于數(shù)據(jù)發(fā)送后,清空文本框中原來(lái)的內(nèi)容
        
        // 取出文本框的消息字符串,并將其轉(zhuǎn)換成字節(jié)數(shù)組
        byte[] buf;
        buf = e.getActionCommand().getBytes();
        try {
         DatagramPacket dp = new DatagramPacket(buf, buf.length,
           InetAddress.getByName(tfIP.getText()), 3000);
         try {
          ds.send(dp);
         } catch (IOException e1) {
          e1.printStackTrace();
         }
        } catch (UnknownHostException e1) {
         e1.printStackTrace();
        }
        ((TextField)e.getSource()).setText("");
       }
      });
     }
     
     public static void main(String[] args){
      Chat chat = new Chat();
      chat.init();
     }
    }

    posted @ 2007-04-20 22:50 chenweicai 閱讀(898) | 評(píng)論 (0)編輯 收藏

    課前思考
      1. 什么是TCP/ IP協(xié)議?
      2. TCP/IP有哪兩種傳輸協(xié)議,各有什么特點(diǎn)?
      3. 什么是URL
      4. URLIP地址有什么樣的關(guān)系?
      5. 什么叫套接字(Socket)?
      6. 套接字(Socket)和TCP/IP協(xié)議的關(guān)系?
      7. URL和套接字(Socket)的關(guān)系?
    8.1 網(wǎng)絡(luò)編程基本概念,TCP/IP協(xié)議簡(jiǎn)介

    8.1.1 網(wǎng)絡(luò)基礎(chǔ)知識(shí)

    網(wǎng)絡(luò)編程的目的就是指直接或間接地通過(guò)網(wǎng)絡(luò)協(xié)議與其他計(jì)算機(jī)進(jìn)行通訊。網(wǎng)絡(luò)編程中有兩個(gè)主要的問(wèn)題,一個(gè)是如何準(zhǔn)確的定位網(wǎng)絡(luò)上一臺(tái)或多臺(tái)主機(jī),另一個(gè)就是找到主機(jī)后如何可靠高效的進(jìn)行數(shù)據(jù)傳輸。在TCP/IP協(xié)議中IP層主要負(fù)責(zé)網(wǎng)絡(luò)主機(jī)的定位,數(shù)據(jù)傳輸?shù)穆酚桑?/span>IP地址可以唯一地確定Internet上的一臺(tái)主機(jī)。而TCP層則提供面向應(yīng)用的可靠的或非可靠的數(shù)據(jù)傳輸機(jī)制,這是網(wǎng)絡(luò)編程的主要對(duì)象,一般不需要關(guān)心IP層是如何處理數(shù)據(jù)的。
      目前較為流行的網(wǎng)絡(luò)編程模型是客戶(hù)機(jī)/服務(wù)器(C/S)結(jié)構(gòu)。即通信雙方一方作為服務(wù)器等待客戶(hù)提出請(qǐng)求并予以響應(yīng)??蛻?hù)則在需要服務(wù)時(shí)向服務(wù)器提出申請(qǐng)。服務(wù)器一般作為守護(hù)進(jìn)程始終運(yùn)行,監(jiān)聽(tīng)網(wǎng)絡(luò)端口,一旦有客戶(hù)請(qǐng)求,就會(huì)啟動(dòng)一個(gè)服務(wù)進(jìn)程來(lái)響應(yīng)該客戶(hù),同時(shí)自己繼續(xù)監(jiān)聽(tīng)服務(wù)端口,使后來(lái)的客戶(hù)也能及時(shí)得到服務(wù)。

    8.1.3兩類(lèi)傳輸協(xié)議:TCP;UDP
      盡管TCP/IP協(xié)議的名稱(chēng)中只有TCP這個(gè)協(xié)議名,但是在TCP/IP的傳輸層同時(shí)存在TCPUDP兩個(gè)協(xié)議。

    TCPTranfer Control Protocol的簡(jiǎn)稱(chēng),是一種面向連接的保證可靠傳輸?shù)膮f(xié)議。通過(guò)TCP協(xié)議傳輸,得到的是一個(gè)順序的無(wú)差錯(cuò)的數(shù)據(jù)流。發(fā)送方和接收方的成對(duì)的兩個(gè)socket之間必須建立連接,以便在TCP協(xié)議的基礎(chǔ)上進(jìn)行通信,當(dāng)一個(gè)socket(通常都是server socket)等待建立連接時(shí),另一個(gè)socket可以要求進(jìn)行連接,一旦這兩個(gè)socket連接起來(lái),它們就可以進(jìn)行雙向數(shù)據(jù)傳輸,雙方都可以進(jìn)行發(fā)送或接收操作。
      UDPUser Datagram Protocol的簡(jiǎn)稱(chēng),是一種無(wú)連接的協(xié)議,每個(gè)數(shù)據(jù)報(bào)都是一個(gè)獨(dú)立的信息,包括完整的源地址或目的地址,它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地,因此能否到達(dá)目的地,到達(dá)目的地的時(shí)間以及內(nèi)容的正確性都是不能被保證的。
      下面我們對(duì)這兩種協(xié)議做簡(jiǎn)單比較:
      使用UDP時(shí),每個(gè)數(shù)據(jù)報(bào)中都給出了完整的地址信息,因此無(wú)需要建立發(fā)送方和接收方的連接。對(duì)于TCP協(xié)議,由于它是一個(gè)面向連接的協(xié)議,在socket之間進(jìn)行數(shù)據(jù)傳輸之前必然要建立連接,所以在TCP中多了一個(gè)連接建立的時(shí)間。
     
     使用UDP傳輸數(shù)據(jù)時(shí)是有大小限制的,每個(gè)被傳輸?shù)臄?shù)據(jù)報(bào)必須限定在64KB之內(nèi)。而TCP沒(méi)有這方面的限制,一旦連接建立起來(lái),雙方的socket就可以按統(tǒng)一的格式傳輸大量的數(shù)據(jù)。UDP是一個(gè)不可靠的協(xié)議,發(fā)送方所發(fā)送的數(shù)據(jù)報(bào)并不一定以相同的次序到達(dá)接收方。而TCP是一個(gè)可靠的協(xié)議,它確保接收方完全正確地獲取發(fā)送方所發(fā)送的全部數(shù)據(jù)。
      總之,TCP在網(wǎng)絡(luò)通信上有極強(qiáng)的生命力,例如遠(yuǎn)程連接(Telnet)和文件傳輸(FTP)都需要不定長(zhǎng)度的數(shù)據(jù)被可靠地傳輸。相比之下UDP操作簡(jiǎn)單,而且僅需要較少的監(jiān)護(hù),因此通常用于局域網(wǎng)高可靠性的分散系統(tǒng)中client/server應(yīng)用程序。
      讀者可能要問(wèn),既然有了保證可靠傳輸?shù)?/span>TCP協(xié)議,為什么還要非可靠傳輸?shù)?/span>UDP協(xié)議呢?主要的原因有兩個(gè)。一是可靠的傳輸是要付出代價(jià)的,對(duì)數(shù)據(jù)內(nèi)容正確性的檢驗(yàn)必然占用計(jì)算機(jī)的處理時(shí)間和網(wǎng)絡(luò)的帶寬,因此TCP傳輸?shù)男什蝗?/span>UDP高。二是在許多應(yīng)用中并不需要保證嚴(yán)格的傳輸可靠性,比如視頻會(huì)議系統(tǒng),并不要求音頻視頻數(shù)據(jù)絕對(duì)的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會(huì)更合理一些。

    8.2 基于URL的高層次Java網(wǎng)絡(luò)編程

    8.2.1一致資源定位器URL

    URL(Uniform Resource Locator)是一致資源定位器的簡(jiǎn)稱(chēng),它表示Internet上某一資源的地址。通過(guò)URL我們可以訪問(wèn)Internet上的各種網(wǎng)絡(luò)資源,比如最常見(jiàn)的WWW,FTP站點(diǎn)。瀏覽器通過(guò)解析給定的URL可以在網(wǎng)絡(luò)上查找相應(yīng)的文件或其他資源。

    8.2.2 URL的組成

    protocol://resourceName
      協(xié)議名(protocol)指明獲取資源所使用的傳輸協(xié)議,如http、ftp、gopherfile等,資源名(resourceName)則應(yīng)該是資源的完整地址,包括主機(jī)名、端口號(hào)、文件名或文件內(nèi)部的一個(gè)引用。例如:
      http://www.sun.com/ 協(xié)議名://主機(jī)名
      http://home.netscape.com/home/welcome.html 協(xié)議名://機(jī)器名+文件名
      http://www.gamelan.com:80/Gamelan/network.html#BOTTOM 協(xié)議名://機(jī)器名+端口號(hào)+文件名+內(nèi)部引用.

    8.2.3 創(chuàng)建一個(gè)URL

    為了表示URL, java.net中實(shí)現(xiàn)了類(lèi)URL。我們可以通過(guò)下面的構(gòu)造方法來(lái)初始化一個(gè)URL對(duì)象:
      (1) public URL (String spec);
         通過(guò)一個(gè)表示URL地址的字符串可以構(gòu)造一個(gè)URL對(duì)象。
         URL urlBase=new URL("http://www. 263.net/")
     ?。?) public URL(URL context, String spec);
         通過(guò)基URL和相對(duì)URL構(gòu)造一個(gè)URL對(duì)象。
         URL net263=new URL ("http://www.263.net/");
         URL index263=new URL(net263, "index.html")
      (3) public URL(String protocol, String host, String file);
         new URL("http", "www.gamelan.com", "/pages/Gamelan.net. html");
     ?。?) public URL(String protocol, String host, int port, String file);
         URL gamelan=new URL("http", "www.gamelan.com", 80, "Pages/Gamelan.network.html");

      注意:類(lèi)URL的構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外(MalformedURLException),因此生成URL對(duì)象時(shí),我們必須要對(duì)這一例外進(jìn)行處理,通常是用try-catch語(yǔ)句進(jìn)行捕獲。格式如下:

    try{
         URL myURL= new URL(…)
      }catch (MalformedURLException e){
        }

    8.2.4 解析一個(gè)URL

    一個(gè)URL對(duì)象生成后,其屬性是不能被改變的,但是我們可以通過(guò)類(lèi)URL所提供的方法來(lái)獲取這些屬性:
       public String getProtocol() 獲取該URL的協(xié)議名。
       public String getHost() 獲取該URL的主機(jī)名。
       public int getPort() 獲取該URL的端口號(hào),如果沒(méi)有設(shè)置端口,返回-1。
       public String getFile() 獲取該URL的文件名。
       public String getRef() 獲取該URL在文件中的相對(duì)位置。
       public String getQuery() 獲取該URL的查詢(xún)信息。
       public String getPath() 獲取該URL的路徑
       public String getAuthority() 獲取該URL的權(quán)限信息
       public String getUserInfo() 獲得使用者的信息
        public String getRef() 獲得該URL的錨

    8.2.5 從URL讀取WWW網(wǎng)絡(luò)資源

    當(dāng)我們得到一個(gè)URL對(duì)象后,就可以通過(guò)它讀取指定的WWW資源。這時(shí)我們將使用URL的方法openStream(),其定義為:
             InputStream openStream();
      
      方法openSteam()與指定的URL建立連接并返回InputStream類(lèi)的對(duì)象以從這一連接中讀取數(shù)據(jù)。
      public class URLReader {
      public static void main(String[] args) throws Exception {
                          //聲明拋出所有例外
        URL tirc = new URL("http://www.tirc1.cs.tsinghua.edu.cn/");
                          //構(gòu)建一URL對(duì)象
        BufferedReader in = new BufferedReader(new InputStreamReader(tirc.openStream()));
        //使用openStream得到一輸入流并由此構(gòu)造一個(gè)BufferedReader對(duì)象
        String inputLine;
        while ((inputLine = in.readLine()) != null)
                     //從輸入流不斷的讀數(shù)據(jù),直到讀完為止
           System.out.println(inputLine); //把讀入的數(shù)據(jù)打印到屏幕上
        in.close(); //關(guān)閉輸入流
      }
      }

    8.2.6 通過(guò)URLConnetction連接WWW

    通過(guò)URL的方法openStream(),我們只能從網(wǎng)絡(luò)上讀取數(shù)據(jù),如果我們同時(shí)還想輸出數(shù)據(jù),例如向服務(wù)器端的CGI程序發(fā)送一些數(shù)據(jù),我們必須先與URL建立連接,然后才能對(duì)其進(jìn)行讀寫(xiě),這時(shí)就要用到類(lèi)URLConnection了。CGI是公共網(wǎng)關(guān)接口(Common Gateway Interface)的簡(jiǎn)稱(chēng),它是用戶(hù)瀏覽器和服務(wù)器端的應(yīng)用程序進(jìn)行連接的接口,有關(guān)CGI程序設(shè)計(jì),請(qǐng)讀者參考有關(guān)書(shū)籍。
      類(lèi)URLConnection也在包java.net中定義,它表示Java程序和URL在網(wǎng)絡(luò)上的通信連接。當(dāng)與一個(gè)URL建立連接時(shí),首先要在一個(gè)URL對(duì)象上通過(guò)方法openConnection()生成對(duì)應(yīng)的URLConnection對(duì)象。例如下面的程序段首先生成一個(gè)指向地址http://edu.chinaren.com/index.shtml的對(duì)象,然后用openConnection()打開(kāi)該URL對(duì)象上的一個(gè)連接,返回一個(gè)URLConnection對(duì)象。如果連接過(guò)程失敗,將產(chǎn)生IOException.
      Try{
        URL netchinaren = new URL ("http://edu.chinaren.com/index.shtml");
        URLConnectonn tc = netchinaren.openConnection();
      }catch(MalformedURLException e){ //創(chuàng)建URL()對(duì)象失敗
      
      }catch (IOException e){ //openConnection()失敗
      
      }
      類(lèi)URLConnection提供了很多方法來(lái)設(shè)置或獲取連接參數(shù),程序設(shè)計(jì)時(shí)最常使用的是getInputStream()getOurputStream(),其定義為:
         InputSteram getInputSteram();
         OutputSteram getOutputStream();
      通過(guò)返回的輸入/輸出流我們可以與遠(yuǎn)程對(duì)象進(jìn)行通信。看下面的例子:
      URL url =new URL ("http://www.javasoft.com/cgi-bin/backwards");
      //創(chuàng)建一URL對(duì)象
      URLConnectin con=url.openConnection();
      //URL對(duì)象獲取URLConnection對(duì)象
      DataInputStream dis=new DataInputStream (con.getInputSteam());
      //URLConnection獲取輸入流,并構(gòu)造DataInputStream對(duì)象
      PrintStream ps=new PrintSteam(con.getOutupSteam());
      //URLConnection獲取輸出流,并構(gòu)造PrintStream對(duì)象
      String line=dis.readLine(); //從服務(wù)器讀入一行
      ps.println("client…"); //向服務(wù)器寫(xiě)出字符串 "client…"
        其中backwards為服務(wù)器端的CGI程序。實(shí)際上,類(lèi)URL的方法openSteam()是通過(guò)URLConnection來(lái)實(shí)現(xiàn)的。它等價(jià)于
        openConnection().getInputStream();
      基于URL的網(wǎng)絡(luò)編程在底層其實(shí)還是基于下面要講的Socket接口的。WWW,FTP等標(biāo)準(zhǔn)化的網(wǎng)絡(luò)服務(wù)都是基于TCP協(xié)議的,所以本質(zhì)上講URL編程也是基于TCP的一種應(yīng)用.

    8.3 基于Socket的低層次Java網(wǎng)絡(luò)編程

    8.3.1 Socket通訊

    網(wǎng)絡(luò)上的兩個(gè)程序通過(guò)一個(gè)雙向的通訊連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)雙向鏈路的一端稱(chēng)為一個(gè)Socket。Socket通常用來(lái)實(shí)現(xiàn)客戶(hù)方和服務(wù)方的連接。SocketTCP/IP協(xié)議的一個(gè)十分流行的編程界面,一個(gè)Socket由一個(gè)IP地址和一個(gè)端口號(hào)唯一確定。
      在傳統(tǒng)的UNIX環(huán)境下可以操作TCP/IP協(xié)議的接口不止Socket一個(gè),Socket所支持的協(xié)議種類(lèi)也不光TCP/IP一種,因此兩者之間是沒(méi)有必然聯(lián)系的。在Java環(huán)境下,Socket編程主要是指基于TCP/IP協(xié)議的網(wǎng)絡(luò)編程。

    8.3.2 Socket通訊的一般過(guò)程

    使用Socket進(jìn)行Client/Server程序設(shè)計(jì)的一般連接過(guò)程是這樣的:ServerListen(監(jiān)聽(tīng))某個(gè)端口是否有連接請(qǐng)求,Client端向Server端發(fā)出Connect(連接)請(qǐng)求,Server端向Client端發(fā)回Accept(接受)消息。一個(gè)連接就建立起來(lái)了。Server端和Client端都可以通過(guò)Send,Write等方法與對(duì)方通信。

    對(duì)于一個(gè)功能齊全的Socket,都要包含以下基本結(jié)構(gòu),其工作過(guò)程包含以下四個(gè)基本的步驟:
     ?。?/span>1 創(chuàng)建Socket;
      (2 打開(kāi)連接到Socket的輸入/出流;
     ?。?/span>3 按照一定的協(xié)議對(duì)Socket進(jìn)行讀/寫(xiě)操作;
     ?。?/span>4 關(guān)閉Socket.

    8.3.3 創(chuàng)建Socket

    java在包java.net中提供了兩個(gè)類(lèi)SocketServerSocket,分別用來(lái)表示雙向連接的客戶(hù)端和服務(wù)端。這是兩個(gè)封裝得非常好的類(lèi),使用很方便。其構(gòu)造方法如下:
      Socket(InetAddress address, int port);
      Socket(InetAddress address, int port, boolean stream);
      Socket(String host, int prot);
      Socket(String host, int prot, boolean stream);
      Socket(SocketImpl impl)
      Socket(String host, int port, InetAddress localAddr, int localPort)
      Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
      ServerSocket(int port);
      ServerSocket(int port, int backlog);
      ServerSocket(int port, int backlog, InetAddress bindAddr)
      其中address、hostport分別是雙向連接中另一方的IP地址、主機(jī)名和端口號(hào),stream指明socket是流socket還是數(shù)據(jù)報(bào)socket,localPort表示本地主機(jī)的端口號(hào),localAddrbindAddr是本地機(jī)器的地址(ServerSocket的主機(jī)地址),implsocket的父類(lèi),既可以用來(lái)創(chuàng)建serverSocket又可以用來(lái)創(chuàng)建Socket。count則表示服務(wù)端所能支持的最大連接數(shù)。例如:
      Socket client = new Socket("127.0.01.", 80);
      ServerSocket server = new ServerSocket(80);
      注意,在選擇端口時(shí),必須小心。每一個(gè)端口提供一種特定的服務(wù),只有給出正確的端口,才能獲得相應(yīng)的服務(wù)。0~1023的端口號(hào)為系統(tǒng)所保留,例如http服務(wù)的端口號(hào)為80,telnet服務(wù)的端口號(hào)為21,ftp服務(wù)的端口號(hào)為23, 所以我們?cè)谶x擇端口號(hào)時(shí),最好選擇一個(gè)大于1023的數(shù)以防止發(fā)生沖突。
      在創(chuàng)建socket時(shí)如果發(fā)生錯(cuò)誤,將產(chǎn)生IOException,在程序中必須對(duì)之作出處理。所以在創(chuàng)建SocketServerSocket是必須捕獲或拋出例外。

    8.3.8 簡(jiǎn)單的Client/Server程序設(shè)計(jì)

    下面我們給出一個(gè)用Socket實(shí)現(xiàn)的客戶(hù)和服務(wù)器交互的典型的C/S結(jié)構(gòu)的演示程序,讀者通過(guò)仔細(xì)閱讀該程序,會(huì)對(duì)前面所討論的各個(gè)概念有更深刻的認(rèn)識(shí)。程序的意義請(qǐng)參考注釋。

    1. 客戶(hù)端程序
      import java.io.*;
      import java.net.*;
      public class TalkClient {
        public static void main(String args[]) {
          try{
            Socket socket=new Socket("127.0.0.1",4700);
            
    //向本機(jī)的4700端口發(fā)出客戶(hù)請(qǐng)求
            BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
            
    //由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造BufferedReader對(duì)象
            PrintWriter os=new PrintWriter(socket.getOutputStream());
            
    //由Socket對(duì)象得到輸出流,并構(gòu)造PrintWriter對(duì)象
            BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
    //由Socket對(duì)象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對(duì)象
            String readline;
            readline=sin.readLine();
    //從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串
            while(!readline.equals("bye")){
            
    //若從標(biāo)準(zhǔn)輸入讀入的字符串為 "bye"則停止循環(huán)
              os.println(readline);
              
    //將從系統(tǒng)標(biāo)準(zhǔn)輸入讀入的字符串輸出到Server
              os.flush();
              
    //刷新輸出流,使Server馬上收到該字符串
              System.out.println("Client:"+readline);

              //在系統(tǒng)標(biāo)準(zhǔn)輸出上打印讀入的字符串

              System.out.println("Server:"+is.readLine());
              
    //從Server讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上
              readline=sin.readLine();
    //從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串
            }
    //繼續(xù)循環(huán)
            os.close();
    //關(guān)閉Socket輸出流
            is.close();
    //關(guān)閉Socket輸入流
            socket.close();
    //關(guān)閉Socket
          }catch(Exception e) {
            System.out.println("Error"+e);
    //出錯(cuò),則打印出錯(cuò)信息
          }
      }
    }

     2. 服務(wù)器端程序
      import java.io.*;
      import java.net.*;
      import java.applet.Applet;
      public class TalkServer{
        public static void main(String args[]) {
          try{
            ServerSocket server=null;
            try{
              server=new ServerSocket(4700);
            
    //創(chuàng)建一個(gè)ServerSocket在端口4700監(jiān)聽(tīng)客戶(hù)請(qǐng)求
            }catch(Exception e) {
              System.out.println("can not listen to:"+e);
            
    //出錯(cuò),打印出錯(cuò)信息
            }

            Socket socket=null;
            try{
              socket=server.accept();
              
    //使用accept()阻塞等待客戶(hù)請(qǐng)求,有客戶(hù)
              //請(qǐng)求到來(lái)則產(chǎn)生一個(gè)Socket對(duì)象,并繼續(xù)執(zhí)行

            }catch(Exception e) {
              System.out.println("Error."+e);
              
    //出錯(cuò),打印出錯(cuò)信息
            }
            String line;
            BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             
    //由Socket對(duì)象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對(duì)象
            PrintWriter os=newPrintWriter(socket.getOutputStream());
             
    //由Socket對(duì)象得到輸出流,并構(gòu)造PrintWriter對(duì)象
            BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
             
    //由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造BufferedReader對(duì)象

            System.out.println("Client:"+is.readLine());
            
    //在標(biāo)準(zhǔn)輸出上打印從客戶(hù)端讀入的字符串
            line=sin.readLine();
            
    //從標(biāo)準(zhǔn)輸入讀入一字符串
            while(!line.equals("bye")){
            
    //如果該字符串為 "bye",則停止循環(huán)
              os.println(line);
              
    //向客戶(hù)端輸出該字符串
              os.flush();
              
    //刷新輸出流,使Client馬上收到該字符串
              System.out.println("Server:"+line);
              
    //在系統(tǒng)標(biāo)準(zhǔn)輸出上打印讀入的字符串
              System.out.println("Client:"+is.readLine());
              
    //從Client讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上
              line=sin.readLine();
              
    //從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串
            }  
    //繼續(xù)循環(huán)
            os.close();
    //關(guān)閉Socket輸出流
            is.close();
    //關(guān)閉Socket輸入流
            socket.close();
    //關(guān)閉Socket
            server.close();
    //關(guān)閉ServerSocket
          }catch(Exception e){
            System.out.println("Error:"+e);
            
    //出錯(cuò),打印出錯(cuò)信息
          }
        }
      }

    8.3.9 支持多客戶(hù)的client/server程序設(shè)計(jì)

    前面提供的Client/Server程序只能實(shí)現(xiàn)Server和一個(gè)客戶(hù)的對(duì)話。在實(shí)際應(yīng)用中,往往是在服務(wù)器上運(yùn)行一個(gè)永久的程序,它可以接收來(lái)自其他多個(gè)客戶(hù)端的請(qǐng)求,提供相應(yīng)的服務(wù)。為了實(shí)現(xiàn)在服務(wù)器方給多個(gè)客戶(hù)提供服務(wù)的功能,需要對(duì)上面的程序進(jìn)行改造,利用多線程實(shí)現(xiàn)多客戶(hù)機(jī)制。服務(wù)器總是在指定的端口上監(jiān)聽(tīng)是否有客戶(hù)請(qǐng)求,一旦監(jiān)聽(tīng)到客戶(hù)請(qǐng)求,服務(wù)器就會(huì)啟動(dòng)一個(gè)專(zhuān)門(mén)的服務(wù)線程來(lái)響應(yīng)該客戶(hù)的請(qǐng)求,而服務(wù)器本身在啟動(dòng)完線程之后馬上又進(jìn)入監(jiān)聽(tīng)狀態(tài),等待下一個(gè)客戶(hù)的到來(lái)。

    ServerSocket serverSocket=null;
        boolean listening=true;
        try{
          serverSocket=new ServerSocket(4700);
          //創(chuàng)建一個(gè)ServerSocket在端口4700監(jiān)聽(tīng)客戶(hù)請(qǐng)求
        }catch(IOException e) {  }
        while(listening){ //永遠(yuǎn)循環(huán)監(jiān)聽(tīng)
          new ServerThread(serverSocket.accept(),clientnum).start();
          //監(jiān)聽(tīng)到客戶(hù)請(qǐng)求,根據(jù)得到的Socket對(duì)象和
           客戶(hù)計(jì)數(shù)創(chuàng)建服務(wù)線程,并啟動(dòng)之
          clientnum++; //增加客戶(hù)計(jì)數(shù)
        }
        serverSocket.close(); //關(guān)閉ServerSocket

    設(shè)計(jì)ServerThread類(lèi)

     public class ServerThread extends Thread{
       Socket socket=null; //保存與本線程相關(guān)的Socket對(duì)象
       int clientnum; //保存本進(jìn)程的客戶(hù)計(jì)數(shù)
       public ServerThread(Socket socket,int num) { //構(gòu)造函數(shù)
        this.socket=socket; //初始化socket變量
        clientnum=num+1; //初始化clientnum變量
       }
       public void run() { //線程主體
        try{//在這里實(shí)現(xiàn)數(shù)據(jù)的接受和發(fā)送}

    8.3.10 據(jù)報(bào)Datagram通訊

    前面在介紹TCP/IP協(xié)議的時(shí)候,我們已經(jīng)提到,在TCP/IP協(xié)議的傳輸層除了TCP協(xié)議之外還有一個(gè)UDP協(xié)議,相比而言UDP的應(yīng)用不如TCP廣泛,幾個(gè)標(biāo)準(zhǔn)的應(yīng)用層協(xié)議HTTPFTP,SMTP…使用的都是TCP協(xié)議。但是,隨著計(jì)算機(jī)網(wǎng)絡(luò)的發(fā)展,UDP協(xié)議正越來(lái)越來(lái)顯示出其威力,尤其是在需要很強(qiáng)的實(shí)時(shí)交互性的場(chǎng)合,如網(wǎng)絡(luò)游戲,視頻會(huì)議等,UDP更是顯示出極強(qiáng)的威力,下面我們就介紹一下Java環(huán)境下如何實(shí)現(xiàn)UDP網(wǎng)絡(luò)傳輸。

    8.3.11 什么是Datagram

    所謂數(shù)據(jù)報(bào)(Datagram)就跟日常生活中的郵件系統(tǒng)一樣,是不能保證可靠的寄到的,而面向鏈接的TCP就好比電話,雙方能肯定對(duì)方接受到了信息。在本章前面,我們已經(jīng)對(duì)UDPTCP進(jìn)行了比較,在這里再稍作小節(jié):
      TCP,可靠,傳輸大小無(wú)限制,但是需要連接建立時(shí)間,差錯(cuò)控制開(kāi)銷(xiāo)大。
      UDP,不可靠,差錯(cuò)控制開(kāi)銷(xiāo)較小,傳輸大小限制在64K以下,不需要建立連接。

    8.3.12 Datagram通訊的表示方法:DatagramSocketDatagramPacket

    java.net中提供了兩個(gè)類(lèi)DatagramSocketDatagramPacket用來(lái)支持?jǐn)?shù)據(jù)報(bào)通信,DatagramSocket用于在程序之間建立傳送數(shù)據(jù)報(bào)的通信連接, DatagramPacket則用來(lái)表示一個(gè)數(shù)據(jù)報(bào)。先來(lái)看一下DatagramSocket的構(gòu)造方法:
       DatagramSocket();
       DatagramSocketint prot;
       DatagramSocket(int port, InetAddress laddr)
        其中,port指明socket所使用的端口號(hào),如果未指明端口號(hào),則把socket連接到本地主機(jī)上一個(gè)可用的端口。laddr指明一個(gè)可用的本地地址。給出端口號(hào)時(shí)要保證不發(fā)生端口沖突,否則會(huì)生成SocketException類(lèi)例外。注意:上述的兩個(gè)構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外SocketException,程序中必須進(jìn)行處理,或者捕獲、或者聲明拋棄。
      
    用數(shù)據(jù)報(bào)方式編寫(xiě)client/server程序時(shí),無(wú)論在客戶(hù)方還是服務(wù)方,首先都要建立一個(gè)DatagramSocket對(duì)象,用來(lái)接收或發(fā)送數(shù)據(jù)報(bào),然后使用DatagramPacket類(lèi)對(duì)象作為傳輸數(shù)據(jù)的載體。下面看一下DatagramPacket的構(gòu)造方法
       DatagramPacketbyte buf[],int length);
       DatagramPacket(byte buf[], int length, InetAddress addr, int port);
       DatagramPacket(byte[] buf, int offset, int length)
       DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port);

      其中,buf中存放數(shù)據(jù)報(bào)數(shù)據(jù),length為數(shù)據(jù)報(bào)中數(shù)據(jù)的長(zhǎng)度,addrport旨明目的地址,offset指明了數(shù)據(jù)報(bào)的位移量。
      在接收數(shù)據(jù)前,應(yīng)該采用上面的第一種方法生成一個(gè)DatagramPacket對(duì)象,給出接收數(shù)據(jù)的緩沖區(qū)及其長(zhǎng)度。然后調(diào)用DatagramSocket 的方法receive()等待數(shù)據(jù)報(bào)的到來(lái),receive()將一直等待,直到收到一個(gè)數(shù)據(jù)報(bào)為止。
      DatagramPacket packet=new DatagramPacket(buf, 256);
      Socket.receive (packet);
      發(fā)送數(shù)據(jù)前,也要先生成一個(gè)新的DatagramPacket對(duì)象,這時(shí)要使用上面的第二種構(gòu)造方法,在給出存放發(fā)送數(shù)據(jù)的緩沖區(qū)的同時(shí),還要給出完整的目的地址,包括IP地址和端口號(hào)。發(fā)送數(shù)據(jù)是通過(guò)DatagramSocket的方法send()實(shí)現(xiàn)的,send()根據(jù)數(shù)據(jù)報(bào)的目的地址來(lái)尋徑,以傳遞數(shù)據(jù)報(bào)。
      DatagramPacket packet=new DatagramPacket(buf, length, address, port);
      Socket.send(packet);
       
    在構(gòu)造數(shù)據(jù)報(bào)時(shí),要給出InetAddress類(lèi)參數(shù)。類(lèi)InetAddress在包java.net中定義,用來(lái)表示一個(gè)Internet地址,我們可以通過(guò)它提供的類(lèi)方法getByName()從一個(gè)表示主機(jī)名的字符串獲取該主機(jī)的IP地址,然后再獲取相應(yīng)的地址信息。

    8.3.14 用數(shù)據(jù)報(bào)進(jìn)行廣播通訊

    DatagramSocket只允許數(shù)據(jù)報(bào)發(fā)送一個(gè)目的地址,java.net包中提供了一個(gè)類(lèi)MulticastSocket,允許數(shù)據(jù)報(bào)以廣播方式發(fā)送到該端口的所有客戶(hù)。MulticastSocket用在客戶(hù)端,監(jiān)聽(tīng)服務(wù)器廣播來(lái)的數(shù)據(jù)。

    1. 客戶(hù)方程序:MulticastClient.java
      import java.io.*;
      import java.net.*;
      import java.util.*;
      public class MulticastClient {
        public static void main(String args[]) throws IOException
        {
         MulticastSocket socket=new MulticastSocket(4446);
         
    //創(chuàng)建4446端口的廣播套接字
         InetAddress address=InetAddress.getByName("230.0.0.1");
         
    //得到230.0.0.1的地址信息
         socket.joinGroup(address);
         
    //使用joinGroup()將廣播套接字綁定到地址上
         DatagramPacket packet;

         for(int i=0;i<5;i++) {
           byte[] buf=new byte[256];
           
    //創(chuàng)建緩沖區(qū)

           packet=new DatagramPacket(buf,buf.length);
           
    //創(chuàng)建接收數(shù)據(jù)報(bào)
           socket.receive(packet);
    //接收
           String received=new String(packet.getData());
           
    //由接收到的數(shù)據(jù)報(bào)得到字節(jié)數(shù)組,
           
    //并由此構(gòu)造一個(gè)String對(duì)象
           System.out.println("Quote of theMoment:"+received);
           
    //打印得到的字符串
         }
    //循環(huán)5次
         socket.leaveGroup(address);
         
    //把廣播套接字從地址上解除綁定
         socket.close();
    //關(guān)閉廣播套接字
       }
     }

     2. 服務(wù)器方程序:MulticastServer.java
      public class MulticastServer{
        public static void main(String args[]) throws java.io.IOException
        {
          new MulticastServerThread().start();
          
    //啟動(dòng)一個(gè)服務(wù)器線程
        }
      }

     3. 程序MulticastServerThread.java
      import java.io.*;
      import java.net.*;
      import java.util.*;
      public class MulticastServerThread extends QuoteServerThread
      
    //從QuoteServerThread繼承得到新的服務(wù)器線程類(lèi)MulticastServerThread
      {
        Private long FIVE_SECOND=5000;
    //定義常量,5秒鐘
        public MulticastServerThread(String name) throws IOException
        {
          super("MulticastServerThread");
          
    //調(diào)用父類(lèi),也就是QuoteServerThread的構(gòu)造函數(shù)
        }

        public void run() //重寫(xiě)父類(lèi)的線程主體
        {
         while(moreQuotes) {
         
    //根據(jù)標(biāo)志變量判斷是否繼續(xù)循環(huán)
          try{
            byte[] buf=new byte[256];
            
    //創(chuàng)建緩沖區(qū)
            String dString=null;
            if(in==null) dString=new Date().toString();
            
    //如果初始化的時(shí)候打開(kāi)文件失敗了,
            //則使用日期作為要傳送的字符串

            else dString=getNextQuote();
            
    //否則調(diào)用成員函數(shù)從文件中讀出字符串
            buf=dString.getByte();
            
    //把String轉(zhuǎn)換成字節(jié)數(shù)組,以便傳送send it
            InetAddress group=InetAddress.getByName("230.0.0.1");
            
    //得到230.0.0.1的地址信息
            DatagramPacket packet=new DatagramPacket(buf,buf.length,group,4446);
            
    //根據(jù)緩沖區(qū),廣播地址,和端口號(hào)創(chuàng)建DatagramPacket對(duì)象
            socket.send(packet);
    //發(fā)送該P(yáng)acket
            try{
              sleep((long)(Math.random()*FIVE_SECONDS));
              
    //隨機(jī)等待一段時(shí)間,0~5秒之間
            }catch(InterruptedException e) { }
    //異常處理
          }catch(IOException e){
    //異常處理
            e.printStackTrace( );
    //打印錯(cuò)誤棧

            moreQuotes=false; //置結(jié)束循環(huán)標(biāo)志
          }
        }
        socket.close( );
    //關(guān)閉廣播套接口
       }
     }

    【本講小結(jié)】

    本講主要講解了Java環(huán)境下的網(wǎng)絡(luò)編程。因?yàn)?/span>TCP/IP協(xié)議是Java網(wǎng)絡(luò)編程的基礎(chǔ)知識(shí),本講開(kāi)篇重點(diǎn)介紹了TCP/IP協(xié)議中的一些概念,TCP/IP協(xié)議本身是一個(gè)十分龐大的系統(tǒng),用幾個(gè)小節(jié)是不可能講清楚的。所以我們只是聯(lián)系實(shí)際,講解了一些最基本的概念,幫助學(xué)生理解后面的相關(guān)內(nèi)容。重點(diǎn)有一下幾個(gè)概念:主機(jī)名,IP,端口,服務(wù)類(lèi)型,TCP,UDP。
      后續(xù)的內(nèi)容分為兩大塊,一塊是以URL為主線,講解如何通過(guò)URL類(lèi)和URLConnection類(lèi)訪問(wèn)WWW網(wǎng)絡(luò)資源,由于使用URL十分方便直觀,盡管功能不是很強(qiáng),還是值得推薦的一種網(wǎng)絡(luò)編程方法,尤其是對(duì)于初學(xué)者特別容易接受。本質(zhì)上講,URL網(wǎng)絡(luò)編程在傳輸層使用的還是TCP協(xié)議。
      另一塊是以Socket接口和C/S網(wǎng)絡(luò)編程模型為主線,依次講解了如何用Java實(shí)現(xiàn)基于TCPC/S結(jié)構(gòu),主要用到的類(lèi)有Socket,ServerSocket。以及如何用Java實(shí)現(xiàn)基于UDPC/S結(jié)構(gòu),還討論了一種特殊的傳輸方式,廣播方式,這種方式是UDP所特有的,主要用到的類(lèi)有DatagramSocket , DatagramPacket, MulticastSocket。這一塊在Java網(wǎng)絡(luò)編程中相對(duì)而言是最難的(盡管Java在網(wǎng)絡(luò)編程這方面已經(jīng)做的夠"傻瓜"了,但是網(wǎng)絡(luò)編程在其他環(huán)境下的卻是一件極為頭痛的事情,再"傻瓜"還是有一定的難度),也是功能最為強(qiáng)大的一部分,讀者應(yīng)該好好研究,領(lǐng)悟其中的思想。
      最后要強(qiáng)調(diào)的是要學(xué)好Java網(wǎng)絡(luò)編程,Java語(yǔ)言,最重要的還是在于多多練習(xí)!

    posted @ 2007-04-20 17:01 chenweicai 閱讀(334) | 評(píng)論 (0)編輯 收藏

    java中的事件機(jī)制

    Java做的圖形界面軟件通過(guò)事件響應(yīng)機(jī)制實(shí)現(xiàn)用戶(hù)與程序的交互,原理大概是這樣:

     

    首先,在java控件對(duì)象(比如文本框)上添加一個(gè)監(jiān)控對(duì)象,方法是one.addXXXListenner(two),這就相當(dāng)于你要對(duì)某人進(jìn)行監(jiān)聽(tīng),先要在他身上綁定一個(gè)竊聽(tīng)器一樣,這里“one”就是你要監(jiān)聽(tīng)的那個(gè)家伙,two就是你自己造的一個(gè)竊聽(tīng)器。

     

    第二步就是要考慮怎樣造這個(gè)竊聽(tīng)器了,我們首先要搞清楚它要實(shí)現(xiàn)的功能:它不僅能夠監(jiān)聽(tīng)one的一舉一動(dòng),還能夠把監(jiān)聽(tīng)到的事件告訴系統(tǒng),并讓系統(tǒng)對(duì)這個(gè)事件做出相應(yīng)的處理。Java中是通過(guò)接口實(shí)現(xiàn)這樣的功能的。這些接口請(qǐng)參見(jiàn)jdk中java.awt.event包,里面那幾個(gè)XXXListener就是(不是很多,常用的更少)。在這些接口中定義了一些方法,這些方法就是相應(yīng)的事件處理方式,它們只由系統(tǒng)調(diào)用并且都有一個(gè)事件類(lèi)的對(duì)象作為參數(shù),這個(gè)參數(shù)正是聯(lián)系發(fā)生事件主體one和操作系統(tǒng)的紐帶。當(dāng)然接口是虛的,不能產(chǎn)生對(duì)象的;所以想必你也猜到,上面的“竊聽(tīng)器”two的類(lèi)型肯定是某個(gè)實(shí)現(xiàn)了XXXListener接口的類(lèi)。

    好了,現(xiàn)在在回顧一下這個(gè)過(guò)程:

    (1)用戶(hù)通過(guò)鼠標(biāo)、鍵盤(pán)等在one對(duì)象上做動(dòng)作(比如點(diǎn)擊鼠標(biāo)),

    (2)這個(gè)動(dòng)作被two監(jiān)聽(tīng)到并產(chǎn)生事件對(duì)象e(即XXXEvent的對(duì)象),

    (3)two通過(guò)事件e對(duì)象向系統(tǒng)打報(bào)告,

    (4)系統(tǒng)調(diào)用two中實(shí)現(xiàn)的方法對(duì)事件進(jìn)行處理,并向方法傳送事件e。

     

     

    如果你清楚了這個(gè)過(guò)程,再來(lái)細(xì)看一下這個(gè)XXXListener接口。我們必須用類(lèi)來(lái)實(shí)現(xiàn)接口的方法(這是java基礎(chǔ)知識(shí)吧◎),再用這個(gè)類(lèi)產(chǎn)生two這樣的對(duì)象。類(lèi)實(shí)現(xiàn)接口必須實(shí)現(xiàn)接口中的每個(gè)方法,而實(shí)際上我們需要的也許只是該接口中的某一個(gè)方法(比如處理鼠標(biāo)右鍵點(diǎn)擊的那個(gè)),那么每個(gè)方法還得去實(shí)現(xiàn)一個(gè)空的,真是浪費(fèi)。Sun為了讓程序員省點(diǎn)事,在JDK中已經(jīng)為我們把這些接口實(shí)現(xiàn)好了,這些類(lèi)就是我們所謂的“適配器”(XXXAdapter),我們只需要繼承(extends)這些類(lèi)并重寫(xiě)里面我們需要的方法就OK了,所以,其實(shí)適配器就是類(lèi),只不過(guò)這些類(lèi)中只有一些空的方法,別無(wú)所有。

    到此,你大概明白程序員在事件處理過(guò)程中該做哪些事了吧:

    (1)制造“竊聽(tīng)器”,即:實(shí)現(xiàn)事件監(jiān)聽(tīng)接口,重寫(xiě)相關(guān)方法,

    (2)安裝竊聽(tīng)器,即:為監(jiān)控對(duì)象添加監(jiān)聽(tīng)器。

     

    下面,我們通過(guò)程序再來(lái)仔細(xì)看看事件響應(yīng)的實(shí)現(xiàn)方式(待續(xù)):

    (以下內(nèi)容參考陳剛《eclipse從入門(mén)到精通》)

    1)、 匿名內(nèi)部類(lèi)寫(xiě)法

    例程:

    text.addMouseListener(new MouseAdapter() {

        public void mouseDoubleClick(MouseEvent e) {//鼠標(biāo)雙擊事件的方法

             //打開(kāi)一個(gè)信息框

            MessageDialog.openInformation (null,"","Hello World");

        }

    });

    new MouseAdapter()就是一個(gè)匿名內(nèi)部類(lèi)。其實(shí)就是在實(shí)現(xiàn)類(lèi)的同時(shí)用new構(gòu)造一個(gè)該類(lèi)的對(duì)象,并把它作為addMouseListener的參數(shù),它的效果和下面的一樣,只不過(guò)代碼比較集中。

     

    (2)、命名內(nèi)部類(lèi)寫(xiě)法

    public class HelloWorld {

        public static void main(String[] args) {

               ……

            Text text = new Text(shell, SWT.BORDER);

    //加入鼠標(biāo)事件監(jiān)聽(tīng)器,并用下面代碼所定義的內(nèi)部類(lèi)生成一個(gè)對(duì)象

            text.addMouseListener(new MyMouseDoubleClick());

            ……

        }

     

    //定義一個(gè)名為MyMouseDoubleClick的內(nèi)部類(lèi)

        private static final class MyMouseDoubleClick extends MouseAdapter {

            public void mouseDoubleClick(MouseEvent e) {

                MessageDialog.openInformation(null, "", "Hello World");

            }

        }

    }

     

    (3)、外部類(lèi)寫(xiě)法

    這種寫(xiě)法和命名內(nèi)部類(lèi)有些相似,只不過(guò)是將MyMouseDoubleClick類(lèi)從HelloWorld.java中拿出去,單獨(dú)寫(xiě)成一個(gè)類(lèi)文件。實(shí)現(xiàn)代碼如下

    //文件1: HelloWorld.java

    public class HelloWorld {

        public static void main(String[] args) {

                  ……

            Text text = new Text(shell, SWT.BORDER);

    //加入鼠標(biāo)事件監(jiān)聽(tīng)器,并用下面代碼所定義的內(nèi)部類(lèi)生成一個(gè)對(duì)象

            text.addMouseListener(new MyMouseDoubleClick());

            ……

        }

    }

     

    //文件2:MyMouseDoubleClick.java

    public class MyMouseDoubleClick extends MouseAdapter {

        public void mouseDoubleClick(MouseEvent e) {

            MessageDialog.openInformation(null, "", "Hello World");

        }

    }

    (4)、實(shí)現(xiàn)監(jiān)聽(tīng)接口的寫(xiě)法

    將HelloWorld類(lèi)實(shí)現(xiàn)MouseListener接口,這樣類(lèi)本身就成了一個(gè)監(jiān)聽(tīng)器,使得加入監(jiān)聽(tīng)器的代碼可以更簡(jiǎn)潔。這種方式適合加入監(jiān)聽(tīng)器的組件較多,且要求監(jiān)聽(tīng)器的事件處理代碼可以被組件共用。這種方式還有一個(gè)要注意的地方:事件方法和其他方法混合在了一起,容易引起誤讀,所以應(yīng)該在事件方法前加入詳細(xì)的注釋說(shuō)明。

    實(shí)現(xiàn)MouseListener接口要寫(xiě)的事件方法多一些,當(dāng)然沒(méi)用的事件方法可以空實(shí)現(xiàn)。如果繼承MouseListener接口的適配器MouseAdapter,則只寫(xiě)需要的方法就行了。另外要注意:只有接口才能有多繼承的特性,所以如果HelloWorld已經(jīng)是某個(gè)類(lèi)的子類(lèi),就只能用實(shí)現(xiàn)接口的方式,而不能繼承接口的適配器了。

    給出示例代碼如下:

    public class HelloWorld extends MouseAdapter{//或implements MouseListener

        public static void main(String[] args) {

                ……

            Text text1 = new Text(shell, SWT.BORDER);

            Text text2 = new Text(shell, SWT.BORDER);

            text1.addMouseListener(this);

            text2.addMouseListener(this);

            ……

        }

     

        public void mouseDoubleClick(MouseEvent e) {

            MessageDialog.openInformation(null, "", "Hello World");

        }

    }

    posted @ 2007-04-13 09:37 chenweicai 閱讀(353) | 評(píng)論 (0)編輯 收藏

    java中的事件機(jī)制的參與者有3種角色:

    1.event object:就是事件產(chǎn)生時(shí)具體的“事件”,用于listener的相應(yīng)的方法之中,作為參數(shù),一般存在與listerner的方法之中

    2.event source:具體的接受事件的實(shí)體,比如說(shuō),你點(diǎn)擊一個(gè)button,那么button就是event source,這樣你必須使button對(duì)某些事件進(jìn)行相應(yīng),你就需要注冊(cè)特定的listener,比如說(shuō)MouseEvent之中的MouseClicked方法,這是他就必須有了add方法

    3.event listener:具體的對(duì)監(jiān)聽(tīng)的事件類(lèi),當(dāng)有其對(duì)應(yīng)的event object產(chǎn)生的時(shí)候,它就調(diào)用相應(yīng)的方法,進(jìn)行處理。在windows程序設(shè)計(jì)里邊這種相應(yīng)使用callback機(jī)制來(lái)實(shí)現(xiàn)的

    先看看jdk提供的event包:
    public interface EventListener:所有事件偵聽(tīng)器接口必須擴(kuò)展的標(biāo)記接口。
    public class EventObject extends Object implements Serializable

    所有事件狀態(tài)對(duì)象都將從其派生的根類(lèi)。 所有 Event 在構(gòu)造時(shí)都引用了對(duì)象 "source",在邏輯上認(rèn)為該對(duì)象是最初發(fā)生有關(guān) Event 的對(duì)象。

            在Java2處理事件時(shí),沒(méi)有采用dispatchEvent()-postEvent()-handleEvent()方式,采用了監(jiān)聽(tīng)器類(lèi),每個(gè)事件類(lèi)都有相關(guān)聯(lián)的監(jiān)聽(tīng)器接口。事件從事件源到監(jiān)聽(tīng)者的傳遞是通過(guò)對(duì)目標(biāo)監(jiān)聽(tīng)者對(duì)象的Java方法調(diào)用進(jìn)行的。

      對(duì)每個(gè)明確的事件的發(fā)生,都相應(yīng)地定義一個(gè)明確的Java方法。這些方法都集中定義在事件監(jiān)聽(tīng)者(EventListener)接口中,這個(gè)接口要繼承 java.util.EventListener。 實(shí)現(xiàn)了事件監(jiān)聽(tīng)者接口中一些或全部方法的類(lèi)就是事件監(jiān)聽(tīng)者。

      伴隨著事件的發(fā)生,相應(yīng)的狀態(tài)通常都封裝在事件狀態(tài)對(duì)象中,該對(duì)象必須繼承自java.util.EventObject。事件狀態(tài)對(duì)象作為單參傳遞給應(yīng)響應(yīng)該事件的監(jiān)聽(tīng)者方法中。發(fā)出某種特定事件的事件源的標(biāo)識(shí)是:遵從規(guī)定的設(shè)計(jì)格式為事件監(jiān)聽(tīng)者定義注冊(cè)方法,并接受對(duì)指定事件監(jiān)聽(tīng)者接口實(shí)例的引用。

    開(kāi)始之前首先問(wèn)個(gè)問(wèn)題:您熟悉java.util.EventObject 和java.util.EventListener兩個(gè)類(lèi)以及他們已有的子類(lèi)嗎?

    如果你已經(jīng)能夠熟練使用jdk為我們提供的事件監(jiān)聽(tīng)器,并且很熟悉MouseEvent, KeyEvent, WindowEvent等等這些jdk為我們準(zhǔn)備好的事件,那么想必你對(duì)java的事件機(jī)制已經(jīng)有所理解。但是也許你還是覺(jué)得雖然用起來(lái)沒(méi)什么問(wèn)題,但是原理還是有些糊涂,那么下面我們?cè)龠M(jìn)一步自己實(shí)現(xiàn)這些事件和監(jiān)聽(tīng)器,我們把這個(gè)取名為自定義事件。

    其實(shí)自定義事件在java中很有用處,我們有的時(shí)候想讓自己的程序產(chǎn)生一個(gè)事件,但有不希望(或者不可能)用鼠標(biāo),鍵盤(pán)之類(lèi)的輸入設(shè)備進(jìn)行操作,比如你寫(xiě)一個(gè)應(yīng)用程序,在這個(gè)程序中一旦收到郵件就對(duì)郵件進(jìn)行相關(guān)處理,對(duì)于“收到郵件”這個(gè)事件,jdk中就沒(méi)有定義。對(duì)于這樣的事件,以及對(duì)于這樣的事件的監(jiān)聽(tīng)器,我們只能自己動(dòng)手完成了。

    那么下面就以實(shí)例開(kāi)始我們這個(gè)“創(chuàng)新”的過(guò)程:首先,我們要明確jdk中需要的資源:類(lèi)EventObject作為父類(lèi)用來(lái)生成我們自己的事件類(lèi),接口EventListener用來(lái)實(shí)現(xiàn)我們自己的監(jiān)聽(tīng)器;剩下的事情就是如何注冊(cè)這些事件以及測(cè)試他們了。

    (1)       通過(guò)DemoEvent.java文件創(chuàng)建DemoEvent類(lèi),這個(gè)類(lèi)繼承EventObject。這個(gè)類(lèi)的構(gòu)造函數(shù)的參數(shù)傳遞了產(chǎn)生這個(gè)事件的事件源(比如各種控件),方法getSource用來(lái)獲得這個(gè)事件源的引用。

    DemoEvent.java

    package demo.listener;

     

    import java.util.EventObject;

     

    public class DemoEvent extends EventObject

    {

            Object obj;

            public DemoEvent(Object source)

            {

                   super(source);

                   obj = source;

            }

            public Object getSource()

            {

                   return obj;

            }

            public void say()

            {

                   System.out.println("This is say method...");

            }

    }

     

    (2)       定義新的事件監(jiān)聽(tīng)接口,該接口繼承自EventListener;該接口包含對(duì)DemeEvent事件的處理程序:

    DemoListener.java

    package demo.listener;

     

    import java.util.EventListener;

     

    public interface DemoListener extends EventListener

    {

           public void demoEvent(DemoEvent dm);

    }

     

    通過(guò)上面的接口我們?cè)俣x事件監(jiān)聽(tīng)類(lèi),這些類(lèi)具體實(shí)現(xiàn)了監(jiān)聽(tīng)功能和事件處理功能?;叵胍幌律衔闹心撬姆N實(shí)現(xiàn)方式,我們這里不正是使用了其中的第三種——外部類(lèi)寫(xiě)法的方式嗎?

    Listener1.java

    package demo.listener;

     

    public class Listener1 implements DemoListener

    {

           public void demoEvent(DemoEvent de)

           {

                  System.out.println("Inside listener1...");

           }

    }



    Listener2.java

    package demo.listener;

     

    public class Listener2 implements DemoListener

    {

           public void demoEvent(DemoEvent de)

           {

                  System.out.println("Inside listener2...");

           }

    }


    Listener3.java

    package demo.listener;

     

    public class Listener3 implements DemoListener

    {

           public void demoEvent(DemoEvent de)

           {

                  System.out.println("Inside listener3...");

           }

    }

     

    (3)       通過(guò)DemeSource..ava文件創(chuàng)造一個(gè)事件源類(lèi),它用一個(gè)java.utile.Vector對(duì)象來(lái)存儲(chǔ)所有的事件監(jiān)聽(tīng)器對(duì)象,存儲(chǔ)方式是通過(guò)addListener(..)這樣的方法。notifyDemeEvent(..)是觸發(fā)事件的方法,用來(lái)通知系統(tǒng):事件發(fā)生了,你調(diào)用相應(yīng)的處理函數(shù)(回調(diào)函數(shù))吧。

    DemoSource.java

     

    package demo.listener;

    import java.util.*;

     

    public class DemoSource

    {

           private Vector repository = new Vector();

           DemoListener dl;

           public DemoSource()

           {

     

           }

           public void addDemoListener(DemoListener dl)

           {

                  repository.addElement(dl);

           }

           public void notifyDemoEvent()

           {

                  Enumeration enum = repository.elements();

                  while(enum.hasMoreElements())

                  {

                        dl = (DemoListener)enum.nextElement();

                        dl.demoEvent(new DemoEvent(this));

                  }

           }

    }

     

     

                 

    (4)       好了,最后寫(xiě)一個(gè)測(cè)試程序測(cè)試一下我們自定義的事件吧,這段程序應(yīng)該不難理解吧:)

    TestDemo.java

     

    package demo.listener;

     

    public class TestDemo

    {

           DemoSource ds;

     

           public TestDemo()

           {

                  try{

                        ds = new DemoSource();

                        Listener1 l1 = new Listener1();

                        Listener2 l2 = new Listener2();

                        Listener3 l3 = new Listener3();

     

                        ds.addDemoListener(l1);

                        ds.addDemoListener(l2);

                        ds.addDemoListener(l3);

                        ds.addDemoListener(new DemoListener(){
                                   public void demoEvent(DemoEvent event){
                                             System.out.println("Method come from 匿名類(lèi)...");
                                   }
                           });

                        ds.notifyDemoEvent();

     

                  }catch(Exception ex)

                  {ex.printStackTrace();}

           }

     

           public static void main(String args[])

           {

                  new TestDemo();

           }

    }


    posted @ 2007-04-13 09:22 chenweicai 閱讀(10338) | 評(píng)論 (14)編輯 收藏

    1.
    import java.net.InetAddress;
    import java.net.UnknownHostException;

    public class NetWork {

     //static fields and static block
     
        //InetAddress of the local host
     private static InetAddress local = null;
     
     //integer version of the local host
     private static int packedLocal = 0;
     
     static {
      try{
       local = InetAddress.getLocalHost();
      }catch(UnknownHostException e){
       e.printStackTrace();
      }

      System.out.println("localhost : " + local);
      packedLocal = translate(local);
     }
     
     public static InetAddress getLocalHost(){
      return local;
     }
     
     public static String getLocalHostName(){
      return local.getHostName();
     }
     
     public static String getLocalMachineName(){
      String hostname = local.getHostName();
      int machEndIndex = hostname.indexOf('.');
      if(machEndIndex == -1)
       return hostname;
      else
       return hostname.substring(0, machEndIndex);
     }
     
     public static boolean isLocal(InetAddress address){
      return local.equals(address);
     }
     
     public static boolean isLocal(int packedAddress){
      return packedAddress == packedLocal;
     }
     
     /**
      * return an integer representation of the specified
      * InetAddress. This is used for efficiency
      */
     public static int translate(InetAddress address){
      byte[] ad = address.getAddress();//原始IP地址
      if(ad.length != 4)
       throw new IllegalArgumentException("only accept 32-byte IP address");
      int packedAddress = ((ad[0] << 24) & 0xFF000000) |
           ((ad[1] << 16) & 0xFF0000) |
           ((ad[2] <<  8) & 0xFF00) |
           (ad[3] & 0xFF);
      return packedAddress;
     }
     
     /**
      * return an InetAddress representation of the specified integer.
      * This is used for performance improvements in serivalization.
      */
     public static InetAddress translate (int packed)
      throws UnknownHostException{
      int[] ad = new int[4];
         ad[0] = (packed  >>> 24);
         ad[1] = ((packed >>> 16) & 0x000000FF);
         ad[2] = ((packed >>>  8) & 0x000000FF);
         ad[3] = (packed          & 0x000000FF);
         StringBuffer buf = new StringBuffer();
         buf.append(ad[0]);
         buf.append(".");
         buf.append(ad[1]);
         buf.append(".");
         buf.append(ad[2]);
         buf.append(".");
         buf.append(ad[3]);
        
         return InetAddress.getByName(buf.toString());
     }
     
     public static String translateToString(int packed){
      int[] ad = new int[4];
      ad[0] = (packed >>> 24);
      ad[1] = ((packed >>> 16) & 0x000000FF);
      ad[2] = ((packed >>> 8) & 0x000000FF);
      ad[3] = (packed & 0x000000FF);
      
      StringBuffer buf = new StringBuffer();
      buf.append(ad[0]);
      buf.append(".");
      buf.append(ad[1]);
      buf.append(".");
      buf.append(ad[2]);
      buf.append(".");
      buf.append(ad[3]);
      
      return buf.toString();
     }
     
     public static void main(String[] args){
      InetAddress localhost = getLocalHost();
      System.out.println("Local Host Name is : " + getLocalHostName());
      System.out.println("Local Host Machine Name is : " + getLocalMachineName());
      if(isLocal(localhost))
       System.out.println(localhost.getHostName() + " is Local Host.");
      int intforaddress = translate(localhost);
      System.out.println("localhost integer representation is " + intforaddress);
      System.out.println("localhost string representation is " + translateToString(intforaddress));
      System.out.println(translateToString(intforaddress).toString());
      
     }
    }



    2. public interfece Externalizable extendx Serializable
    Externalizable 實(shí)例類(lèi)的惟一特性是可以被寫(xiě)入序列化流中,該類(lèi)負(fù)責(zé)保存和恢復(fù)實(shí)例內(nèi)容。 若某個(gè)要完全控制某一對(duì)象及其超類(lèi)型的流格式和內(nèi)容,則它要實(shí)現(xiàn) Externalizable 接口的 writeExternal 和 readExternal 方法。這些方法必須顯式與超類(lèi)型進(jìn)行協(xié)調(diào)以保存其狀態(tài)。這些方法將代替自定義的 writeObject 和 readObject 方法實(shí)現(xiàn)。
    Serialization 對(duì)象將使用 Serializable 和 Externalizable 接口。對(duì)象持久性機(jī)制也可以使用它們。要存儲(chǔ)的每個(gè)對(duì)象都需要檢測(cè)是否支持 Externalizable 接口。如果對(duì)象支持 Externalizable,則調(diào)用 writeExternal 方法。如果對(duì)象不支持 Externalizable 但實(shí)現(xiàn)了 Serializable,則使用 ObjectOutputStream 保存該對(duì)象。
    在重構(gòu) Externalizable 對(duì)象時(shí),先使用無(wú)參數(shù)的公共構(gòu)造方法創(chuàng)建一個(gè)實(shí)例,然后調(diào)用 readExternal 方法。通過(guò)從 ObjectInputStream 中讀取 Serializable 對(duì)象可以恢復(fù)這些對(duì)象。
    Externalizable 實(shí)例可以通過(guò) Serializable 接口中記錄的 writeReplace 和 readResolve 方法來(lái)指派一個(gè)替代對(duì)象。

    writeExternal

    void writeExternal(ObjectOutput out)
    throws IOException
    該對(duì)象可實(shí)現(xiàn) writeExternal 方法來(lái)保存其內(nèi)容,它可以通過(guò)調(diào)用 DataOutput 的方法來(lái)保存其基本值,或調(diào)用 ObjectOutput 的 writeObject 方法來(lái)保存對(duì)象、字符串和數(shù)組。

     

    參數(shù):
    out - 要寫(xiě)入對(duì)象的流
    拋出:
    IOException - 包含可能發(fā)生的所有 I/O 異常

    readExternal

    void readExternal(ObjectInput in)
    throws IOException,
    ClassNotFoundException
    對(duì)象實(shí)現(xiàn) readExternal 方法來(lái)恢復(fù)其內(nèi)容,它通過(guò)調(diào)用 DataInput 的方法來(lái)恢復(fù)其基礎(chǔ)類(lèi)型,調(diào)用 readObject 來(lái)恢復(fù)對(duì)象、字符串和數(shù)組。readExternal 方法必須按照與 writeExternal 方法寫(xiě)入值時(shí)使用的相同順序和類(lèi)型來(lái)讀取這些值。

     

    參數(shù):
    in - 為了恢復(fù)對(duì)象而從中讀取數(shù)據(jù)的流
    拋出:
    IOException - 如果發(fā)生 I/O 錯(cuò)誤
    ClassNotFoundException - 如果無(wú)法找到需要恢復(fù)的某個(gè)對(duì)象的類(lèi)。

    import java.io.Externalizable;
    import java.net.InetAddress;

    public interface EndPoint extends Externalizable {

     /**
      *
      * @return Returns the IP address contained in this endpoint.
      */
     public InetAddress getAddress();
     
     /**
      * returns the IP address contained in this endpoint.
      * enclosed in an integer value.
      */
     public int getIntAddress();
     
     /**
      * Returns the port number contained in this endpoint.
      */
     public int getPort();
     
     
     /**
      * Return true if this endpoint is the local endpoint.
      */
     public boolean isLocal();
     
     /**
      * Returns true if this endpoint is a multicast endpoint.
      */
     public boolean isMulticastEndPoint();
    }



    import internet.network.NetWork;

    import java.io.Externalizable;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInput;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutput;
    import java.io.ObjectOutputStream;
    import java.net.InetAddress;
    import java.net.UnknownHostException;

    public class EndPointImpl implements EndPoint, Comparable, Externalizable{

     //Constants
     
     /* Size of this object (in bytes) when marshalled. */
     public static final int SIZE = 6;
     
     //Fields
     protected InetAddress address;
     
     private int packedAddress;
     
     private int port;
     
     protected boolean local;
     
     //Constructors
     
     /**
      * Default constructor for externalization
      */
     public EndPointImpl(){
      
     }
     
     public EndPointImpl(InetAddress address, int port){
      this.address = address;
      this.port = port;
      packedAddress = NetWork.translate(address);
      local = NetWork.isLocal(packedAddress);
     }
     
     public EndPointImpl(int packedAddress, int port){
      this.packedAddress = packedAddress;
      this.port = port;
      this.local = NetWork.isLocal(packedAddress);
      //The InetAddress version of the address is computed
      //only when needed, to avoid uselesss DNS lookups.
      address = null;
     }
     
     public InetAddress getAddress() {
      if(address == null)
       try{
        address = NetWork.translate(packedAddress);
       }catch(UnknownHostException e){
        System.out.println(e.getMessage());
       }
       return address;
     }

     public int getIntAddress() {
      return this.packedAddress;
     }

     public int getPort() {
      return this.port;
     }

     public boolean isLocal() {
      return this.local;
     }

     public boolean isMulticastEndPoint() {
      return address.isMulticastAddress();
     }
     
     
     ////////////////////////////////////////////////////////////
     // Methods from Externalizable
     ////////////////////////////////////////////////////////////

     /**
      * Restores the content of this object form the marshaled data contained
      * in the specified input stream.
      *
      * @param in the stream to be read.
      */
     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
      
      packedAddress = in.readInt();
      port = (int) in.readUnsignedShort();
      local = NetWork.isLocal(packedAddress);
      address = null;
     }

     /**
      * Marshals the content of this object to the specified output stream.
      *
      * @param out the stream to be written
      */
     public void writeExternal(ObjectOutput out) throws IOException {
      
      out.writeInt(packedAddress);
      out.writeShort(port);
     }

     
     ////////////////////////////////////////////////////////////
     // Methods from Object and Comparable
     ////////////////////////////////////////////////////////////
     /**
      *  Compares this object with the specified object for order.
      */
     public int compareTo(Object obj) {
      if(this == obj){
       return 0;
      }
      else{
       EndPointImpl endpoint = (EndPointImpl) obj;
       if(packedAddress < endpoint.packedAddress){
        return -1;
       }
       else if(packedAddress == endpoint.packedAddress){
        if(port < endpoint.port)
         return -1;
        else if(port == endpoint.port)
         return 0;
        else
         return 1;
       }
       else{
        return 1;
       }
      }
     }
     
     public int hashcode(){
      return packedAddress ^ port;
     }

     /**
      * Compares two EndPointImpl objects for content equality.
      */
     public boolean equals (Object obj){
      if(!(obj instanceof EndPointImpl))
       return false;
      EndPointImpl endpoint = (EndPointImpl) obj;
      return (endpoint.packedAddress == packedAddress && endpoint.port == port);
     }
     
     /**
      * Returns a string representation of this object.
      */
     public String toString(){
      StringBuffer buf = new StringBuffer();
      buf.append("[");
      buf.append(NetWork.translateToString(packedAddress));
      buf.append(":");
      buf.append(port);
      buf.append("]");
      
      return buf.toString();
     }
     
     
     public static void main(String[] args) throws FileNotFoundException, IOException{
      InetAddress localhost = NetWork.getLocalHost();
      EndPointImpl endpoint = new EndPointImpl(localhost, 100);
      System.out.println("This EndPoint is : " + endpoint.toString());
      EndPointImpl endpoint2 = new EndPointImpl();
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("endpoint.txt"));
      try {
       endpoint.writeExternal(oos);
      } catch (IOException e) {
       e.printStackTrace();
      }
      oos.close();
      
      ObjectInputStream ios = new ObjectInputStream(new FileInputStream("endpoint.txt"));
      try {
       endpoint2.readExternal(ios);
      } catch (IOException e) {
       e.printStackTrace();
      } catch (ClassNotFoundException e) {
       e.printStackTrace();
      }
      ios.close();
      System.out.println("This EndPoint is : " + endpoint2.toString());
     }
    }

    posted @ 2007-04-11 21:41 chenweicai 閱讀(164) | 評(píng)論 (0)編輯 收藏

    類(lèi)Deflater 和 類(lèi)Inflater:此類(lèi)使用流行的 ZLIB 壓縮程序庫(kù)為通用壓縮提供支持。ZLIB 壓縮程序庫(kù)最初是作為 PNG 圖形標(biāo)準(zhǔn)的一部分開(kāi)發(fā)的,不受專(zhuān)利的保護(hù)。

      //encoding a string into bytes
      String inputString = "chenweicai";
      byte[] input = inputString.getBytes("UTF-8");
      System.out.println("before compressed, the bytes length is :" + input.length);
      
      //compress the bytes
      byte[] output = new byte[100];
      Deflater compresser = new Deflater();
      compresser.setInput(input);
      compresser.finish();
      int compressedDataLength = compresser.deflate(output);
      System.out.println("compressed bytes length is :" + compressedDataLength);
      
      //decompress the bytes
      Inflater decompresser = new Inflater();
      decompresser.setInput(output, 0, compressedDataLength);
      byte[] result = new byte[100];
      int resultLength = decompresser.inflate(result);
      decompresser.end();
      System.out.println("after decompressed, the bytes length is :" + resultLength);
      
      //decode the bytes into a string
      String outputString = new String(result, 0, resultLength, "UTF-8");
      System.out.println("after compress and decompress the string is :" + outputString);


    2.


    import java.io.Serializable;

    public class Employee implements Serializable {

     private String name;
     
     private double salary;

     public Employee(String name, double salary) {
      super();
      // TODO Auto-generated constructor stub
      this.name = name;
      this.salary = salary;
     }
     
     public void raiseSalary(double byPercent){
      double temp = salary * byPercent / 100;
      salary += temp;
     }

     public String toString() {
      // TODO Auto-generated method stub
      return getClass().getName() +
       "[ Name = " + name + ", salary = " + salary +"]";
     }
     
    }





    public class Test2 {

     protected byte[] deflate(Object object) throws IOException{
      
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      Deflater def = new Deflater (Deflater.BEST_COMPRESSION);
      DeflaterOutputStream dos = new DeflaterOutputStream(baos, def);
      
      ObjectOutputStream out = new ObjectOutputStream(dos);
      out.writeObject(object);
      out.close();
      dos.finish();
      dos.close();
      
      return baos.toByteArray();
     }
     
     protected Object inflate(byte[] compressedContent) throws IOException{
      if(compressedContent == null)
       return null;
      
      try{
       ObjectInputStream in = new ObjectInputStream(
         new InflaterInputStream(new ByteArrayInputStream(compressedContent)));
       Object object = in.readObject();
       in.close();
       return object;
      }catch(Exception e){
       throw new IOException(e.toString());
      }
     }
     
     public static void main(String[] args) throws IOException{
      Employee employee = new Employee("LiLei", 1000);
      Test2 test2 = new Test2();
      byte[] bytes = new byte[1000];
      
      bytes = test2.deflate(employee);
      System.out.println(bytes);
      
      System.out.println(test2.inflate(bytes));
      
     }
    }

    posted @ 2007-04-10 21:29 chenweicai 閱讀(592) | 評(píng)論 (0)編輯 收藏

      JDK為程序員提供了大量的類(lèi)庫(kù),而為了保持類(lèi)庫(kù)的可重用性,可擴(kuò)展性和靈活性,其中使用到了大量的設(shè)計(jì)模式,本文將介紹JDK的I/O包中使用到的Decorator模式,并運(yùn)用此模式,實(shí)現(xiàn)一個(gè)新的輸出流類(lèi)。

      Decorator模式簡(jiǎn)介

      Decorator模式又名包裝器(Wrapper),它的主要用途在于給一個(gè)對(duì)象動(dòng)態(tài)的添加一些額外的職責(zé)。與生成子類(lèi)相比,它更具有靈活性。
    有時(shí)候,我們需要為一個(gè)對(duì)象而不是整個(gè)類(lèi)添加一些新的功能,比如,給一個(gè)文本區(qū)添加一個(gè)滾動(dòng)條的功能。我們可以使用繼承機(jī)制來(lái)實(shí)現(xiàn)這一功能,但是這種方法不夠靈活,我們無(wú)法控制文本區(qū)加滾動(dòng)條的方式和時(shí)機(jī)。而且當(dāng)文本區(qū)需要添加更多的功能時(shí),比如邊框等,需要?jiǎng)?chuàng)建新的類(lèi),而當(dāng)需要組合使用這些功能時(shí)無(wú)疑將會(huì)引起類(lèi)的爆炸。

      我們可以使用一種更為靈活的方法,就是把文本區(qū)嵌入到滾動(dòng)條中。而這個(gè)滾動(dòng)條的類(lèi)就相當(dāng)于對(duì)文本區(qū)的一個(gè)裝飾。這個(gè)裝飾(滾動(dòng)條)必須與被裝飾的組件(文本區(qū))繼承自同一個(gè)接口,這樣,用戶(hù)就不必關(guān)心裝飾的實(shí)現(xiàn),因?yàn)檫@對(duì)他們來(lái)說(shuō)是透明的。裝飾會(huì)將用戶(hù)的請(qǐng)求轉(zhuǎn)發(fā)給相應(yīng)的組件(即調(diào)用相關(guān)的方法),并可能在轉(zhuǎn)發(fā)的前后做一些額外的動(dòng)作(如添加滾動(dòng)條)。通過(guò)這種方法,我們可以根據(jù)組合對(duì)文本區(qū)嵌套不同的裝飾,從而添加任意多的功能。這種動(dòng)態(tài)的對(duì)對(duì)象添加功能的方法不會(huì)引起類(lèi)的爆炸,也具有了更多的靈活性。

      以上的方法就是Decorator模式,它通過(guò)給對(duì)象添加裝飾來(lái)動(dòng)態(tài)的添加新的功能。如下是Decorator模式的UML圖:



      Component為組件和裝飾的公共父類(lèi),它定義了子類(lèi)必須實(shí)現(xiàn)的方法。

      ConcreteComponent是一個(gè)具體的組件類(lèi),可以通過(guò)給它添加裝飾來(lái)增加新的功能。

      Decorator是所有裝飾的公共父類(lèi),它定義了所有裝飾必須實(shí)現(xiàn)的方法,同時(shí),它還保存了一個(gè)對(duì)于Component的引用,以便將用戶(hù)的請(qǐng)求轉(zhuǎn)發(fā)給Component,并可能在轉(zhuǎn)發(fā)請(qǐng)求前后執(zhí)行一些附加的動(dòng)作。

      ConcreteDecoratorA和ConcreteDecoratorB是具體的裝飾,可以使用它們來(lái)裝飾具體的Component。

      Java IO包中的Decorator模式

      JDK提供的java.io包中使用了Decorator模式來(lái)實(shí)現(xiàn)對(duì)各種輸入輸出流的封裝。以下將以java.io.OutputStream及其子類(lèi)為例,討論一下Decorator模式在IO中的使用。

      首先來(lái)看一段用來(lái)創(chuàng)建IO流的代碼:

    以下是代碼片段:
    try {
     OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
    } catch (FileNotFoundException e) {
     e.printStackTrace();
    }

      這段代碼對(duì)于使用過(guò)JAVA輸入輸出流的人來(lái)說(shuō)再熟悉不過(guò)了,我們使用DataOutputStream封裝了一個(gè)FileOutputStream。這是一個(gè)典型的Decorator模式的使用,F(xiàn)ileOutputStream相當(dāng)于Component,DataOutputStream就是一個(gè)Decorator。將代碼改成如下,將會(huì)更容易理解:

    以下是代碼片段:
    try {
     OutputStream out = new FileOutputStream("test.txt");
     out = new DataOutputStream(out);
    } catch(FileNotFoundException e) {
     e.printStatckTrace();
    }

      由于FileOutputStream和DataOutputStream有公共的父類(lèi)OutputStream,因此對(duì)對(duì)象的裝飾對(duì)于用戶(hù)來(lái)說(shuō)幾乎是透明的。下面就來(lái)看看OutputStream及其子類(lèi)是如何構(gòu)成Decorator模式的:



      OutputStream是一個(gè)抽象類(lèi),它是所有輸出流的公共父類(lèi),其源代碼如下:

    以下是代碼片段:
    public abstract class OutputStream implements Closeable, Flushable {
     public abstract void write(int b) throws IOException;
     ...
    }

      它定義了write(int b)的抽象方法。這相當(dāng)于Decorator模式中的Component類(lèi)。

      ByteArrayOutputStream,F(xiàn)ileOutputStream 和 PipedOutputStream 三個(gè)類(lèi)都直接從OutputStream繼承,以ByteArrayOutputStream為例:

    以下是代碼片段:
    public class ByteArrayOutputStream extends OutputStream {
     protected byte buf[];
     protected int count;
     public ByteArrayOutputStream() {
      this(32);
     }
     public ByteArrayOutputStream(int size) {
      if (size 〈 0) {
       throw new IllegalArgumentException("Negative initial size: " + size);
      }
      buf = new byte[size];
     }
     public synchronized void write(int b) {
      int newcount = count + 1;
      if (newcount 〉 buf.length) {
       byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
       System.arraycopy(buf, 0, newbuf, 0, count);
       buf = newbuf;
      }
      buf[count] = (byte)b;
      count = newcount;
     }
     ...
    }

      它實(shí)現(xiàn)了OutputStream中的write(int b)方法,因此我們可以用來(lái)創(chuàng)建輸出流的對(duì)象,并完成特定格式的輸出。它相當(dāng)于Decorator模式中的ConcreteComponent類(lèi)。

      接著來(lái)看一下FilterOutputStream,代碼如下:

    以下是代碼片段:
    public class FilterOutputStream extends OutputStream {
     protected OutputStream out;
     public FilterOutputStream(OutputStream out) {
      this.out = out;
     }
     public void write(int b) throws IOException {
      out.write(b);
     }
     ...
    }

      同樣,它也是從OutputStream繼承。但是,它的構(gòu)造函數(shù)很特別,需要傳遞一個(gè)OutputStream的引用給它,并且它將保存對(duì)此對(duì)象的引用。而如果沒(méi)有具體的OutputStream對(duì)象存在,我們將無(wú)法創(chuàng)建FilterOutputStream。由于out既可以是指向FilterOutputStream類(lèi)型的引用,也可以是指向ByteArrayOutputStream等具體輸出流類(lèi)的引用,因此使用多層嵌套的方式,我們可以為ByteArrayOutputStream添加多種裝飾。這個(gè)FilterOutputStream類(lèi)相當(dāng)于Decorator模式中的Decorator類(lèi),它的write(int b)方法只是簡(jiǎn)單的調(diào)用了傳入的流的write(int b)方法,而沒(méi)有做更多的處理,因此它本質(zhì)上沒(méi)有對(duì)流進(jìn)行裝飾,所以繼承它的子類(lèi)必須覆蓋此方法,以達(dá)到裝飾的目的。

      BufferedOutputStream 和 DataOutputStream是FilterOutputStream的兩個(gè)子類(lèi),它們相當(dāng)于Decorator模式中的ConcreteDecorator,并對(duì)傳入的輸出流做了不同的裝飾。以BufferedOutputStream類(lèi)為例:

    以下是代碼片段:
    public class BufferedOutputStream extends FilterOutputStream {
     ...
     private void flushBuffer() throws IOException {
      if (count 〉 0) {
       out.write(buf, 0, count);
       count = 0;
      }
     }
     public synchronized void write(int b) throws IOException {
      if (count 〉= buf.length) {
       flushBuffer();
      }
      buf[count++] = (byte)b;
     }
     ...
    }

      這個(gè)類(lèi)提供了一個(gè)緩存機(jī)制,等到緩存的容量達(dá)到一定的字節(jié)數(shù)時(shí)才寫(xiě)入輸出流。首先它繼承了FilterOutputStream,并且覆蓋了父類(lèi)的write(int b)方法,在調(diào)用輸出流寫(xiě)出數(shù)據(jù)前都會(huì)檢查緩存是否已滿,如果未滿,則不寫(xiě)。這樣就實(shí)現(xiàn)了對(duì)輸出流對(duì)象動(dòng)態(tài)的添加新功能的目的。

      下面,將使用Decorator模式,為IO寫(xiě)一個(gè)新的輸出流?!?JDK為程序員提供了大量的類(lèi)庫(kù),而為了保持類(lèi)庫(kù)的可重用性,可擴(kuò)展性和靈活性,其中使用到了大量的設(shè)計(jì)模式,本文將介紹JDK的I/O包中使用到的Decorator模式,并運(yùn)用此模式,實(shí)現(xiàn)一個(gè)新的輸出流類(lèi)。

      Decorator模式簡(jiǎn)介

      Decorator模式又名包裝器(Wrapper),它的主要用途在于給一個(gè)對(duì)象動(dòng)態(tài)的添加一些額外的職責(zé)。與生成子類(lèi)相比,它更具有靈活性。
    有時(shí)候,我們需要為一個(gè)對(duì)象而不是整個(gè)類(lèi)添加一些新的功能,比如,給一個(gè)文本區(qū)添加一個(gè)滾動(dòng)條的功能。我們可以使用繼承機(jī)制來(lái)實(shí)現(xiàn)這一功能,但是這種方法不夠靈活,我們無(wú)法控制文本區(qū)加滾動(dòng)條的方式和時(shí)機(jī)。而且當(dāng)文本區(qū)需要添加更多的功能時(shí),比如邊框等,需要?jiǎng)?chuàng)建新的類(lèi),而當(dāng)需要組合使用這些功能時(shí)無(wú)疑將會(huì)引起類(lèi)的爆炸。

      我們可以使用一種更為靈活的方法,就是把文本區(qū)嵌入到滾動(dòng)條中。而這個(gè)滾動(dòng)條的類(lèi)就相當(dāng)于對(duì)文本區(qū)的一個(gè)裝飾。這個(gè)裝飾(滾動(dòng)條)必須與被裝飾的組件(文本區(qū))繼承自同一個(gè)接口,這樣,用戶(hù)就不必關(guān)心裝飾的實(shí)現(xiàn),因?yàn)檫@對(duì)他們來(lái)說(shuō)是透明的。裝飾會(huì)將用戶(hù)的請(qǐng)求轉(zhuǎn)發(fā)給相應(yīng)的組件(即調(diào)用相關(guān)的方法),并可能在轉(zhuǎn)發(fā)的前后做一些額外的動(dòng)作(如添加滾動(dòng)條)。通過(guò)這種方法,我們可以根據(jù)組合對(duì)文本區(qū)嵌套不同的裝飾,從而添加任意多的功能。這種動(dòng)態(tài)的對(duì)對(duì)象添加功能的方法不會(huì)引起類(lèi)的爆炸,也具有了更多的靈活性。

      以上的方法就是Decorator模式,它通過(guò)給對(duì)象添加裝飾來(lái)動(dòng)態(tài)的添加新的功能。如下是Decorator模式的UML圖:



      Component為組件和裝飾的公共父類(lèi),它定義了子類(lèi)必須實(shí)現(xiàn)的方法。

      ConcreteComponent是一個(gè)具體的組件類(lèi),可以通過(guò)給它添加裝飾來(lái)增加新的功能。

      Decorator是所有裝飾的公共父類(lèi),它定義了所有裝飾必須實(shí)現(xiàn)的方法,同時(shí),它還保存了一個(gè)對(duì)于Component的引用,以便將用戶(hù)的請(qǐng)求轉(zhuǎn)發(fā)給Component,并可能在轉(zhuǎn)發(fā)請(qǐng)求前后執(zhí)行一些附加的動(dòng)作。

      ConcreteDecoratorA和ConcreteDecoratorB是具體的裝飾,可以使用它們來(lái)裝飾具體的Component。

      Java IO包中的Decorator模式

      JDK提供的java.io包中使用了Decorator模式來(lái)實(shí)現(xiàn)對(duì)各種輸入輸出流的封裝。以下將以java.io.OutputStream及其子類(lèi)為例,討論一下Decorator模式在IO中的使用。

      首先來(lái)看一段用來(lái)創(chuàng)建IO流的代碼:

    以下是代碼片段:
    try {
     OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
    } catch (FileNotFoundException e) {
     e.printStackTrace();
    }

      這段代碼對(duì)于使用過(guò)JAVA輸入輸出流的人來(lái)說(shuō)再熟悉不過(guò)了,我們使用DataOutputStream封裝了一個(gè)FileOutputStream。這是一個(gè)典型的Decorator模式的使用,F(xiàn)ileOutputStream相當(dāng)于Component,DataOutputStream就是一個(gè)Decorator。將代碼改成如下,將會(huì)更容易理解:

    以下是代碼片段:
    try {
     OutputStream out = new FileOutputStream("test.txt");
     out = new DataOutputStream(out);
    } catch(FileNotFoundException e) {
     e.printStatckTrace();
    }

      由于FileOutputStream和DataOutputStream有公共的父類(lèi)OutputStream,因此對(duì)對(duì)象的裝飾對(duì)于用戶(hù)來(lái)說(shuō)幾乎是透明的。下面就來(lái)看看OutputStream及其子類(lèi)是如何構(gòu)成Decorator模式的:



      OutputStream是一個(gè)抽象類(lèi),它是所有輸出流的公共父類(lèi),其源代碼如下:

    以下是代碼片段:
    public abstract class OutputStream implements Closeable, Flushable {
     public abstract void write(int b) throws IOException;
     ...
    }

      它定義了write(int b)的抽象方法。這相當(dāng)于Decorator模式中的Component類(lèi)。

      ByteArrayOutputStream,F(xiàn)ileOutputStream 和 PipedOutputStream 三個(gè)類(lèi)都直接從OutputStream繼承,以ByteArrayOutputStream為例:

    以下是代碼片段:
    public class ByteArrayOutputStream extends OutputStream {
     protected byte buf[];
     protected int count;
     public ByteArrayOutputStream() {
      this(32);
     }
     public ByteArrayOutputStream(int size) {
      if (size 〈 0) {
       throw new IllegalArgumentException("Negative initial size: " + size);
      }
      buf = new byte[size];
     }
     public synchronized void write(int b) {
      int newcount = count + 1;
      if (newcount 〉 buf.length) {
       byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
       System.arraycopy(buf, 0, newbuf, 0, count);
       buf = newbuf;
      }
      buf[count] = (byte)b;
      count = newcount;
     }
     ...
    }

      它實(shí)現(xiàn)了OutputStream中的write(int b)方法,因此我們可以用來(lái)創(chuàng)建輸出流的對(duì)象,并完成特定格式的輸出。它相當(dāng)于Decorator模式中的ConcreteComponent類(lèi)。

      接著來(lái)看一下FilterOutputStream,代碼如下:

    以下是代碼片段:
    public class FilterOutputStream extends OutputStream {
     protected OutputStream out;
     public FilterOutputStream(OutputStream out) {
      this.out = out;
     }
     public void write(int b) throws IOException {
      out.write(b);
     }
     ...
    }

      同樣,它也是從OutputStream繼承。但是,它的構(gòu)造函數(shù)很特別,需要傳遞一個(gè)OutputStream的引用給它,并且它將保存對(duì)此對(duì)象的引用。而如果沒(méi)有具體的OutputStream對(duì)象存在,我們將無(wú)法創(chuàng)建FilterOutputStream。由于out既可以是指向FilterOutputStream類(lèi)型的引用,也可以是指向ByteArrayOutputStream等具體輸出流類(lèi)的引用,因此使用多層嵌套的方式,我們可以為ByteArrayOutputStream添加多種裝飾。這個(gè)FilterOutputStream類(lèi)相當(dāng)于Decorator模式中的Decorator類(lèi),它的write(int b)方法只是簡(jiǎn)單的調(diào)用了傳入的流的write(int b)方法,而沒(méi)有做更多的處理,因此它本質(zhì)上沒(méi)有對(duì)流進(jìn)行裝飾,所以繼承它的子類(lèi)必須覆蓋此方法,以達(dá)到裝飾的目的。

      BufferedOutputStream 和 DataOutputStream是FilterOutputStream的兩個(gè)子類(lèi),它們相當(dāng)于Decorator模式中的ConcreteDecorator,并對(duì)傳入的輸出流做了不同的裝飾。以BufferedOutputStream類(lèi)為例:

    以下是代碼片段:
    public class BufferedOutputStream extends FilterOutputStream {
     ...
     private void flushBuffer() throws IOException {
      if (count 〉 0) {
       out.write(buf, 0, count);
       count = 0;
      }
     }
     public synchronized void write(int b) throws IOException {
      if (count 〉= buf.length) {
       flushBuffer();
      }
      buf[count++] = (byte)b;
     }
     ...
    }

      這個(gè)類(lèi)提供了一個(gè)緩存機(jī)制,等到緩存的容量達(dá)到一定的字節(jié)數(shù)時(shí)才寫(xiě)入輸出流。首先它繼承了FilterOutputStream,并且覆蓋了父類(lèi)的write(int b)方法,在調(diào)用輸出流寫(xiě)出數(shù)據(jù)前都會(huì)檢查緩存是否已滿,如果未滿,則不寫(xiě)。這樣就實(shí)現(xiàn)了對(duì)輸出流對(duì)象動(dòng)態(tài)的添加新功能的目的。

      下面,將使用Decorator模式,為IO寫(xiě)一個(gè)新的輸出流。


    自己寫(xiě)一個(gè)新的輸出流

      了解了OutputStream及其子類(lèi)的結(jié)構(gòu)原理后,我們可以寫(xiě)一個(gè)新的輸出流,來(lái)添加新的功能。這部分中將給出一個(gè)新的輸出流的例子,它將過(guò)濾待輸出語(yǔ)句中的空格符號(hào)。比如需要輸出"java io OutputStream",則過(guò)濾后的輸出為"javaioOutputStream"。以下為SkipSpaceOutputStream類(lèi)的代碼:

    以下是代碼片段:
    import java.io.FilterOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    /**
    * A new output stream, which will check the space character
    * and won’t write it to the output stream.
    * @author Magic
    *
    */
    public class SkipSpaceOutputStream extends FilterOutputStream {
     public SkipSpaceOutputStream(OutputStream out) {
      super(out);
     }
     /**
     * Rewrite the method in the parent class, and
     * skip the space character.
     */
     public void write(int b) throws IOException{
      if(b!=’ ’){
       super.write(b);
      }
     }
    }

      它從FilterOutputStream繼承,并且重寫(xiě)了它的write(int b)方法。在write(int b)方法中首先對(duì)輸入字符進(jìn)行了檢查,如果不是空格,則輸出。

      以下是一個(gè)測(cè)試程序:

    以下是代碼片段:
    import java.io.BufferedInputStream;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    /**
    * Test the SkipSpaceOutputStream.
    * @author Magic
    *
    */
    public class Test {
     public static void main(String[] args){
      byte[] buffer = new byte[1024];

      /**
      * Create input stream from the standard input.
      */
      InputStream in = new BufferedInputStream(new DataInputStream(System.in));

      /**
      * write to the standard output.
      */
      OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out));

      try {
       System.out.println("Please input your words: ");
       int n = in.read(buffer,0,buffer.length);
       for(int i=0;i〈n;i++){
        out.write(buffer[i]);
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
    }

      執(zhí)行以上測(cè)試程序,將要求用戶(hù)在console窗口中輸入信息,程序?qū)⑦^(guò)濾掉信息中的空格,并將最后的結(jié)果輸出到console窗口。比如:

    以下是引用片段:
    Please input your words:
    a b c d e f
    abcdef

      總 結(jié)

      在java.io包中,不僅OutputStream用到了Decorator設(shè)計(jì)模式,InputStream,Reader,Writer等都用到了此模式。而作為一個(gè)靈活的,可擴(kuò)展的類(lèi)庫(kù),JDK中使用了大量的設(shè)計(jì)模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。對(duì)于JDK中模式的研究不僅能加深對(duì)于模式的理解,而且還有利于更透徹的了解類(lèi)庫(kù)的結(jié)構(gòu)和組成。

    posted @ 2007-04-10 09:44 chenweicai 閱讀(116) | 評(píng)論 (0)編輯 收藏

    僅列出標(biāo)題
    共6頁(yè): 上一頁(yè) 1 2 3 4 5 6 下一頁(yè) 
    主站蜘蛛池模板: 亚洲午夜一区二区电影院| 久久久久亚洲精品无码蜜桃| 亚洲av成人一区二区三区观看在线| **真实毛片免费观看| 久久亚洲国产精品成人AV秋霞| 一区二区三区在线免费看| 亚洲AV无码久久| 51精品视频免费国产专区| 亚洲成a人片在线观看播放| 成人黄色免费网址| 久久精品国产亚洲AV忘忧草18| 成人无码区免费视频观看| 久久久久亚洲AV无码去区首| 免费一看一级毛片全播放| 国产精品免费αv视频| 国产亚洲婷婷香蕉久久精品| 在线成人爽a毛片免费软件| 国产成人精品亚洲2020| 浮力影院第一页小视频国产在线观看免费| 亚洲精品宾馆在线精品酒店| 亚洲国产成人久久一区久久| 国产无遮挡色视频免费观看性色| 亚洲国产精品不卡在线电影| 动漫黄网站免费永久在线观看| 亚洲色大成网站www久久九| 亚洲国产成人乱码精品女人久久久不卡 | 久久久久久av无码免费看大片| 亚洲AV色香蕉一区二区| 91频在线观看免费大全| 亚洲AV日韩综合一区| 国产美女亚洲精品久久久综合| 最好看最新的中文字幕免费| 亚洲久热无码av中文字幕| 国产亚洲精品拍拍拍拍拍| 免费国产叼嘿视频大全网站| 亚洲乱色熟女一区二区三区蜜臀| 337p日本欧洲亚洲大胆裸体艺术 | 免费无码专区毛片高潮喷水| 久久水蜜桃亚洲av无码精品麻豆| 大学生一级特黄的免费大片视频| 国产免费高清69式视频在线观看|