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

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

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

    內(nèi)部類(lèi)的作用

    轉(zhuǎn)貼:
    Java內(nèi)部類(lèi)是Java言語(yǔ)的一個(gè)很重要的概念,《Java編程思想》花了很大的篇幅來(lái)講述這個(gè)概念。但是我們?cè)趯?shí)踐中很少用到它,雖然我們?cè)诤芏鄷r(shí)候會(huì)被動(dòng)的使用到它,但它仍然像一個(gè)幕后英雄一樣,不為我們所知,不為我們所用。
    本文不試圖來(lái)講述Java內(nèi)部類(lèi)的今生前世、來(lái)龍去脈,這些在網(wǎng)絡(luò)上都已經(jīng)汗牛充棟。如果讀者想了解這些,可以在網(wǎng)絡(luò)上搜索來(lái)學(xué)習(xí)。Java內(nèi)部類(lèi)總是躲在它的外部類(lèi)里,像一個(gè)幕后英雄一樣。但是幕后英雄也有用武之地,在很多時(shí)候,恰當(dāng)?shù)氖褂肑ava內(nèi)部類(lèi)能起到讓人拍案叫絕的作用。本文試圖談一談讓這個(gè)幕后英雄也有用武之地的四個(gè)場(chǎng)景,希望引起大家對(duì)使用Java內(nèi)部類(lèi)的興趣。
    以下的文字,要求大家熟悉Java內(nèi)部類(lèi)的概念后來(lái)閱讀。
     
     
    場(chǎng)景一:當(dāng)某個(gè)類(lèi)除了它的外部類(lèi),不再被其他的類(lèi)使用時(shí)
    我們說(shuō)這個(gè)內(nèi)部類(lèi)依附于它的外部類(lèi)而存在,可能的原因有:1、不可能為其他的類(lèi)使用;2、出于某種原因,不能被其他類(lèi)引用,可能會(huì)引起錯(cuò)誤。等等。這個(gè)場(chǎng)景是我們使用內(nèi)部類(lèi)比較多的一個(gè)場(chǎng)景。下面我們以一個(gè)大家熟悉的例子來(lái)說(shuō)明。
    在我們的企業(yè)級(jí)Java項(xiàng)目開(kāi)發(fā)過(guò)程中,數(shù)據(jù)庫(kù)連接池是一個(gè)我們經(jīng)常要用到的概念。雖然在很多時(shí)候,我們都是用的第三方的數(shù)據(jù)庫(kù)連接池,不需要我們親自來(lái)做這個(gè)數(shù)據(jù)庫(kù)連接池。但是,作為我們Java內(nèi)部類(lèi)使用的第一個(gè)場(chǎng)景,這個(gè)數(shù)據(jù)庫(kù)連接池是一個(gè)很好的例子。為了簡(jiǎn)單起見(jiàn),以下我們就來(lái)簡(jiǎn)單的模擬一下數(shù)據(jù)庫(kù)連接池,在我們的例子中,我們只實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池的一些簡(jiǎn)單的功能。如果想完全實(shí)現(xiàn)它,大家不妨自己試一試。
    首先,我們定義一個(gè)接口,將數(shù)據(jù)庫(kù)連接池的功能先定義出來(lái),如下:
    public interface Pool extends TimerListener
    {
            //初始化連接池
            public boolean init();
            //銷(xiāo)毀連接池
            public void destory();
            //取得一個(gè)連接
            public Connection getConn();
            //還有一些其他的功能,這里不再列出
            ……
    }
    有了這個(gè)功能接口,我們就可以在它的基礎(chǔ)上實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池的部分功能了。我們首先想到這個(gè)數(shù)據(jù)庫(kù)連接池類(lèi)的操作對(duì)象應(yīng)該是由Connection對(duì)象組成的一個(gè)數(shù)組,既然是數(shù)組,我們的池在取得Connection的時(shí)候,就要對(duì)數(shù)組元素進(jìn)行遍歷,看看Connection對(duì)象是否已經(jīng)被使用,所以數(shù)組里每一個(gè)Connection對(duì)象都要有一個(gè)使用標(biāo)志。我們?cè)賹?duì)連接池的功能進(jìn)行分析,會(huì)發(fā)現(xiàn)每一個(gè)Connection對(duì)象還要一個(gè)上次訪問(wèn)時(shí)間和使用次數(shù)。
    通過(guò)上面的分析,我們可以得出,連接池里的數(shù)組的元素應(yīng)該是由對(duì)象組成,該對(duì)象的類(lèi)可能如下:
    public class PoolConn
    {
            private Connection conn;
            private boolean isUse;
            private long lastAccess;
            private int useCount;
            ……
    }
    下面的省略號(hào)省掉的是關(guān)于四個(gè)屬性的一些get和set方法。我們可以看到這個(gè)類(lèi)的核心就是Connection,其他的一些屬性都是Connection的一些標(biāo)志。可以說(shuō)這個(gè)類(lèi)只有在連接池這個(gè)類(lèi)里有用,其他地方用不到。這時(shí)候,我們就該考慮是不是可以把這個(gè)類(lèi)作為一個(gè)內(nèi)部類(lèi)呢?而且我們把它作為一個(gè)內(nèi)部類(lèi)以后,可以把它定義成一個(gè)私有類(lèi),然后將它的屬性公開(kāi),這樣省掉了那些無(wú)謂的get和set方法。下面我們就試試看:
    public class ConnectPool implements Pool
    {
            //存在Connection的數(shù)組
            private PoolConn[] poolConns;
            //連接池的最小連接數(shù)
            private int min;
            //連接池的最大連接數(shù)
            private int max;
            //一個(gè)連接的最大使用次數(shù)
            private int maxUseCount;
            //一個(gè)連接的最大空閑時(shí)間
            private long maxTimeout;
            //同一時(shí)間的Connection最大使用個(gè)數(shù)
            private int maxConns;
            //定時(shí)器
            private Timer timer;
            public boolean init()
            {
                   try
                   {
                          ……
                          this.poolConns = new PoolConn[this.min];
                          for(int i=0;i<this.min;i++)
                          {
                                 PoolConn poolConn = new PoolConn();
                                 poolConn.conn = ConnectionManager.getConnection();
                                 poolConn.isUse = false;
                                 poolConn.lastAccess = new Date().getTime();
                                 poolConn.useCount = 0;
                                 this.poolConns[i] = poolConn;
    }
    ……
    return true;
                   }
                   catch(Exception e)
                   {
                          return false;
    }
    }
    ……
    private class PoolConn
    {
           public Connection conn;
           public boolean isUse;
    public long lastAccess;
           public int useCount;
    }
    }
    因?yàn)楸疚牟皇菍?zhuān)題來(lái)講述數(shù)據(jù)庫(kù)連接池的,所以在上面的代碼中絕大部分的內(nèi)容被省略掉了。PoolConn類(lèi)不大可能被除了ConnectionPool類(lèi)的其他類(lèi)使用到,把它作為ConnectionPool的私有內(nèi)部類(lèi)不會(huì)影響到其他類(lèi)。同時(shí),我們可以看到,使用了內(nèi)部類(lèi),使得我們可以將該內(nèi)部類(lèi)的數(shù)據(jù)公開(kāi),ConnectionPool類(lèi)可以直接操作PoolConn類(lèi)的數(shù)據(jù)成員,避免了因set和get方法帶來(lái)的麻煩。
    上面的一個(gè)例子,是使用內(nèi)部類(lèi)使得你的代碼得到簡(jiǎn)化和方便。還有些情況下,你可能要避免你的類(lèi)被除了它的外部類(lèi)以外的類(lèi)使用到,這時(shí)候你卻不得不使用內(nèi)部類(lèi)來(lái)解決問(wèn)題。
     
    場(chǎng)景二:解決一些非面向?qū)ο蟮恼Z(yǔ)句塊
    這些語(yǔ)句塊包括if…else if…else語(yǔ)句,case語(yǔ)句,等等。這些語(yǔ)句都不是面向?qū)ο蟮?,給我們?cè)斐闪讼到y(tǒng)的擴(kuò)展上的麻煩。我們可以看看,在模式中,有多少模式是用來(lái)解決由if語(yǔ)句帶來(lái)的擴(kuò)展性的問(wèn)題。
    Java編程中還有一個(gè)困擾我們的問(wèn)題,那就是try…catch…問(wèn)題,特別是在JDBC編程過(guò)程中。請(qǐng)看下面的代碼:
    ……
    try
             {
                    String[] divisionData = null;
                    conn = manager.getInstance().getConnection();
                    stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                    stmt.setLong(1 ,productId.longValue() );
                    stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                    stmt.execute();
                    ResultSet rs = stmt.getCursor(2);
                    int i = 0 ;
                    String strDivision = "";
                    while( rs.next() )
                    {
                                 strDivision += rs.getString("DIVISION_ID") + "," ;
                      }
                      int length = strDivision.length() ;
                      if(length != 0 )
                      {
                             strDivision = strDivision.substring(0,length - 1);
                      }
                      divisionData = StringUtil.split(strDivision, ",") ;
                      map.put("Division", strDivision ) ;
                      LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
           }catch(Exception e)
            {
                           LoggerAgent.error("GetHeaderData", "getDivisionData",
                                                         "SQLException: " + e);
                           e.printStackTrace() ;
     
           }finally
            {
                           manager.close(stmt);
                           manager.releaseConnection(conn);
            }
    這是我們最最常用的一個(gè)JDBC編程的代碼示例。一個(gè)系統(tǒng)有很多這樣的查詢(xún)方法,這段代碼一般分作三段:try關(guān)鍵字括起來(lái)的那段是用來(lái)做查詢(xún)操作的,catch關(guān)鍵字括起來(lái)的那段需要做兩件事,記錄出錯(cuò)的原因和事務(wù)回滾(如果需要的話),finally關(guān)鍵字括起來(lái)的那段用來(lái)釋放數(shù)據(jù)庫(kù)連接。
    我們的煩惱是:try關(guān)鍵字括起來(lái)的那段是變化的,每個(gè)方法的一般都不一樣。而catch和finally關(guān)鍵字括起來(lái)的那兩段卻一般都是不變的,每個(gè)方法的那兩段都是一樣的。既然后面那兩段是一樣的,我們就非常希望將它們提取出來(lái),做一個(gè)單獨(dú)的方法,然后讓每一個(gè)使用到它們的方法調(diào)用。但是,try…catch…finally…是一個(gè)完整的語(yǔ)句段,不能把它們分開(kāi)。這樣的結(jié)果,使得我們不得不在每一個(gè)數(shù)據(jù)層方法里重復(fù)的寫(xiě)相同的catch…finally…這兩段語(yǔ)句。
    既然不能將那些討厭的try…catch…finally…作為一個(gè)公用方法提出去,那么我們還是需要想其他的辦法來(lái)解決這個(gè)問(wèn)題。不然我們老是寫(xiě)那么重復(fù)代碼,真是既繁瑣,又不容易維護(hù)。
    我們?nèi)菀紫氲剑热籧atch…finally…這兩段代碼不能提出來(lái),那么我們能不能將try…里面的代碼提出去呢?唉喲,try…里面的代碼是可變的呢。怎么辦?
    既然try…里面的代碼是可變的,這意味著這些代碼是可擴(kuò)展的,是應(yīng)該由用戶(hù)來(lái)實(shí)現(xiàn)的,對(duì)于這樣的可擴(kuò)展內(nèi)容,我們很容易想到用接口來(lái)定義它們,然后由用戶(hù)去實(shí)現(xiàn)。這樣以來(lái)我們首先定義一個(gè)接口:
    public interface DataManager
    {
            public void manageData();
    }
    我們需要用戶(hù)在manageData()方法中實(shí)現(xiàn)他們對(duì)數(shù)據(jù)層訪問(wèn)的代碼,也就是try…里面的代碼。
    然后我們使用一個(gè)模板類(lèi)來(lái)實(shí)現(xiàn)所有的try…catch…finally…語(yǔ)句的功能,如下:
    public class DataTemplate
    {
            public void execute(DataManager dm)
            {
                   try
                   {
                          dm.manageData();
    }
    catch(Exception e)
    {
           LoggerAgent.error("GetHeaderData", "getDivisionData",
                            "SQLException: " + e);
           e.printStackTrace() ;
     
    }finally
    {
           manager.close(stmt);
           manager.releaseConnection(conn);
    }
    }
    }
    這樣,一個(gè)模板類(lèi)就完成了。我們也通過(guò)這個(gè)模板類(lèi)將catch…finally…兩段代碼提出來(lái)了。我們來(lái)看看使用了這個(gè)模板類(lèi)的數(shù)據(jù)層方法是怎么實(shí)現(xiàn)的:
    new DataTemplate().execute(new DataManager()
    {
            public void manageData()
            {
                    String[] divisionData = null;
                    conn = manager.getInstance().getConnection();
                    stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                    stmt.setLong(1 ,productId.longValue() );
                    stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                    stmt.execute();
                    ResultSet rs = stmt.getCursor(2);
                    int i = 0 ;
                    String strDivision = "";
                    while( rs.next() )
                    {
                                 strDivision += rs.getString("DIVISION_ID") + "," ;
    }
                      int length = strDivision.length() ;
                      if(length != 0 )
                      {
                             strDivision = strDivision.substring(0,length - 1);
                      }
                      divisionData = StringUtil.split(strDivision, ",") ;
                      map.put("Division", strDivision ) ;
                      LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
    }
    });
    注意:本段代碼僅供思路上的參考,沒(méi)有經(jīng)過(guò)上機(jī)測(cè)試。
    我們可以看到,正是這個(gè)實(shí)現(xiàn)了DataManager接口得匿名內(nèi)部類(lèi)的使用,才使得我們解決了對(duì)try…catch…finally…語(yǔ)句的改造。這樣,第一為我們解決了令人痛苦的重復(fù)代碼;第二也讓我們?cè)跀?shù)據(jù)層方法的編碼中,直接關(guān)注對(duì)數(shù)據(jù)的操作,不用關(guān)心那些必需的但是與數(shù)據(jù)操作無(wú)關(guān)的東西。
    我們現(xiàn)在來(lái)回想一下Spring框架的數(shù)據(jù)層,是不是正是使用了這種方法呢?
     
     
    場(chǎng)景之三:一些多算法場(chǎng)合
    假如我們有這樣一個(gè)需求:我們的一個(gè)方法用來(lái)對(duì)數(shù)組排序并且依次打印各元素,對(duì)數(shù)組排序方法有很多種,用哪種方法排序交給用戶(hù)自己確定。
    對(duì)于這樣一個(gè)需求,我們很容易解決。我們決定給哪些排序算法定義一個(gè)接口,具體的算法實(shí)現(xiàn)由用戶(hù)自己完成,只要求他實(shí)現(xiàn)我們的接口就行。
    public interface SortAlgor
    {
            public void sort(int[] is);
    }
    這樣,我們?cè)僭诜椒ɡ飳?shí)現(xiàn)先排序后打印,代碼如下:
    public void printSortedArray(int[] is,SortAlgor sa)
    {
            ……
           sa.sort(is);
            for(int i=0;i<is.length;i++)
            {
                   System.out.print(is[i]+” “);
    }
    System.out.println();
    }
    客戶(hù)端對(duì)上面方法的使用如下:
    int[] is = new int[]{3,1,4,9,2};
    printSortedArray(is,new SortAlgor()
    {
            public void sort(is)
            {
                   int k = 0;
                   for(int i=0;i<is.length;i++)
                   {
                         for(int j=i+1;j<is.length;j++)
                          {
                                 if(is[i]>is[j])
                                 {
                                        k = is[i];
                                        is[i] = is[j];
                                        is[j] = k;
                                 }
                          }
                   }
    }
    });
    這樣的用法很多,我們都或多或少的被動(dòng)的使用過(guò)。如在Swing編程中,我們經(jīng)常需要對(duì)組件增加監(jiān)聽(tīng)器對(duì)象,如下所示:
    spinner2.addChangeListener(new ChangeListener()
    {
    public void stateChanged(ChangeEvent e)
    {
    System.out.println("Source: " + e.getSource());
    }
    }
    );
    在Arrays包里,對(duì)元素為對(duì)象的數(shù)組的排序:
    Arrays.sort(emps,new Comparator(){
            Public int compare(Object o1,Object o2)
            {
                   return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
    }
    });
    這樣的例子還有很多,JDK教會(huì)了我們很多使用內(nèi)部類(lèi)的方法。隨時(shí)我們都可以看一看API,看看還會(huì)在什么地方使用到內(nèi)部類(lèi)呢?
     
     
     
    場(chǎng)景之四:適當(dāng)使用內(nèi)部類(lèi),使得代碼更加靈活和富有擴(kuò)展性
    適當(dāng)?shù)氖褂脙?nèi)部類(lèi),可以使得你的代碼更加靈活和富有擴(kuò)展性。當(dāng)然,在這里頭起作用的還是一些模式的運(yùn)行,但如果不配以?xún)?nèi)部類(lèi)的使用,這些方法的使用效果就差遠(yuǎn)了。不信?請(qǐng)看下面的例子:
    我們記得簡(jiǎn)單工廠模式的作用就是將客戶(hù)對(duì)各個(gè)對(duì)象的依賴(lài)轉(zhuǎn)移到了工廠類(lèi)里。很顯然,簡(jiǎn)單工廠模式并沒(méi)有消除那些依賴(lài),只是簡(jiǎn)單的將它們轉(zhuǎn)移到了工廠類(lèi)里。如果有新的對(duì)象增加進(jìn)來(lái),則我們需要修改工廠類(lèi)。所以我們需要對(duì)工廠類(lèi)做進(jìn)一步的改造,進(jìn)一步消除它對(duì)具體類(lèi)的依賴(lài)。以前我們提供過(guò)一個(gè)使用反射來(lái)消除依賴(lài)的方法;這里,我們將提供另外一種方法。
    這種方法是將工廠進(jìn)一步抽象,而將具體的工廠類(lèi)交由具體類(lèi)的創(chuàng)建者來(lái)實(shí)現(xiàn),這樣,工廠類(lèi)和具體類(lèi)的依賴(lài)的問(wèn)題就得到了解決。而工廠的使用者則調(diào)用抽象的工廠來(lái)獲得具體類(lèi)的對(duì)象。如下。
    我們以一個(gè)生產(chǎn)形體的工廠為例,下面是這些形體的接口:
    package polyFactory;
     
    public interface Shape {
    public void draw();
    public void erase();
     
    }
     
    通過(guò)上面的描述,大家都可能已經(jīng)猜到,這個(gè)抽象的工廠肯定使用的是模板方法模式。如下:
    package polyFactory;
     
    import java.util.HashMap;
    import java.util.Map;
     
     
    public abstract class ShapeFactory {
    protected abstract Shape create();
    private static Map factories = new HashMap();
    public static void addFactory(String id,ShapeFactory f)
    {
           factories.put(id,f);
    }
    public static final Shape createShape(String id)
    {
           if(!factories.containsKey(id))
            {
                   try
                   {
                          Class.forName("polyFactory."+id);
                   }
                   catch(ClassNotFoundException e)
                   {
                          throw new RuntimeException("Bad shape creation : "+id);
                   }
            }
            return ((ShapeFactory)factories.get(id)).create();
    }
    }
    不錯(cuò),正是模板方法模式的運(yùn)用。這個(gè)類(lèi)蠻簡(jiǎn)單的:首先是一個(gè)create()方法,用來(lái)產(chǎn)生具體類(lèi)的對(duì)象,留交各具體工廠實(shí)現(xiàn)去實(shí)現(xiàn)。然后是一個(gè)Map類(lèi)型的靜態(tài)變量,用來(lái)存放具體工廠的實(shí)現(xiàn)以及他們的ID號(hào)。接著的一個(gè)方法使用來(lái)增加一個(gè)具體工廠的實(shí)現(xiàn)。最后一個(gè)靜態(tài)方法是用來(lái)獲取具體對(duì)象,里面的那個(gè)Class.forName……的作用是調(diào)用以ID號(hào)為類(lèi)名的類(lèi)的一些靜態(tài)的東西。
    下面,我們來(lái)看具體的類(lèi)的實(shí)現(xiàn):
    package polyFactory;
     
    public class Circle implements Shape {
     
     
    public void draw() {
            // TODO Auto-generated method stub
           System.out.println("the circle is drawing...");
    }
     
     
    public void erase() {
            // TODO Auto-generated method stub
           System.out.println("the circle is erasing...");
    }
    private static class Factory extends ShapeFactory
    {
           protected Shape create()
            {
                   return new Circle();
            }
    }
    static {ShapeFactory.addFactory("Circle",new Factory());}
     
    }
    這個(gè)類(lèi)的其他的地方也平常得很。但就是后面的那個(gè)內(nèi)部類(lèi)Factory用得好。第一呢,這個(gè)類(lèi)只做一件事,就是產(chǎn)生一個(gè)Circle對(duì)象,與其他類(lèi)無(wú)關(guān),就這一個(gè)條也就滿足了使用內(nèi)部類(lèi)的條件。第二呢,這個(gè)Factory類(lèi)需要是靜態(tài)的,這也得要求它被使用內(nèi)部類(lèi),不然,下面的ShapeFacotry.addFactory就沒(méi)辦法add了。而最后的那個(gè)靜態(tài)的語(yǔ)句塊是用來(lái)將具體的工廠類(lèi)添加到抽象的工廠里面去。在抽象工廠里調(diào)用Class.forName就會(huì)執(zhí)行這個(gè)靜態(tài)的語(yǔ)句塊了。
    下面仍然是一個(gè)具體類(lèi):
    package polyFactory;
     
     
    public class Square implements Shape {
     
    public void draw() {
            // TODO Auto-generated method stub
           System.out.println("the square is drawing...");
    }
     
    public void erase() {
            // TODO Auto-generated method stub
           System.out.println("the square is erasing...");
    }
    private static class Factory extends ShapeFactory
    {
           protected Shape create()
            {
                   return new Square();
            }
    }
    static {ShapeFactory.addFactory("Square",new Factory());}
     
    }
    最后,我們來(lái)測(cè)試一下:
    String[] ids = new String[]{"Circle","Square","Square","Circle"};
            for(int i=0;i<ids.length;i++)
            {
                   Shape shape = ShapeFactory.createShape(ids[i]);
                   shape.draw();
                   shape.erase();
            }
    測(cè)試結(jié)果為:
    the circle is drawing...
    the circle is erasing...
    the square is drawing...
    the square is erasing...
    the square is drawing...
    the square is erasing...
    the circle is drawing...
    the circle is erasing...
           這個(gè)方法是巧妙地使用了內(nèi)部類(lèi),將具體類(lèi)的實(shí)現(xiàn)和它的具體工廠類(lèi)綁定起來(lái),由具體類(lèi)的實(shí)現(xiàn)者在這個(gè)內(nèi)部類(lèi)的具體工廠里去產(chǎn)生一個(gè)具體類(lèi)的對(duì)象,這當(dāng)然容易得多。雖然需要每一個(gè)具體類(lèi)都創(chuàng)建一個(gè)具體工廠類(lèi),但由于具體工廠類(lèi)是一個(gè)內(nèi)部類(lèi),這樣也不會(huì)隨著具體類(lèi)的增加而不斷增加新的工廠類(lèi),使得代碼看起來(lái)很臃腫,這也是本方法不得不使用內(nèi)部類(lèi)的一個(gè)原因吧。

    posted on 2007-10-22 15:59 劉錚 閱讀(774) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JAVA General

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

    導(dǎo)航

    統(tǒng)計(jì)

    留言簿(1)

    文章分類(lèi)(141)

    文章檔案(147)

    搜索

    最新評(píng)論

    主站蜘蛛池模板: 一个人看的免费高清视频日本| 无码视频免费一区二三区| 一级一黄在线观看视频免费| 无码天堂va亚洲va在线va| 蜜桃传媒一区二区亚洲AV| 看成年女人免费午夜视频| 国产一区二区三区亚洲综合| 美女黄色毛片免费看| 七次郎成人免费线路视频| 一区二区三区在线观看免费| 国产高潮久久免费观看| 中文字幕高清免费不卡视频| 国产成人免费AV在线播放| 无码人妻AV免费一区二区三区| 1000部禁片黄的免费看| 四虎永久在线精品免费观看视频| 9久9久女女免费精品视频在线观看| 无码免费午夜福利片在线 | 特级毛片免费播放| 免费很黄无遮挡的视频毛片| caoporm碰最新免费公开视频| aa级毛片毛片免费观看久| 免费观看四虎精品成人| 亚洲国产精华液网站w| 免费h视频在线观看| **真实毛片免费观看| 永久免费毛片在线播放| 国产jizzjizz视频免费看| 久久久久久久亚洲精品| 亚洲国产精品久久久久网站 | 成年人视频在线观看免费| 免费人成在线观看网站视频 | 亚洲VA综合VA国产产VA中| 国外亚洲成AV人片在线观看| 久久久久久亚洲精品| 亚洲a级成人片在线观看| 国产精品亚洲专区无码不卡| a毛片在线看片免费| 青娱分类视频精品免费2| 免费A级毛片无码A∨男男| 亚洲成A人片在线观看无码不卡|