青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

JAVA中的指針,引用及對象的clone

2002 12 23

Java語言的一個優點就是取消了指針的概念,但也導致了許多程序員在編程中常常忽略了對象與引用的區別,本文會試圖澄清這一概念。并且由于Java不能通過簡單的賦值來解決對象復制的問題,在開發過程中,也常常要要應用clone()方法來復制對象。本文會讓你了解什么是影子clone與深度clone,認識它們的區別、優點及缺點。

 

看到這個標題,是不是有點困惑:Java語言明確說明取消了指針,因為指針往往是在帶來方便的同時也是導致代碼不安全的根源,同時也會使程序的變得非常復雜難以理解,濫用指針寫成的代碼不亞于使用早已臭名昭著的"GOTO"語句。Java放棄指針的概念絕對是極其明智的。但這只是在Java語言中沒有明確的指針定義,實質上每一個new語句返回的都是一個指針的引用,只不過在大多時候Java中不用關心如何操作這個"指針",更不用象在操作C++的指針那樣膽戰心驚。唯一要多多關心的是在給函數傳遞對象的時候。如下例程:

 

package reference;
class Obj{
    String str = "init value";
    public String toString(){
        return str;
    }
}
public class ObjRef{
    Obj aObj = new Obj(); //定義時候即new
    int aInt = 11;
    public void changeObj(Obj inObj){
        inObj.str = "changed value";
    }
    public void changePri(int inInt){
        inInt = 22;
    }
    public static void main(String[] args) 
    {
        ObjRef oRef = new ObjRef();
        System.out.println("Before call changeObj() method: " + oRef.aObj);
        oRef.changeObj(oRef.aObj);//---- 對象,傳引用
        System.out.println("After call changeObj() method: " + oRef.aObj);
        System.out.println("==================Print Primtive=================");
        System.out.println("Before call changePri() method: " + oRef.aInt);
        oRef.changePri(oRef.aInt);//---- 內置類型,傳值
        System.out.println("After call changePri() method: " + oRef.aInt);
    }
}
/* RUN RESULT
Before call changeObj() method: init value
After call changeObj() method: changed value
==================Print Primtive=================
Before call changePri() method: 11
After call changePri() method: 11
*
*/

 

這段代碼的主要部分調用了兩個很相近的方法,changeObj()changePri()。唯一不同的是它們一個把對象作為輸入參數,另一個把Java中的基本類型int作為輸入參數。并且在這兩個函數體內部都對輸入的參數進行了改動??此埔粯拥姆椒ǎ绦蜉敵龅慕Y果卻不太一樣。changeObj()方法真正的把輸入的參數改變了,而changePri()方法對輸入的參數沒有任何的改變。

 

從這個例子知道Java對對象和基本的數據類型的處理是不一樣的。C語言一樣,當把Java的基本數據類型(如intchar,double等)作為入口參數傳給函數體的時候,傳入的參數在函數體內部變成了局部變量,這個局部變量是輸入參數的一個拷貝,所有的函數體內部的操作都是針對這個拷貝的操作,函數執行結束后,這個局部變量也就完成了它的使命,它影響不到作為輸入參數的變量。這種方式的參數傳遞被稱為"值傳遞"。而在Java中用對象的作為入口參數的傳遞則缺省為"引用傳遞",也就是說僅僅傳遞了對象的一個"引用",這個"引用"的概念同C語言中的指針引用是一樣的。當函數體內部對輸入變量改變時,實質上就是在對這個對象的直接操作。

 

除了在函數傳值的時候是"引用傳遞",在任何用""向對象變量賦值的時候都是"引用傳遞"。如:

package reference;
class PassObj
{
    String str = "init value";
}
public class ObjPassValue 
{
    public static void main(String[] args) 
    {
        PassObj objA = new PassObj();
        PassObj objB = objA;
        objA.str = "changed in objA";
        System.out.println("Print objB.str value: " + objB.str);
    }
}
/* RUN RESULT
Print objB.str value: changed in objA
*/

 

第一句是在內存中生成一個新的PassObj對象,然后把這個PassObj的引用賦給變量objA,第二句是把PassObj對象的引用又賦給了變量objB。此時objAobjB是兩個完全一致的變量,以后任何對objA的改變都等同于對objB的改變。

即使明白了Java語言中的"指針"概念也許還會不經意間犯下面的錯誤。

 

l     Hashtable真的能存儲對象嗎?

看一看下面的很簡單的代碼,先是聲明了一個HashtableStringBuffer對象,然后分四次把StriingBuffer對象放入到Hashtable表中,在每次放入之前都對這個StringBuffer對象append()了一些新的字符串:

package reference;
import java.util.*;
public class HashtableAdd{
    public static void main(String[] args){
        Hashtable ht = new Hashtable();
        StringBuffer sb = new StringBuffer();
        sb.append("abc,");
        ht.put("1",sb);     
        sb.append("def,");
        ht.put("2",sb);
        sb.append("mno,");
        ht.put("3",sb);
        sb.append("xyz.");
        ht.put("4",sb);
        
        int numObj=0;
        Enumeration it = ht.elements();
        while(it.hasMoreElements()){
            System.out.print("get StringBufffer "+(++numObj)+" from Hashtable: ");
            System.out.println(it.nextElement());
        }
    }
}

 

如果你認為輸出的結果是:
get StringBufffer 1 from Hashtable: abc,
get StringBufffer 2 from Hashtable: abc,def
,
get StringBufffer 3 from Hashtable: abc,def,mno,
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.

 

那么你就要回過頭再仔細看一看上一個問題了,把對象時作為入口參數傳給函數,實質上是傳遞了對象的引用,向Hashtable傳遞StringBuffer對象也是只傳遞了這個StringBuffer對象的引用!每一次向Hashtable表中put一次StringBuffer,并沒有生成新的StringBuffer對象,只是在Hashtable表中又放入了一個指向同一StringBuffer對象的引用而已。

 

Hashtable表存儲的任何一個StringBuffer對象(更確切的說應該是對象的引用)的改動,實際上都是對同一個"StringBuffer"的改動。所以Hashtable并不能真正存儲能對象,而只能存儲對象的引用。也應該知道這條原則對與Hashtable相似的Vector, List, Map, Set等都是一樣的。

 

上面的例程的實際輸出的結果是:

/* RUN RESULT
get StringBufffer 1 from Hashtable: abc,def,mno,xyz.
get StringBufffer 2 from Hashtable: abc,def,mno,xyz.
get StringBufffer 3 from Hashtable: abc,def,mno,xyz.
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.
*/

 

l     類,對象與引用

Java最基本的概念就是類,類包括函數和變量。如果想要應用類,就要把類生成對象,這個過程被稱作"類的實例化"。有幾種方法把類實例化成對象,最常用的就是用"new"操作符類實例化成對象后,就意味著要在內存中占據一塊空間存放實例。想要對這塊空間操作就要應用到對象的引用。引用在Java語言中的體現就是變量,而變量的類型就是這個引用的對象。雖然在語法上可以在生成一個對象后直接調用該對象的函數或變量,如:

new String("Hello NDP")).substring(0,3)  //RETURN RESULT: Hel

 

但由于沒有相應的引用,對這個對象的使用也只能局限這條語句中了。(回立即銷毀嗎?內存回收機制是不是使用了引用計數的方式呢?

  1. 產生:引用總是在把對象作參數"傳遞"的過程中自動發生,不需要人為的產生,也不能人為的控制引用的產生。這個傳遞包括把對象作為函數的入口參數的情況,也包括用""進行對象賦值的時候。
  2. 范圍:只有局部的引用,沒有局部的對象。引用在Java語言的體現就是變量,而變量在Java語言中是有范圍的,可以是局部的,也可以是全局的。
  3. 生存期:程序只能控制引用的生存周期。對象的生存期是由Java控制。用"new Object()"語句生成一個新的對象,是在計算機的內存中聲明一塊區域存儲對象,只有Java的垃圾收集器才能決定在適當的時候回收對象占用的內存
  4. 沒有辦法阻止對引用的改動。

 

l     什么是"clone"?

在實際編程過程中,我們常常要遇到這種情況:有一個對象A,在某一時刻A中已經包含了一些有效值,此時可能會需要一個和A完全相同新對象B,并且此后對B任何改動都不會影響到A中的值,也就是說,AB是兩個獨立的對象,但B的初始值是由A對象確定的。在Java語言中,用簡單的賦值語句是不能滿足這種需求的。要滿足這種需求雖然有很多途徑,但實現clone()方法是其中最簡單,也是最高效的手段。

 

Java的所有類都默認繼承java.lang.Object類,在java.lang.Object類中有一個方法clone()。JDK API的說明文檔解釋這個方法將返回Object對象的一個拷貝。要說明的有兩點:一是拷貝對象返回的是一個新對象,而不是一個引用。二是拷貝對象與用new操作符返回的新對象的區別就是這個拷貝已經包含了一些原來對象的信息,而不是對象的初始信息。

 

l     怎樣應用clone()方法?

 

一個很典型的調用clone()代碼如下:

class CloneClass implements Cloneable{
    public int aInt;
    public Object clone(){
        CloneClass o = null;
        try{
            o = (CloneClass)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }

 

有三個值得注意的地方,一是希望能實現clone功能的CloneClass類實現了Cloneable接口,這個接口屬于java.lang包,java.lang包已經被缺省的導入類中,所以不需要寫成java.lang.Cloneable。另一個值得請注意的是重載了clone()方法。最后在clone()方法中調用了super.clone(),這也意味著無論clone類的繼承結構是什么樣的,super.clone()直接或間接調用了java.lang.Object類的clone()方法。下面再詳細的解釋一下這幾點。

 

應該說第三點是最重要的,仔細觀察一下Object類的clone()一個native方法,native方法的效率一般來說都是遠高于java中的非native方法。這也解釋了為什么要用Objectclone()方法而不是先new一個類,然后把原始對象中的信息賦到新對象中,雖然這也實現了clone功能。對于第二點,也要觀察Object類中的clone()還是一個protected屬性的方法。這也意味著如果要應用clone()方法,必須繼承Object類,在Java中所有的類是缺省繼承Object類的,也就不用關心這點了。然后重載clone()方法。還有一點要考慮的是為了讓其它類能調用這個clone類的clone()方法,重載之后要把clone()方法的屬性設置為public。

 

那么clone類為什么還要實現Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其實這個接口僅僅是一個標志,而且這個標志也僅僅是針對Object類中clone()方法的,如果clone類沒有實現Cloneable接口,并調用了Objectclone()方法(也就是調用了super.Clone()方法),那么Objectclone()方法就會拋出CloneNotSupportedException異常。

 

以上是clone的最基本的步驟,想要完成一個成功的clone,還要了解什么是"影子clone""深度clone"

 

l     什么是影子clone?

下面的例子包含三個類UnCloneA,CloneB,CloneMain。CloneB類包含了一個UnCloneA的實例和一個int類型變量,并且重載clone()方法。CloneMain類初始化UnCloneA類的一個實例b1,然后調用clone()方法生成了一個b1的拷貝b2。最后考察一下b1b2的輸出:

 

package clone;
class UnCloneA {
    private int i;
    public UnCloneA(int ii) { i = ii; }
    public void doubleValue() { i *= 2; }
    public String toString() {
        return Integer.toString(i);
    }
}
class CloneB implements Cloneable{
    public int aInt;
    public UnCloneA unCA = new UnCloneA(111);
    public Object clone(){
        CloneB o = null;
        try{
            o = (CloneB)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}
public class CloneMain {
    public static void main(String[] a){
        CloneB b1 = new CloneB();
        b1.aInt = 11;
        System.out.println("before clone,b1.aInt = "+ b1.aInt);
        System.out.println("before clone,b1.unCA = "+ b1.unCA);
                
        CloneB b2 = (CloneB)b1.clone();
        b2.aInt = 22;
        b2.unCA.doubleValue();
        System.out.println("=================================");
        System.out.println("after clone,b1.aInt = "+ b1.aInt);
        System.out.println("after clone,b1.unCA = "+ b1.unCA);
        System.out.println("=================================");
        System.out.println("after clone,b2.aInt = "+ b2.aInt);
        System.out.println("after clone,b2.unCA = "+ b2.unCA);
    }
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 222
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/

 

輸出的結果說明int類型的變量aIntUnCloneA的實例對象unCAclone結果不一致,int類型是真正的被clone了,因為改變了b2中的aInt變量,對b1aInt沒有產生影響,也就是說,b2.aIntb1.aInt已經占據了不同的內存空間,b2.aIntb1.aInt的一個真正拷貝。相反,對b2.unCA的改變同時改變了b1.unCA,很明顯,b2.unCAb1.unCA是僅僅指向同一個對象的不同引用!從中可以看出,調用Object類中clone()方法產生的效果是:先在內存中開辟一塊和原始對象一樣的空間,然后原樣拷貝原始對象中的內容。對基本數據類型,這樣的操作是沒有問題的,但對非基本類型變量,我們知道它們保存的僅僅是對象的引用,這也導致clone后的非基本類型變量和原始對象中相應的變量指向的是同一個對象。

 

大多時候,這種clone的結果往往不是我們所希望的結果,這種clone也被稱為"影子clone"。要想讓b2.unCA指向與b2.unCA不同的對象,而且b2.unCA中還要包含b1.unCA中的信息作為初始信息,就要實現深度clone。

 

l     怎么進行深度clone?

把上面的例子改成深度clone很簡單,需要兩個改變:一是讓UnCloneA類也實現和CloneB類一樣的clone功能(實現Cloneable接口,重載clone()方法)。二是在CloneBclone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();

程序如下:

package clone.ext;
class UnCloneA implements Cloneable{
    private int i;
    public UnCloneA(int ii) { i = ii; }
    public void doubleValue() { i *= 2; }
    public String toString() {
        return Integer.toString(i);
    }
    public Object clone(){
        UnCloneA o = null;
        try{
            o = (UnCloneA)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}
class CloneB implements Cloneable{
    public int aInt;
    public UnCloneA unCA = new UnCloneA(111);
    public Object clone(){
        CloneB o = null;
        try{
            o = (CloneB)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        o.unCA = (UnCloneA)unCA.clone();
        return o;
    }
}
public class CloneMain {
    public static void main(String[] a){
        CloneB b1 = new CloneB();
        b1.aInt = 11;
        System.out.println("before clone,b1.aInt = "+ b1.aInt);
        System.out.println("before clone,b1.unCA = "+ b1.unCA);
                
        CloneB b2 = (CloneB)b1.clone();
        b2.aInt = 22;
        b2.unCA.doubleValue();
        System.out.println("=================================");
        System.out.println("after clone,b1.aInt = "+ b1.aInt);
        System.out.println("after clone,b1.unCA = "+ b1.unCA);
        System.out.println("=================================");
        System.out.println("after clone,b2.aInt = "+ b2.aInt);
        System.out.println("after clone,b2.unCA = "+ b2.unCA);
    }
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 111
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/

 

可以看出,現在b2.unCA的改變對b1.unCA沒有產生影響。此時b1.unCAb2.unCA指向了兩個不同的UnCloneA實例,而且在CloneB b2 = (CloneB)b1.clone();調用的那一刻b1b2擁有相同的值,在這里,b1.i = b2.i = 11

 

要知道不是所有的類都能實現深度clone的。例如,如果把上面的CloneB類中的UnCloneA類型變量改成StringBuffer類型,看一下JDK API中關于StringBuffer的說明,StringBuffer沒有重載clone()方法,更為嚴重的是StringBuffer還是一個final,這也是說我們也不能用繼承的辦法間接實現StringBufferclone。如果一個類中包含有StringBuffer類型對象或和StringBuffer相似類的對象,我們有兩種選擇:要么只能實現影子clone,要么就在類的clone()方法中加一句(假設是SringBuffer對象,而且變量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原來的是:o.unCA = (UnCloneA)unCA.clone();

 

還要知道的是除了基本數據類型能自動實現深度clone以外,String對象是一個例外,它clone后的表現好象也實現了深度clone,雖然這只是一個假象,但卻大大方便了我們的編程。

 

l     CloneStringStringBuffer的區別

應該說明的是,這里不是著重說明StringStringBuffer的區別,但從這個例子里也能看出String類的一些與眾不同的地方。

 

下面的例子中包括兩個類,CloneC類包含一個String類型變量和一個StringBuffer類型變量,并且實現了clone()方法。在StrClone類中聲明了CloneC類型變量c1,然后調用c1clone()方法生成c1的拷貝c2,在對c2中的StringStringBuffer類型變量用相應的方法改動之后打印結果:

package clone;
class CloneC implements Cloneable{
    public String str;
    public StringBuffer strBuff;
    public Object clone(){
        CloneC o = null;
        try{
            o = (CloneC)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
    
}
public class StrClone {
    public static void main(String[] a){
        CloneC c1 = new CloneC();
        c1.str = new String("initializeStr");
        c1.strBuff = new StringBuffer("initializeStrBuff");
        System.out.println("before clone,c1.str = "+ c1.str);
        System.out.println("before clone,c1.strBuff = "+ c1.strBuff);
                
        CloneC c2 = (CloneC)c1.clone();
        c2.str = c2.str.substring(0,5);
        c2.strBuff = c2.strBuff.append(" change strBuff clone");
        System.out.println("=================================");
        System.out.println("after clone,c1.str = "+ c1.str);
        System.out.println("after clone,c1.strBuff = "+ c1.strBuff);
        System.out.println("=================================");
        System.out.println("after clone,c2.str = "+ c2.str);
        System.out.println("after clone,c2.strBuff = "+ c2.strBuff);
    }
}
/* RUN RESULT
before clone,c1.str = initializeStr
before clone,c1.strBuff = initializeStrBuff
=================================
after clone,c1.str = initializeStr
after clone,c1.strBuff = initializeStrBuff change strBuff clone
=================================
after clone,c2.str = initi
after clone,c2.strBuff = initializeStrBuff change strBuff clone
*
*/

 

打印的結果可以看出,String類型的變量好象已經實現了深度clone,因為對c2.str的改動并沒有影響到c1.str!難道JavaSring類看成了基本數據類型?其實不然,這里有一個小小的把戲,秘密就在于c2.str = c2.str.substring(0,5)這一語句!實質上,在clone的時候c1.strc2.str仍然是引用,而且都指向了同一個String對象。但在執行c2.str = c2.str.substring(0,5)的時候,它作用相當于生成了一個新的String類型,然后又賦回給c2.str。這是因為StringSun公司的工程師寫成了一個不可更改的類(immutable class),在所有String類中的函數都不能更改自身的值。下面給出很簡單的一個例子:

 

package clone;

public class StrTest { public static void main(String[] args) {

String str1 = "This is a test for immutable";

String str2 = str1.substring(0,8);

System.out.println("print str1 : " + str1);

System.out.println("print str2 : " + str2); }

 } /* RUN RESULT print str1 : This is a test for immutable print str2 : This is */

 

例子中,雖然str1調用了substring()方法,但str1的值并沒有改變。類似的,String類中的其它方法也是如此。當然如果我們把最上面的例子中的這兩條語句

 

c2.str = c2.str.substring(0,5);
c2.strBuff = c2.strBuff.append(" change strBuff clone");
       

 

改成下面這樣:

c2.str.substring(0,5);
c2.strBuff.append(" change strBuff clone");

 

去掉了重新賦值的過程,c2.str也就不能有變化了,我們的把戲也就露餡了。但在編程過程中只調用

c2.str.substring(0,5);

 

語句是沒有任何意義的。

應該知道的是在Java中所有的基本數據類型都有一個相對應的類,Integer類對應int類型,Double類對應double類型等等,這些類也String類相同,都是不可以改變的類。也就是說,這些的類中的所有方法都是不能改變其自身的值的。這也讓我們在編clone類的時候有了一個更多的選擇。同時我們也可以把自己的類編成不可更改的類。

 

關于作者

倪大鵬,有五年的軟件開發經驗,其中的近四年時間里是在從事與Java相關技術的應用與開發。你可以通過e-mail: ndp@21cn.com與他聯系。

posted on 2009-07-24 13:17 肥仔 閱讀(239) 評論(0)  編輯 收藏 引用 所屬分類: Web-后臺

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美在线高清| 午夜精品www| 亚洲一区二区免费| 精品成人在线观看| 国产精品高潮久久| 欧美视频一区二区三区…| 欧美在线亚洲在线| 欧美在线一二三| 免费日韩av电影| 欧美国产日本高清在线| 欧美金8天国| 国产精品成人一区二区艾草| 香港久久久电影| 亚洲国产另类精品专区| 亚洲二区精品| 亚洲一区二区精品在线| 亚洲欧美韩国| 欧美1级日本1级| 国产精品高潮视频| 亚洲第一网站| 午夜在线视频一区二区区别| 麻豆成人精品| 亚洲免费中文| 欧美午夜大胆人体| 亚洲欧洲日韩在线| 久久精品女人的天堂av| 一本色道久久综合狠狠躁的推荐| 欧美亚洲免费高清在线观看| 国产主播精品| 欧美婷婷在线| 久久久国产精彩视频美女艺术照福利 | 欧美精品一区在线观看| 日韩视频免费看| 亚洲第一黄色| 韩国福利一区| 国产精品裸体一区二区三区| 一本一本久久a久久精品综合妖精| 亚洲人成网站777色婷婷| 欧美午夜不卡| 老色鬼久久亚洲一区二区| 欧美在线免费视屏| 久久久噜噜噜久久中文字免| 亚洲免费人成在线视频观看| 亚洲欧美日韩精品久久久久| 亚洲欧美日本伦理| 久久精品99国产精品| 免费观看在线综合| 欧美wwwwww| 国产日韩在线亚洲字幕中文| 伊人久久av导航| 亚洲毛片在线| 蜜桃av一区二区| 午夜伦欧美伦电影理论片| 久久久久免费| 欧美日韩综合视频网址| 亚洲国产高清一区| 久久激情视频| 99精品免费| 欧美日韩三区| 亚洲欧美成aⅴ人在线观看| 99精品视频一区二区三区| 亚洲一区二区不卡免费| 欧美成人影音| 亚洲区在线播放| 国产毛片久久| 亚洲精品美女久久久久| 噜噜噜91成人网| 玖玖玖免费嫩草在线影院一区| 国产精品国产三级国产aⅴ入口 | 狠狠干综合网| 久久免费午夜影院| 久久久亚洲国产天美传媒修理工| 国模大胆一区二区三区| 欧美高清视频一区二区| 欧美日韩不卡视频| 午夜精品成人在线| 久热精品视频| 久久激情久久| 欧美激情免费观看| 久久国产免费| 欧美日韩一区精品| 亚洲第一天堂无码专区| 美乳少妇欧美精品| 欧美一区二区三区视频免费播放| 久久激情综合| 亚洲在线一区| 欧美日韩国产影院| 欧美激情在线播放| 国内伊人久久久久久网站视频| 欧美11—12娇小xxxx| 欧美精品一区二区视频| 久久久久久香蕉网| 欧美黄色网络| 亚洲高清123| 国产精品自拍一区| 亚洲性感美女99在线| 日韩写真视频在线观看| 久久五月天婷婷| 久久免费少妇高潮久久精品99| 亚洲免费av观看| 免费中文日韩| 在线亚洲免费视频| 亚洲开发第一视频在线播放| 久久久精品动漫| 久久亚洲色图| 亚洲精品中文字幕在线观看| 欧美国产精品v| 亚洲国产高清aⅴ视频| 亚洲人成在线观看| 欧美激情五月| 亚洲欧美一区二区原创| 亚洲五月婷婷| 国内精品视频一区| 久久久精品视频成人| 欧美成人亚洲成人| 亚洲经典在线看| 国产精品日韩一区| 亚洲一区观看| 欧美福利电影在线观看| 夜夜嗨av一区二区三区免费区| 国产精品高潮呻吟久久| 日韩视频二区| 久久视频精品在线| 亚洲一区二区三区欧美| 国产综合色产| 国产色综合久久| 久久午夜激情| 欧美一区二区三区日韩| 亚洲国产天堂久久综合| 久久精品视频va| 亚洲免费成人av电影| 黄色精品一区二区| 国产欧美一区二区白浆黑人| 国产精品日韩欧美综合| 欧美激情一区二区久久久| 欧美一区二区三区久久精品茉莉花| 亚洲福利在线观看| 国产精品第2页| 欧美日韩精品久久| 免费在线观看成人av| 欧美成熟视频| 久久综合伊人77777| 欧美成人日本| 国产精品视频男人的天堂| 国产一区二区三区免费观看| 欧美—级高清免费播放| 欧美激情一区二区三区在线| 欧美日韩高清一区| 欧美日韩国产综合视频在线| 欧美日韩一区二区视频在线| 免费在线亚洲欧美| 国产精品久久久久久久久果冻传媒 | 欧美成人午夜视频| 免费不卡亚洲欧美| 亚洲美女黄色片| 亚洲欧美综合精品久久成人 | 免费成人美女女| 91久久亚洲| 午夜精品久久久久久久99水蜜桃| 免费视频一区| 国产毛片一区二区| 亚洲精品日韩久久| 久久久福利视频| 91久久线看在观草草青青| 一本一本a久久| 久久九九久精品国产免费直播| 欧美在线一区二区三区| 欧美精品一区二区在线观看| 国产日韩欧美高清| 欧美一级视频| 欧美一级视频免费在线观看| 欧美日韩国产电影| 在线观看视频欧美| 国产精品爽黄69| 在线观看欧美成人| 久久婷婷影院| 麻豆精品国产91久久久久久| 国产精品国产三级国产| 亚洲黄色av一区| 蜜臀av一级做a爰片久久| 亚洲欧美国产一区二区三区| 欧美午夜不卡| 亚洲欧美国产另类| 欧美亚洲一区三区| 在线视频观看日韩| 免费久久99精品国产自在现线| 亚洲影院色在线观看免费| 国产精品一区二区久久久| 亚洲欧美视频在线观看视频| 亚洲作爱视频| 国产偷国产偷精品高清尤物| 久久蜜桃资源一区二区老牛| 久久综合伊人77777蜜臀| 亚洲国产欧美一区二区三区同亚洲 | 欧美风情在线观看| 亚洲免费在线观看视频| 亚洲精品国产品国语在线app| 国产精品av免费在线观看| 欧美ed2k| 国产免费成人|