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

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

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

    posts - 495,  comments - 11,  trackbacks - 0
     
      線性表,鏈表,哈希表是常用的數據結構,在進行Java開發時,JDK已經為我們提供了一系列相應的類來實現基本的數據結構。這些類均在java.util包中。本文試圖通過簡單的描述,向讀者闡述各個類的作用以及如何正確使用這些類。

    Collection
    ├List
    │├LinkedList
    │├ArrayList
    │└Vector
    │ └Stack
    └Set
    Map
    ├Hashtable
    ├HashMap
    └WeakHashMap

    Collection接口
      Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些 Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類, Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。
      所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用于創建一個空的Collection,有一個Collection參數的構造函數用于創建一個新的 Collection,這個新的Collection與傳入的Collection有相同的元素。后一個構造函數允許用戶復制一個Collection。
      如何遍歷Collection中的每一個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下:
        Iterator it = collection.iterator(); // 獲得一個迭代子
        while(it.hasNext()) {
          Object obj = it.next(); // 得到下一個元素
        }
      由Collection接口派生的兩個接口是List和Set。

    List接口
      List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似于數組下標)來訪問List中的元素,這類似于Java的數組。
    和下面要提到的Set不同,List允許有相同的元素。
      除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個 ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向后遍歷。
      實現List接口的常用類有LinkedList,ArrayList,Vector和Stack。

    LinkedList類
      LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在 LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。
      注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
        List list = Collections.synchronizedList(new LinkedList(...));

    ArrayList類
      ArrayList實現了可變大小的數組。它允許所有元素,包括null。ArrayList沒有同步。
    size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間為線性。
      每個ArrayList實例都有一個容量(Capacity),即用于存儲元素的數組的大小。這個容量可隨著不斷添加新元素而自動增加,但是增長算法并沒有定義。當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
      和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。

    Vector類
      Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的 Iterator是同一接口,但是,因為Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。

    Stack 類
      Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得 Vector得以被當作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。

    Set接口
      Set是一種不包含重復的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
      很明顯,Set的構造函數有一個約束條件,傳入的Collection參數不能包含重復的元素。
      請注意:必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。

    Map接口
      請注意,Map沒有繼承Collection接口,Map提供key到value的映射。一個Map中不能包含相同的key,每個key只能映射一個 value。Map接口提供3種集合的視圖,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value映射。

    Hashtable類
      Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null)的對象都可作為key或者value。
      添加數據使用put(key, value),取出數據使用get(key),這兩個基本操作的時間開銷為常數。
    Hashtable 通過initial capacity和load factor兩個參數調整性能。通常缺省的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查找時間將增大,這會影響像get和put這樣的操作。
    使用Hashtable的簡單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:
        Hashtable numbers = new Hashtable();
        numbers.put(“one”, new Integer(1));
        numbers.put(“two”, new Integer(2));
        numbers.put(“three”, new Integer(3));
      要取出一個數,比如2,用相應的key:
        Integer n = (Integer)numbers.get(“two”);
        System.out.println(“two = ” + n);
      由于作為key的對象將通過計算其散列函數來確定與之對應的value的位置,因此任何作為key的對象都必須實現hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同,如果兩個不同對象的hashCode相同,這種現象稱為沖突,沖突會導致操作哈希表的時間開銷增大,所以盡量定義好的hashCode()方法,能加快哈希表的操作。
      如果相同的對象有不同的hashCode,對哈希表的操作會出現意想不到的結果(期待的get方法返回null),要避免這種問題,只需要牢記一條:要同時復寫equals方法和hashCode方法,而不要只寫其中一個。
      Hashtable是同步的。

    HashMap類
      HashMap和Hashtable類似,不同之處在于HashMap是非同步的,并且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的性能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。

    WeakHashMap類
      WeakHashMap是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那么該key可以被GC回收。

    總結
      如果涉及到堆棧,隊列等操作,應該考慮用List,對于需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機訪問元素,應該使用ArrayList。
      如果程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,如果多個線程可能同時操作一個類,應該使用同步的類。
      要特別注意對哈希表的操作,作為key的對象要正確復寫equals和hashCode方法。
      盡量返回接口而非實際的類型,如返回List而非ArrayList,這樣如果以后需要將ArrayList換成LinkedList時,客戶端代碼不用改變。這就是針對抽象編程。

    同步性
    Vector 是同步的。這個類中的一些方法保證了Vector中的對象是線程安全的。而ArrayList則是異步的,因此ArrayList中的對象并不是線程安全的。因為同步的要求會影響執行的效率,所以如果你不需要線程安全的集合那么使用ArrayList是一個很好的選擇,這樣可以避免由于同步帶來的不必要的性能開銷。
    數據增長
    從內部實現機制來講ArrayList和Vector都是使用數組(Array)來控制集合中的對象。當你向這兩種類型中增加元素的時候,如果元素的數目超出了內部數組目前的長度它們都需要擴展內部數組的長度,Vector缺省情況下自動增長原來一倍的數組長度, ArrayList是原來的50%,所以最后你獲得的這個集合所占的空間總是比你實際需要的要大。所以如果你要在集合中保存大量的數據那么使用 Vector有一些優勢,因為你可以通過設置集合的初始化大小來避免不必要的資源開銷。
    使用模式
    在ArrayList和Vector中,從一個指定的位置(通過索引)查找數據或是在集合的末尾增加、移除一個元素所花費的時間是一樣的,這個時間我們用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花費的時間會呈線形增長:O(n-i),其中n代表集合中元素的個數,i代表元素增加或移除元素的索引位置。為什么會這樣呢?以為在進行上述操作的時候集合中第i和第i個元素之后的所有元素都要執行位移的操作。這一切意味著什么呢?
    這意味著,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好選擇其他的集合操作類。比如,LinkList集合類在增加或移除集合中任何位置的元素所花費的時間都是一樣的?O(1),但它在索引一個元素的使用缺比較慢-O(i),其中i是索引的位置.使用 ArrayList也很容易,因為你可以簡單的使用索引來代替創建iterator對象的操作。LinkList也會為每個插入的元素創建對象,所有你要明白它也會帶來額外的開銷。
    最后,在《Practical Java》一書中Peter Haggar建議使用一個簡單的數組(Array)來代替Vector或ArrayList。尤其是對于執行效率要求高的程序更應如此。因為使用數組(Array)避免了同步、額外的方法調用和不必要的重新分配空間的操作。
    posted @ 2007-07-01 02:36 jadmin 閱讀(89) | 評論 (0)編輯 收藏
    posted @ 2007-06-30 21:27 jadmin 閱讀(73) | 評論 (0)編輯 收藏
    1,事業永遠第一
      雖然金錢不是萬能的,但沒有錢是萬萬不能的,雖然這句話很俗,但絕對有道理,所以30歲之前,請把你大部分精力放在你的事業上.
    2,別把錢看得太重
      不要抱怨自己現在工資低,銀行存款4位數以下,看不到前途,現在要做的就是努力學習,即使你文憑再高,怎么把理論運用到實踐還是需要一個很長的鍛煉過程,社會永遠是一所最博大的大學,它讓你學到的知識遠比你在學校學到的重要得多,所以同樣,你也別太介意學歷低.30歲之前靠自己能力買車買房的人還是極少.
    3,學會體諒父母
      別嫌他們嘮叨,等你為人父了你就知道可憐天下父母心,在他們眼里你還是個孩子,但他們真的老了,現在得你哄他們開心了,也許只要你的一個電話,一點小禮物,就可以讓他們安心,很容易做到.
    4,交上好朋友
      朋友對你一生都影響重大,不要去結識太多酒肉朋友,至少得有一個能在關鍵時刻幫助你的朋友,如果遇到這么一個人,就好好把握,日后必定有用,不管他現在是富還是窮.
    5,別太相信愛情
      心中要有愛,但請別說也別相信那些瓊瑤阿姨小說里面的山盟海誓,世上本無永恒,重要的是責任,但女人心海底針,心變了,一切都成枉然,你要做的就是該出手時就出手,該放手時別猶豫.30歲之前的愛情不是假的,但只是大多數人都沒有能真正把握好的能力,所以學會量力而行.
    6,別擔心至今還保留初吻
      愛情不在多而在精,別以為自己20多歲還沒碰過女孩子就害怕自己永遠找不到老婆.以后你會有很多機會認識女孩子,要知道這個社會雖然男人多于女人,但現實是女人其實比男人更擔心這個問題.男人30一枝花,你在升值而不是貶值,成熟的愛情往往更美麗更長久,所以不要像瘋狗一樣看到女孩就想追,學會品味寂寞.
    7,不要沉迷于任何東西
      所謂玩物而喪志,網絡游戲是你在出校門之前玩的,你現在沒有多余的時間和精力花費到這上面,否則你透支的東西以后都得償還.一個人要有興趣,愛好,但請分清楚輕重.
    8,年輕沒有失敗
      不要遇到挫折就灰心,年輕人要時刻保持積極向上的態度.失敗了,重來過;失去了,再爭取別的。錯過了,要分析,下次來,要把握;幼稚了,下次,成熟點。不要緊,會好的,哪怕到了極點,也不要放棄,相信一定可以挺過去。不要消極,會好的。曾經的錯,過去了,總不能回味在過去。現在的,很好,累完了,很舒服。不要傷,總會有人在支撐你。
    9,不要輕易崇拜或者鄙視一個人
    ???? 人都有偶像,但請擁有你自己的個性.不要刻意去模仿一個人,因為你就是你,是唯一的,獨一無二的,要有自信.也不要全盤否定一個人,每個人是有價值的,如果你不能理解他,也請學會接受.
    10,要有責任心.
      不管你曾經怎樣,但請從現在開始做一個正直的人.男人要有責任心,無論是工作還是生活上,一個有責任心的人才能讓別人有安全感,才能讓別人覺得你是一個值得信賴的人.我們不要懦弱,但請不要傷害愛你的人和你愛的人,尤其是善良的女孩,因為這個世界善良的女孩不多了,即使不想擁有,但也請讓她保持她美麗的心.
    11,男人的外貌并不重要.
      不要為自己的長相身高而過分擔心,一個心地善良,為人正直的男人遠比那些空有英俊相貌,挺拔身材但內心齷齪的男人要帥得多.如果有人以貌取人,請不要太在意,因為你不用去為一個低級趣味的人而難過.
    12,學會保護身體
      不要以為現在抽煙喝酒,熬夜通宵也沒什么事.那是因為你的身體正處于你一生的黃金時段.30歲以后你就能明白力不從心這個詞的意義了,身體是革命的本錢,沒有好的身體什么也做不了,所以要盡量讓自己過有規律的健康生活.
    13,別覺得一事無成.
      你現在還沒有資格談成功,當然如果你有千萬資產的除外.一開始太固定的職業并不一定是好事,或許在不斷的改行當中,你會學到更豐富的知識,而且可以挖掘出自己的潛能,找到最適合你的工作.
    14,請認真工作
      即使你現在的工作再怎么無聊再怎么低級,也請你認真去對待,要知道任何成功人士都是從最小的事做起,或許你現在學不到多么了不起的知識,但起碼你要學會良好的工作態度和工作方法,這對以后很重要.
    15,請認真對待感情.
      不要羨慕那些換女人像換鞋一樣的花花公子,逢場作戲的愛情只是讓你浪費時間浪費精力,一個人最痛苦的不是找不到愛人,而是心中沒有了愛,當你把我愛你3 個字變成你最容易說的一句話時,那么你在愛情的世界里已經很難找到真正的幸福了.愛情沒有公平,總有一個人比對方付出得多,即使沒有結果,也別覺得不值, 因為你的付出不光是為了她,也是為了你自己的愛,為愛付出是很可貴的,贊自己一下.
    16.請留一點童心
      在內心深處,哪怕只是一個很小的角落里,請保持一份童心,不是幼稚,但有的時候單純一點會讓你很快樂.所以不要太計較得失,生活本無完美.
    posted @ 2007-06-25 14:51 jadmin 閱讀(43) | 評論 (0)編輯 收藏

    pageEncoding
    ?????
    在JSP標準的語法中,如果pageEncoding屬性存在,那么JSP頁面的字符編碼方式就由pageEncoding決定,否則就由contentType屬性中的charset決定,如果charset也不存在,JSP頁面的字符編碼方式就采用默認的ISO-8859-1。

    ContentType
    ????? ContentType屬性指定了MIME類型和JSP頁面回應時的字符編碼方式。MIME類型的默認值是“text/html”; 字符編碼方式的默認值是“ISO-8859-1”. MIME類型和字符編碼方式由分號隔開

    pageEncoding的內容只是用于jsp輸出時的編碼,不會作為header發出去的。

    pageEncoding??? 是通知web server jsp的編碼。

    ===========================================================

    contentType — 指定的是JSP頁最終 Browser(客戶端)所見到的網頁內容的編碼.
    就是 Mozilla的 Character encoding, 或者是 IE6的 encoding. 例如 JSPtw Forum 用的contentType就是 Big5.
    pageEncoding — 指定JSP編寫時所用的編碼
    如果你的是 WIN98, 或 ME 的NOTEPAD記事本編寫JSP, 就一定是常用的是Big5 或 gb2312, 如果是用 WIN2k winXP的
    NOTEPAD時, SAVE時就可以選擇不同的編,碼, 包括 ANSI(BIG5/GB2312)或 UTF-8 或 UNIONCODE(估是 UCS 16).
    因為 JSP要經過 兩次的”編碼”,
    第一階段會用 pageEncoding, 第二階段會用 utf-8 至utf-8, 第三階段就是由TOMCAT出來的網頁, 用的是contentType.

    階段一是 JSPC的 JSP至JAVA(.java)原碼的”翻譯”, 它會跟據 pageEncoding 的設定讀取JSP. 結果是 由指定的
    pageEncoding(utf-8,Big5,gb2312)的JSP 翻譯成統一的utf-8 JAVA原碼(.java). 如果pageEncoding設定錯了, 或沒設定(預設 ISO8859-1), 出來的 在這個階段 就已是中文亂碼.

    階段二是由 JAVAC的JAVA原碼至JAVA BYTECODE的編譯. 不論JSP的編寫時是用(utf-8,Big5,gb2312),經過階段一的結果全都是utf-8的ENCODING的JAVA原碼.
    JAVAC用 utf-8的ENCODING讀取AVA原碼, 編譯成字符串是 utf-8 ENCODING的二進制碼(.class). 這是 JAVA VIRTUAL MACNHINE對常數字符串在 二進制碼(JAVA BYTECODE)內表逹的規范.

    階段三是TOMCAT(或其的application container)加載和執行階段二得來的JAVA二進制碼, 輸出的結果( 也就是BROWSER(客戶端)見到的)
    這時一早隱藏在階段一和二的參數contentType, 就發揮了功效. (見 階段一的 ).

    response.setContentType(”text/html; charset=utf-8″);
    出來的可以是 utf-8, Big5, gb2312, 看的就是JSP ? contentType的設定.
    <%@ page session=”false” pageEncoding=”big5″ contentType=”text/html; charset=utf-8″ %>
    還有, pageEncoding 和contentType的預設都是 ISO8859-1. 而隨便設定了其中一個, 另一個就跟著一樣了(TOMCAT4.1.27是如此).
    但這不是絕對, 看的各自JSPC的處理方式. 而pageEncoding不等于contentType, 更有利亞洲區的文字 CJKV系JSP網頁的開發和展示,
    (例pageEncoding=Big5 不等于 contentType=utf-8).
    一個簡單的解決方法是在包含和被包含文件的開始部分都加上:
    <%@ page contentType=”text/html;charset=GB2312″ language=”java” %>

    posted @ 2007-06-24 15:43 jadmin 閱讀(52) | 評論 (0)編輯 收藏
    //先寫入文件,后讀取
    import java.io.*;
    public class StreamDemo
    {
    public static void main(String[] args)
    {
    ?? try
    ?? {
    ??? //建立輸出流
    ??? DataOutputStream out=
    ???? new DataOutputStream(
    ????? new BufferedOutputStream(
    ?????? new FileOutputStream(
    ??????? "Example.txt")));
    ??? //寫入一個double類型數據
    ??? out.writeDouble(3.1425926);
    ??? //寫入一行文本
    ??? out.writeBytes("This was pi");
    ??? out.close();
    ??? //建立輸入流
    ??? DataInputStream in=
    ???? new DataInputStream(
    ????? new BufferedInputStream(
    ?????? new FileInputStream(
    ??????? "Example.txt")));
    ??? //建立輸入Reader
    ??? BufferedReader reader=
    ???? new BufferedReader(
    ????? new InputStreamReader(in));
    ??? //讀入一個double類型數據
    ??? System.out.println(in.readDouble());
    ??? //讀入一行文本
    ??? System.out.println(reader.readLine());
    ?? }
    ?? catch(IOException e)
    ?? {
    ??? System.out.println(e.toString());
    ?? }
    }
    }
    posted @ 2007-06-18 23:51 jadmin 閱讀(67) | 評論 (0)編輯 收藏
    import java.io.*;
    import java.util.zip.*;
    public class ZipCompress
    {
    public static void main(String[] args)
    {
    ?? try
    ?? {
    ??? //建立輸出文件流
    ??? FileOutputStream fileOut=
    ???? new FileOutputStream("Example.zip");
    ??? //建立冗余驗證流
    ??? CheckedOutputStream checkedOut=
    ???? new CheckedOutputStream(fileOut,new CRC32());
    ??? //建立Zip流
    ??? ZipOutputStream zipOut=
    ???? new ZipOutputStream(
    ????? new BufferedOutputStream(checkedOut));
    ??? //設置注釋內容
    ??? zipOut.setComment("This is a java zipping test file");
    ??? //文件名
    ??? String fileName=
    ???? "D:/kk/"+"mm.txt"; //文件路徑(最好是全英文字符)
    ??? //讀取被壓速文件流
    ??? BufferedReader in=
    ???? new BufferedReader(new FileReader(fileName));
    ??? //建立壓縮實體
    ??? zipOut.putNextEntry(new ZipEntry(fileName));
    ??? int ch;
    ??? //當被壓縮文件沒有結束時繼續讀寫
    ??? while ((ch=in.read())!=-1)
    ??? {
    ???? zipOut.write(ch);
    ??? }
    ??? //關閉文件流,釋放資源
    ??? in.close();
    ??? zipOut.close();
    ?? }
    ?? catch (IOException e)
    ?? {
    ??? System.out.println(e.toString());
    ?? }
    }
    }
    posted @ 2007-06-18 19:21 jadmin 閱讀(55) | 評論 (0)編輯 收藏

    import java.net.*;

    public class GetHostName {

    ???? /**
    ????? * @param args
    ????? */
    ???? public static void main(String[] args) {
    ???????? try{
    ???????????? InetAddress addr = InetAddress.getByName("127.0.0.1");//在給定主機名的情況下確定主機的 IP 地址。
    ???????????? byte[] ipAddr = new byte[]{127,0,0,1};
    ???????????? addr = InetAddress.getByAddress(ipAddr);// 返回此 InetAddress 對象的原始 IP 地址。

    ???????????? String hostName = addr.getHostName();//?? 獲取此 IP 地址的主機名。
    ???????????? System.out.println(hostName);

    ???????????? String hostnameCanonical = addr.getCanonicalHostName();//獲取此 IP 地址的完全限定域名。
    ???????????? System.out.println(hostnameCanonical);
    ???????? }catch(Exception e){
    ???????????? e.printStackTrace();
    ???????? }

    ???? }

    }

    posted @ 2007-06-18 02:31 jadmin 閱讀(73) | 評論 (0)編輯 收藏
    ??????? 本文將提供一個對這些概念的簡明的解釋,而不是提供一些深入的或者如何使用的問題。

      Java在虛擬機上運行

      Java源代碼并不是被編譯成為普通的機器代碼。而是被翻譯成為虛擬機可以執行的代碼。一個Java解釋器最終執行這些代碼。這其中沒有連接的過程;解釋在需要的時候動態的加載一些類;

      Java是完全面向對象的

      Java是一種完全面向對象的語言。這意味著你對任何一個Java對象所做的動作都是通過一個方法實現的。第一點就是,再也沒有沒有主函數這樣的孤立的東西了。取而代之的是,你必須開始用一個對象的看法看待一個程序,一個類的對象。但是這個對象又什么對象呢?大多數Java程序只是簡單的通過繼承Java基礎類Object來實現所需要的東西,但是你可以通過創建程序基礎類用于多個特性相似的應用程序來節省時間。

      嚴格的面向對象的規定意味著理用原有的C/C++代碼不可以直接不加改動的使用;系統調用也是這樣的。C++中,你可以通過在C++正常的命名空間外聲明extern"C"來使用原有的C的過程調用,包括系統調用。

      在Java中,只有一個類似的安全回溯的方法,但是并不是十分簡單的方法。你必須定義一個本地方法,其目的是為C語言提供接口,然后提供連接的介質。Java環境提供了完成這種任務的工具,但是整個過程和C++中提供的extern比微不足道,完成使用C++類的過程則更加復雜,因為這樣會引入對C的借口和C函數和C++成員函數的問題。

      幸運的是,許多常用的系統實用工具函數已經在系統類中的方法中提供出來,但是這些明顯沒有包含經過許多年來你所創建的那些類和過程。所以,在你需要的時候你應該去鉆研一下。

      Java中沒有獨立的頭文件

      在Java中,關于類的一切東西都被放到一個單獨的文件中。方法的位置只可能在一個地方出現,一個方法的實現必須在它的定義過程中同時進行。這樣做得優點是在實現程序的時候不容易因為文件的非同步錯誤而失敗,或者獲取到一個沒有實現的聲明。類的聲明可以被Java解釋器利用甚至是從一個編譯過的單元中獲取,所以不再需要有頭文件,只要有編譯過的文件。

      這樣做的缺點與我們編程的過程有關。許多C++程序員喜歡用頭文件來代替文檔。要看一個成員函數的接口參數,只需要看頭文件中的聲明即可。你可以經常的看頭文件即可了解怎樣去使用這個類。在Java中,沒有這樣的總結。因為實現類方法的代碼必須在方法定義的時候出現,而且,對于一個單獨的函數的代碼來說就經常占據了一整頁乃至更多。這樣,很難通過看Java的代碼就初步了解類是怎樣使用的。你必須為你需要的類準備足夠多的文檔。不言而喻,再處理非商業類庫的時候文檔是極度缺乏的。

      在當先的Java環境中提供了兩個工具來補償這些,javap來打印類標識,javadoc為嵌入式程序提供HTML文檔。

      用Package來分解Java命名空間

      在大的C++工程中經常遇到的一個問題是命名空間--怎樣保證工程的一些程序員不會創建和另一些程序員一樣名字的類?更糟糕的是,供應商可能會提供一個包含和你的類一樣名字的類的庫。有許多方法可以解決這一問題,但是很可能在問題發現之前工程已經啟動,改正錯誤是需要付出許多痛苦的。

      Java通過"Package"這個概念解決了這個問題,Package有效地通過通過集合類劃分了命名空間。在不同包內的兩個同名的類仍然是不同的。關鍵問題就變成了類是否放置到相應的包中。

      記住,Java并沒有解決命名沖突的問題。擴展一個基類而引起了派生類的沖突。比如說,如果你最喜歡的供應商提供了一些類,然后你把它們用做基類并且派生有一個foo方法的類,當供應商提供一個新版本的類的時候就可能出現,如果供應商業也在新類中提供了一個foo的方法。

      異常是Java的重要特性

      在C++中,異常和異常處理是十分深奧的事情;許多C++程序員從沒有處理過它們甚至不知道它們是何物。異常是在正常的過程中出現的未預料的錯誤,因此,它們不會從方法中返回,或者作為參數傳入;但是,它們不能被忽略!這里的一個例子是計算一個書的方根的方法。正常的接口形式是將一個正數作為參數傳入方法,然后方法會返回一個正實數作為結果,方法可以檢驗這些并且在異常產生的時候拋出異常。在大多數系統中,程序員并不是必須這樣做,這樣,一個沒有考慮到的異常可以使程序不正常的退出。

      在Java中,異常已經成為語言中非常成熟的部分。方法的說明中就包含了異常的信息,程序處理器也強制檢驗如果你使用了一個能夠產生異常的方法,你就必須檢查異常是否發生。幾乎所有的Java程序員都會遇到異常的情況,因為許多非常有用的庫中的類都會拋出異常。處理異常并不難,但是在一些時候是需要注意的。一個方法的文檔會指明方法拋出的異常的類型。如果你忘了,不要緊,編譯器會提醒你的。

      字符串不再是字符數組

      Java中包括了一個字符串的對象,并且是個常量。字符串不像字符數組一樣,雖然可以簡單的從一個字符數組構造一個字符串。你應該盡可能的用字符串代替字符數組,因為他們不會因為誤操作而被覆蓋。

      Java限制了常量對象和方法

      在C++中,你可以正式的聲明一個函數參數或者函數返回值為const類型,這樣可以有效的防止對參數或者返回值的不正當修改。另外,你可以聲明一個成員函數為const,表明它不可以修改任何他操作的對象。

      Java支持常量操作符,只讀變量,這些通過final關鍵字實現。但是Java沒有支持強制的使一個可寫變量在函數傳遞、返回的過程中變為只讀。或者定義一個不操作修改對象的常量方法。

      在Java中,這個省略帶來的影響和在C++中相比就非常小了,這很大程度上因為字符串變量和字符數組的不同,但是這也帶來一個引起錯誤的隱患。特別地,沒有辦法檢驗一個方法是否可以改動對象。

      Java沒有指針

      理解指針的概念是一個C或C++程序員最難應付的問題。指針也是錯誤產生的一大根源。Java中沒有指針,對象的句柄直接作為參數傳遞,而不是傳遞指針。另外,你必須通過索引使用數組。這些都不是什么大問題。然而,沒有指針是在寫含有函數指針或者成員函數指針的系統的時候引起很大麻煩。這個問題在處理回調函數的時候更加顯著。

      Java沒有參數化類型

      參數化類型提供了用一段程序處理許多相似程序的方法。一個例子就是開平方根的方法,它可以對int或者float操作。在C++中,這一特性是由模板提供的。

      Java中不包含C++中的模板的等價物。如果你經常使用模板來簡化程序,比如說構造許多使用相似參數類型的函數,這簡直就是災難。這意味著更多使用復制、粘貼的過程來手動的完成。然而,如果你使用模板來生成類的話,沒有簡單的方法。

      Java使用垃圾回收

      在垃圾回收的語言中,運行時環境一直監測哪些內存不被使用。當一塊內存不用的時候,系統自動的回收內存。比如說,一個對象在一個方法中生成,但是沒有被調用著返回或者沒有儲存為全局變量,不能在方法外部使用。系統自己會知道哪些變量是你用不到的,哪些是可以用到的。因此,你不必再為破壞對象回收內存而擔心。在C++中,很多的調試時間都被使用到檢查內存漏洞中。Java的這種方法很大程度上降低了這種錯誤的可能。但是他依然不能處理邏輯混亂的程序,他們不能夠被回收。許多C++的類中的析構函數是用來釋放對象引用的內存的。Java使垃圾回收的事實說明在Java中不是必需寫析構函數了。但是并不意味著你可以忘記為你的類寫析構函數。比如,一個對象打開了網絡連接就必須被恰當的清理來關閉這個連接。在Java中,析構函數被稱作"finalization"方法。

      Java不支持多重繼承

      在任何一個復雜的面向對象的系統中,實現一個有更多方法的新類是十分經常遇到的事情。比如說,一個Manager類,需要被作為一個連表的表頭,但是一個Manager又必須是一個Employee。有許多方法來處理這樣的問題。一個方法是允許從多個類繼承。在這個例子中,Manager需要從Linked List和Employee繼承。

      Java沒有多重繼承。但是你可以聲明接口--來描述實現一些功能的編程接口。一個類可以由多個接口實現,包括他唯一的功能。不同的類可以由同樣的接口實現。方法的參數既可以聲明為類,也可以聲明為接口。如果是接口的話,實現接口的類就可以作為參數傳入方法。

      接口的概念要比多繼承容易理解一些,但是他有一定的局限性。特別地,你必須在類中實現接口的時候編碼去重新實現類的功能。

      Java支持多線程

      多線程可以使你寫出在同一時刻完成多種任務的程序。比如說,你可以在完成讀取一個大文件之間允許用戶對已經讀取的部分進行編輯。你需要把程序分為多線程來執行。為安全起見。你的程序要被精心的設計,因為可能不止一個線程需要對數據進行訪問、修改。

      Java開始就支持多線程。類和接口用來分解一個程序成為不同的線程。語言簡單的對重要的數據作同步或者鎖定處理。

      Java以一些預定義的類為基礎

      默認的Java環境中包括一些從Java基礎類實現而來的一些包。這些允許你很快的寫出一些有用的程序,這些包如下:

      java.awt:當今許多應用程序都非常依賴GUI,java提供了一個Abstract Window Toolkid,這可以讓你在不考慮運行平臺的前提下處理GUI對象。

      java.applet:applet的主要目的是提供瀏覽有關的內容。它本身是awt組件的字類并且支持其他一些特性,比如聲音、渲染等。

      java.io:java.io提供了對流、文件、管道的讀寫操作。

      java.lang:提供了java的基礎類Objcet,Integar,Float……;

      java.net:提供對網絡編程的支持。包括處理socket,URL,Internet尋址等。

      java.util:為數據結構提供的通用實用工具集。
    posted @ 2007-06-15 00:11 jadmin 閱讀(73) | 評論 (0)編輯 收藏

    插入排序:

    package org.rut.util.algorithm.support;

    import org.rut.util.algorithm.SortUtil;
    /**
    * @author treeroot
    * @since 2006-2-2
    * @version 1.0
    */
    public class InsertSort implements SortUtil.Sort{

    ???? /* (non-Javadoc)
    ????? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
    ????? */
    ???? public void sort(int[] data) {
    ???????? int temp;
    ???????? for(int i=1;i<data.length;i++){
    ???????????? for(int j=i;(j>0)&&(data[j]<data[j-1]);j--){
    ???????????????? SortUtil.swap(data,j,j-1);
    ???????????? }
    ???????? }????????
    ???? }

    }


    冒泡排序:

    package org.rut.util.algorithm.support;

    import org.rut.util.algorithm.SortUtil;

    /**
    * @author treeroot
    * @since 2006-2-2
    * @version 1.0
    */
    public class BubbleSort implements SortUtil.Sort{

    ???? /* (non-Javadoc)
    ????? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
    ????? */
    ???? public void sort(int[] data) {
    ???????? int temp;
    ???????? for(int i=0;i<data.length;i++){
    ???????????? for(int j=data.length-1;j>i;j--){
    ???????????????? if(data[j]<data[j-1]){
    ???????????????????? SortUtil.swap(data,j,j-1);
    ???????????????? }
    ???????????? }
    ???????? }
    ???? }

    }


    選擇排序:

    package org.rut.util.algorithm.support;

    import org.rut.util.algorithm.SortUtil;

    /**
    * @author treeroot
    * @since 2006-2-2
    * @version 1.0
    */
    public class SelectionSort implements SortUtil.Sort {

    ???? /*
    ????? * (non-Javadoc)
    ????? *
    ????? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
    ????? */
    ???? public void sort(int[] data) {
    ???????? int temp;
    ???????? for (int i = 0; i < data.length; i++) {
    ???????????? int lowIndex = i;
    ???????????? for (int j = data.length - 1; j > i; j--) {
    ???????????????? if (data[j] < data[lowIndex]) {
    ???????????????????? lowIndex = j;
    ???????????????? }
    ???????????? }
    ???????????? SortUtil.swap(data,i,lowIndex);
    ???????? }
    ???? }

    }


    改進后的歸并排序:

    package org.rut.util.algorithm.support;

    import org.rut.util.algorithm.SortUtil;

    /**
    * @author treeroot
    * @since 2006-2-2
    * @version 1.0
    */
    public class ImprovedMergeSort implements SortUtil.Sort {

    ???? private static final int THRESHOLD = 10;

    ???? /*
    ????? * (non-Javadoc)
    ????? *
    ????? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
    ????? */
    ???? public void sort(int[] data) {
    ???????? int[] temp=new int[data.length];
    ???????? mergeSort(data,temp,0,data.length-1);
    ???? }

    ???? private void mergeSort(int[] data, int[] temp, int l, int r) {
    ???????? int i, j, k;
    ???????? int mid = (l + r) / 2;
    ???????? if (l == r)
    ???????????? return;
    ???????? if ((mid - l) >= THRESHOLD)
    ???????????? mergeSort(data, temp, l, mid);
    ???????? else
    ???????????? insertSort(data, l, mid - l + 1);
    ???????? if ((r - mid) > THRESHOLD)
    ???????????? mergeSort(data, temp, mid + 1, r);
    ???????? else
    ???????????? insertSort(data, mid + 1, r - mid);

    ???????? for (i = l; i <= mid; i++) {
    ???????????? temp[i] = data[i];
    ???????? }
    ???????? for (j = 1; j <= r - mid; j++) {
    ???????????? temp[r - j + 1] = data[j + mid];
    ???????? }
    ???????? int a = temp[l];
    ???????? int b = temp[r];
    ???????? for (i = l, j = r, k = l; k <= r; k++) {
    ???????????? if (a < b) {
    ???????????????? data[k] = temp[i++];
    ???????????????? a = temp[i];
    ???????????? } else {
    ???????????????? data[k] = temp[j--];
    ???????????????? b = temp[j];
    ???????????? }
    ???????? }
    ???? }

    ???? /**
    ????? * @param data
    ????? * @param l
    ????? * @param i
    ????? */
    ???? private void insertSort(int[] data, int start, int len) {
    ???????? for(int i=start+1;i<start+len;i++){
    ???????????? for(int j=i;(j>start) && data[j]<data[j-1];j--){
    ???????????????? SortUtil.swap(data,j,j-1);
    ???????????? }
    ???????? }
    ???? }


    堆排序:

    package org.rut.util.algorithm.support;

    import org.rut.util.algorithm.SortUtil;

    /**
    * @author treeroot
    * @since 2006-2-2
    * @version 1.0
    */
    public class HeapSort implements SortUtil.Sort{

    ???? /* (non-Javadoc)
    ????? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
    ????? */
    ???? public void sort(int[] data) {
    ???????? MaxHeap h=new MaxHeap();
    ???????? h.init(data);
    ???????? for(int i=0;i<data.length;i++)
    ???????????? h.remove();
    ???????? System.arraycopy(h.queue,1,data,0,data.length);
    ???? }

    ????? private static class MaxHeap{?????????
    ????????
    ???????? void init(int[] data){
    ???????????? this.queue=new int[data.length+1];
    ???????????? for(int i=0;i<data.length;i++){
    ???????????????? queue[++size]=data[i];
    ???????????????? fixUp(size);
    ???????????? }
    ???????? }
    ?????????
    ???????? private int size=0;

    ???????? private int[] queue;
    ????????????????
    ???????? public int get() {
    ???????????? return queue[1];
    ???????? }

    ???????? public void remove() {
    ???????????? SortUtil.swap(queue,1,size--);
    ???????????? fixDown(1);
    ???????? }
    ???????? //fixdown
    ???????? private void fixDown(int k) {
    ???????????? int j;
    ???????????? while ((j = k << 1) <= size) {
    ???????????????? if (j < size && queue[j]<queue[j+1])
    ???????????????????? j++;
    ???????????????? if (queue[k]>queue[j]) //不用交換
    ???????????????????? break;
    ???????????????? SortUtil.swap(queue,j,k);
    ???????????????? k = j;
    ???????????? }
    ???????? }
    ???????? private void fixUp(int k) {
    ???????????? while (k > 1) {
    ???????????????? int j = k >> 1;
    ???????????????? if (queue[j]>queue[k])
    ???????????????????? break;
    ???????????????? SortUtil.swap(queue,j,k);
    ???????????????? k = j;
    ???????????? }
    ???????? }

    ???? }

    }

    posted @ 2007-06-10 18:47 jadmin 閱讀(77) | 評論 (0)編輯 收藏
    GCC

    GCC是GNU推出的Linux系統下C編譯器,下面主要介紹這種編譯器的基本原理和使用方法,以及編譯過程中所產生的錯誤的原因及對策。

    GCC簡介
    Linux系統下的gcc(GNU C Compiler)是GNU推出的功能強大、性能優越的多平臺編譯器,是GNU的代表作品之一。gcc是可以在多種硬體平臺上編譯出可執行程序的超級編譯器,其執行效率與一般的編譯器相比平均效率要高20%~30%。

    gcc編譯器能將C、C++語言源程序、匯程式化序和目標程序編譯、連接成可執行文件,如果沒有給出可執行文件的名字,gcc將生成一個名為a.out的文件。在Linux系統中,可執行文件沒有統一的后綴,系統從文件的屬性來區分可執行文件和不可執行文件。而gcc則通過后綴來區別輸入文件的類別,下面我們來介紹gcc所遵循的部分約定規則。

    .c為后綴的文件,C語言源代碼文件;

    .a為后綴的文件,是由目標文件構成的檔案庫文件;

    .C,.cc或.cxx 為后綴的文件,是C++源代碼文件;

    .h為后綴的文件,是程序所包含的頭文件;

    .i 為后綴的文件,是已經預處理過的C源代碼文件;

    .ii為后綴的文件,是已經預處理過的C++源代碼文件;

    .m為后綴的文件,是Objective-C源代碼文件;

    .o為后綴的文件,是編譯后的目標文件;

    .s為后綴的文件,是匯編語言源代碼文件;

    .S為后綴的文件,是經過預編譯的匯編語言源代碼文件。

    gcc的執行過程
    雖然我們稱gcc是C語言的編譯器,但使用gcc由C語言源代碼文件生成可執行文件的過程不僅僅是編譯的過程,而是要經歷四個相互關聯的步驟∶預處理(也稱預編譯,Preprocessing)、編譯(Compilation)、匯編(Assembly)和連接(Linking)。

    命令gcc首先調用cpp進行預處理,在預處理過程中,對源代碼文件中的文件包含(include)、預編譯語句(如宏定義define等)進行分析。接著調用cc1進行編譯,這個階段根據輸入文件生成以.o為后綴的目標文件。匯編過程是針對匯編語言的步驟,調用as進行工作,一般來講,.S為后綴的匯編語言源代碼文件和匯編、.s為后綴的匯編語言文件經過預編譯和匯編之后都生成以.o為后綴的目標文件。當所有的目標文件都生成之后,gcc就調用ld來完成最后的關鍵性工作,這個階段就是連接。在連接階段,所有的目標文件被安排在可執行程序中的恰當的位置,同時,該程序所調用到的庫函數也從各自所在的檔案庫中連到合適的地方。

    gcc的基本用法和選項
    在使用gcc編譯器的時候,我們必須給出一系列必要的調用參數和文件名稱。gcc編譯器的調用參數大約有100多個,其中多數參數我們可能根本就用不到,這里只介紹其中最基本、最常用的參數。

    gcc最基本的用法是∶gcc [options] [filenames]

    其中options就是編譯器所需要的參數,filenames給出相關的文件名稱。

    -c,只編譯,不連接成為可執行文件,編譯器只是由輸入的.c等源代碼文件生成.o為后綴的目標文件,通常用于編譯不包含主程序的子程序文件。

    -o output_filename,確定輸出文件的名稱為output_filename,同時這個名稱不能和源文件同名。如果不給出這個選項,gcc就給出預設的可執行文件a.out。

    -g,產生符號調試工具(GNU的gdb)所必要的符號資訊,要想對源代碼進行調試,我們就必須加入這個選項。

    -O,對程序進行優化編譯、連接,采用這個選項,整個源代碼會在編譯、連接過程中進行優化處理,這樣產生的可執行文件的執行效率可以提高,但是,編譯、連接的速度就相應地要慢一些。

    -O2,比-O更好的優化編譯、連接,當然整個編譯、連接過程會更慢。

    -Idirname,將dirname所指出的目錄加入到程序頭文件目錄列表中,是在預編譯過程中使用的參數。C程序中的頭文件包含兩種情況∶

    A)#include

    B)#include “myinc.h”

    其中,A類使用尖括號(< >),B類使用雙引號(“ ”)。對于A類,預處理程序cpp在系統預設包含文件目錄(如/usr/include)中搜尋相應的文件,而對于B類,cpp在當前目錄中搜尋頭文件,這個選項的作用是告訴cpp,如果在當前目錄中沒有找到需要的文件,就到指定的dirname目錄中去尋找。在程序設計中,如果我們需要的這種包含文件分別分布在不同的目錄中,就需要逐個使用-I選項給出搜索路徑。

    -Ldirname,將dirname所指出的目錄加入到程序函數檔案庫文件的目錄列表中,是在連接過程中使用的參數。在預設狀態下,連接程序ld在系統的預設路徑中(如/usr/lib)尋找所需要的檔案庫文件,這個選項告訴連接程序,首先到-L指定的目錄中去尋找,然后到系統預設路徑中尋找,如果函數庫存放在多個目錄下,就需要依次使用這個選項,給出相應的存放目錄。

    -lname,在連接時,裝載名字為“libname.a”的函數庫,該函數庫位于系統預設的目錄或者由-L選項確定的目錄下。例如,-lm表示連接名為“libm.a”的數學函數庫。

    上面我們簡要介紹了gcc編譯器最常用的功能和主要參數選項,更為詳盡的資料可以參看Linux系統的聯機幫助。

    假定我們有一個程序名為test.c的C語言源代碼文件,要生成一個可執行文件,最簡單的辦法就是∶

    gcc test.c

    這時,預編譯、編譯連接一次完成,生成一個系統預設的名為a.out的可執行文件,對于稍為復雜的情況,比如有多個源代碼文件、需要連接檔案庫或者有其他比較特別的要求,就要給定適當的調用選項參數。再看一個簡單的例子。

    整個源代碼程序由兩個文件testmain.c 和testsub.c組成,程序中使用了系統提供的數學庫,同時希望給出的可執行文件為test,這時的編譯命令可以是∶

    gcc testmain.c testsub.c □lm □o test

    其中,-lm表示連接系統的數學庫libm.a,這個過程可以用圖12-1框圖描述。


    gcc的錯誤類型及對策
    gcc編譯器如果發現源程序中有錯誤,就無法繼續進行,也無法生成最終的可執行文件。為了便于修改,gcc給出錯誤資訊,我們必須對這些錯誤資訊逐個進行分析、處理,并修改相應的語言,才能保證源代碼的正確編譯連接。gcc給出的錯誤資訊一般可以分為四大類,下面我們分別討論其產生的原因和對策。

    第一類∶C語法錯誤
    錯誤資訊∶文件source.c中第n行有語法錯誤(syntex errror)。這種類型的錯誤,一般都是C語言的語法錯誤,應該仔細檢查源代碼文件中第n行及該行之前的程序,有時也需要對該文件所包含的頭文件進行檢查。有些情況下,一個很簡單的語法錯誤,gcc會給出一大堆錯誤,我們最主要的是要保持清醒的頭腦,不要被其嚇倒,必要的時候再參考一下C語言的基本教材。

    第二類∶頭文件錯誤
    錯誤資訊∶找不到頭文件head.h(Can not find include file head.h)。這類錯誤是源代碼文件中的包含頭文件有問題,可能的原因有頭文件名錯誤、指定的頭文件所在目錄名錯誤等,也可能是錯誤地使用了雙引號和尖括號。

    第三類∶檔案庫錯誤
    錯誤資訊∶連接程序找不到所需的函數庫,例如∶

    ld: -lm: No such file or directory

    這類錯誤是與目標文件相連接的函數庫有錯誤,可能的原因是函數庫名錯誤、指定的函數庫所在目錄名稱錯誤等,檢查的方法是使用find命令在可能的目錄中尋找相應的函數庫名,確定檔案庫及目錄的名稱并修改程序中及編譯選項中的名稱。

    第四類∶未定義符號
    錯誤資訊∶有未定義的符號(Undefined symbol)。這類錯誤是在連接過程中出現的,可能有兩種原因∶一是使用者自己定義的函數或者全局變量所在源代碼文件,沒有被編譯、連接,或者干脆還沒有定義,這需要使用者根據實際情況修改源程序,給出全局變量或者函數的定義體;二是未定義的符號是一個標準的庫函數,在源程序中使用了該庫函數,而連接過程中還沒有給定相應的函數庫的名稱,或者是該檔案庫的目錄名稱有問題,這時需要使用檔案庫維護命令ar檢查我們需要的庫函數到底位于哪一個函數庫中,確定之后,修改gcc連接選項中的-l和-L項。

    排除編譯、連接過程中的錯誤,應該說這只是程序設計中最簡單、最基本的一個步驟,可以說只是開了個頭。這個過程中的錯誤,只是我們在使用C語言描述一個算法中所產生的錯誤,是比較容易排除的。我們寫一個程序,到編譯、連接通過為止,應該說剛剛開始,程序在運行過程中所出現的問題,是算法設計有問題,說得更玄點是對問題的認識和理解不夠,還需要更加深入地測試、調試和修改。一個程序,稍為復雜的程序,往往要經過多次的編譯、連接和測試、修改。下面我們學習的程序維護、調試工具和版本維護就是在程序調試、測試過程中使用的,用來解決調測階段所出現的問題。

    posted @ 2007-06-10 02:16 jadmin 閱讀(51) | 評論 (0)編輯 收藏
    僅列出標題
    共50頁: First 上一頁 36 37 38 39 40 41 42 43 44 下一頁 Last 
    主站蜘蛛池模板: 亚洲国产美女视频| 亚洲成人精品久久| 蜜桃传媒一区二区亚洲AV| 色影音免费色资源| 亚洲中文字幕久在线| 性短视频在线观看免费不卡流畅| 亚洲精品**中文毛片| 麻豆国产精品免费视频| 亚洲国产成人久久| 好吊妞788免费视频播放| 久久亚洲中文字幕无码| 男人的天堂亚洲一区二区三区 | 免费无码成人AV片在线在线播放| 亚洲国产成人无码av在线播放| 两性刺激生活片免费视频| 久久久久se色偷偷亚洲精品av | 亚洲线精品一区二区三区| 中国videos性高清免费| 亚洲电影一区二区三区| 一二三四在线观看免费高清中文在线观看 | 偷自拍亚洲视频在线观看99| 国产免费人视频在线观看免费| 新最免费影视大全在线播放| 亚洲国产a∨无码中文777| 13一14周岁毛片免费| 亚洲国产成人久久精品大牛影视| 四虎免费影院4hu永久免费| 中国黄色免费网站| 亚洲国产精品综合久久网各| 浮力影院第一页小视频国产在线观看免费 | 久久精品视频免费| 久久久国产亚洲精品| 亚洲真人日本在线| xxxxwww免费| 成人a毛片视频免费看| 91精品国产亚洲爽啪在线影院 | 亚洲国产无线乱码在线观看| 国产成人亚洲综合| 57PAO成人国产永久免费视频| 老司机午夜性生免费福利| 久久丫精品国产亚洲av|