1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        淺談Java常見設(shè)計模式(二)

        共 16127字,需瀏覽 33分鐘

         ·

        2021-02-05 12:26

        你,發(fā)如雪,凄美了離別

        我焚香感動了誰

        邀明月,讓回憶皎潔

        愛在月光下完美

        你,發(fā)如雪,紛飛了眼淚

        我等待蒼老了誰

        紅塵醉,微醺的歲月

        我用無悔,刻永世愛你的碑



        淺談Java常見設(shè)計模式(一),上文說到了關(guān)于如何創(chuàng)建城市的對比信息,一般來說,對比城市,需要創(chuàng)建所有的信息保證唯一性,以免到時候維度不唯一了,導致對比性出現(xiàn)了差異。比如,如果創(chuàng)建城市基本信息,那么關(guān)于每一個城市,只需要創(chuàng)建出唯一的一個就好了,這時候,就需要用到單例模式,即在程序運行時,對象只有一份!



        一般來說,單例模式具有4種實現(xiàn)方式:


        餓漢式單例:


        餓漢式單例,即對象被加載到內(nèi)存中后,初始化改對象,然后使用的時候直接獲?。?br />



        package com.lgli.create.single;
        /** * 單例模式 * @author lgli */public class Single { public static void main(String[] args) { ChongQingCityBase base = ChongQingCityBase.getInstance(); System.out.println(base); }
        }


        /** * 重慶市基本信息 * @author lgli */class?ChongQingCityBase?extends?CityBase?{ private static final ChongQingCityBase chongQingCityBase????????????=?new?ChongQingCityBase(); private ChongQingCityBase (){ }
        public static ChongQingCityBase getInstance(){ return chongQingCityBase; }
        void base() { System.out.println("重慶市基本信息"); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{ abstract void base();}


        這里用多線程來測試下,是否都只實例化了一個對象:


        0366d3336917a105b1cf1fb0b6960149.webp


        這里定義了1000個線程運行,得到的對象都是一個


        餓漢式單例,是無論是否需要用到,在類被加載到內(nèi)存中的時候,就已經(jīng)在堆中實例化了這個對象,


        這時候,就有點尷尬了,假設(shè)從來沒有用到過,那么這就有點浪費內(nèi)存了,


        所以,懶漢式單例應(yīng)運而生


        懶漢式單例:


        懶漢式單例,是需要用到這個對象的時候,才實例化這個對象


        package com.lgli.create.single.singlev2;

        /** * * 單例模式--懶漢式 * @author lgli */public class Single {

        public static void main(String[] args) { ChongQingCityBase base = ChongQingCityBase.getInstance(); System.out.println(base); }
        }
        /** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase {
        private static ChongQingCityBase chongQingCityBase;

        private ChongQingCityBase (){
        }
        public static ChongQingCityBase getInstance(){ if(chongQingCityBase == null){ chongQingCityBase = new ChongQingCityBase(); } return chongQingCityBase; }
        }
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }


        這里先不初始化對象,當調(diào)用getInstance方法的時候,才初始化這個對象,同時判斷當前對象是否存在,如果存在,則直接返回


        這種情況,在單線程的情況下,是沒有什么問題的,可是當遇到多個線程并發(fā)的時候,就出現(xiàn)問題了:


        8b9ebe85a5482dd458063270f4518caf.webp


        如上圖所示,這里用100個線程模擬并發(fā),出現(xiàn)了多個實例


        來看下這個代碼:


        95d07f33abb835d5230bb5c5655bc54e.webp


        分析下,并發(fā)的情況下,


        當某個線程執(zhí)行到44行的時候,這個時候chongQingCityBase是null的,此時,線程切換,另外一個線程也走到44行,判斷也是為空的,那么這2個線程,均符合if判斷,去new一個對象,這時候就出現(xiàn)了多個實例的情況。


        那么解決這個問題吧


        首先想到的就是加鎖吧,首先可能想到把這個實例化方法加鎖,這樣子肯定是可能滿足只產(chǎn)生一個對象的,因為畢竟方法鎖的話,多個線程都會等待方法鎖的釋放才能調(diào)用這個方法。


        4a9393d18a127ddd9c6cf7952a61da22.webp


        如果,實例化方法里面可能還有其他邏輯,這樣子暴力加鎖肯定不是一個很好的解決方案,


        所以只需要在關(guān)鍵的地方加鎖:


        比如:


        d816f5fac58c66ba10446be440612d3a.webp


        這里將加鎖加到初始化實例對象的地方,然后同樣的用多線程去運行下結(jié)果,這里為了解決可能因為多線程導致JVM指令重排<JVM中將不影響單線程代碼實際效果的指令下,顛倒執(zhí)行順序>出錯,這里將這個成員變量加上volatile。


        5e632b222d1eebe7e9209485d63be2a0.webp



        這里可以看見,依然有多個實例的產(chǎn)生,即此種加鎖的方式,是存在問題的,可能導致初始化多個對象。


        簡單分析下原因:


        當多個線程同時走過if判斷,此時chongQingCityBase是null的,然后進入到加鎖的方法,這時候出現(xiàn)的結(jié)果就是會有多個對象被實例化了


        即:


        e8603ae0d2ce3c97180919d490721630.webp



        線程1和2在chongQingCityBase為null時,同時進入if判斷。




        如何解決這個問題呢?



        這就是懶漢式加載的雙重判斷機制,即:


        c8ff8aa87e0488d96e72ebee1e12c1fa.webp


        這里加了雙重的校驗,即在加鎖的方法內(nèi)部,也同樣執(zhí)行判斷。


        此時,看下運行結(jié)果<記住這里的volatile關(guān)鍵字,否則也是有可能出現(xiàn)多個實例化對象的,具體原因,后續(xù)會說到>:


        052a9ccf529ec41d28b19d48be9439eb.webp


        此時,實例化的對象就只有一個了,

        那么這個地方有個問題就是,這個雙重判斷外面這個判斷的意義是什么呢?


        如果沒有外面的判斷,意味著每次執(zhí)行方法的時候,都會進去到線程等待的方法,和在方法上加鎖沒有太大的區(qū)別,所以為了性能考慮,加上這個外層判斷


        內(nèi)部類單例:


        看下面代碼:


        package com.lgli.create.single.singlev3;

        import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
        /** * 單例模式 -- 內(nèi)部類 * @author lgli */public class Single {
        public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool(); for(int i = 0 ; i < 100 ; i++){ executorService.execute(()->{ ChongQingCityBase base = ChongQingCityBase.getInstance(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().toString()+base); }); }
        executorService.shutdown();
        }}


        /** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase {
        private ChongQingCityBase (){
        }
        public static ChongQingCityBase getInstance(){ return ChongQingCityBaseHolder.CHONGQINGCITYBASE; }
        private static class ChongQingCityBaseHolder{
        private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase(); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }


        運行結(jié)果,也是只有一個實例的產(chǎn)生,


        那么這種靜態(tài)內(nèi)部類的實現(xiàn)原理是什么樣子的呢?


        涉及到JVM類加載的一些邏輯,大致情況是這樣子的:


        當ChongQingCityBase被加載的時候,會優(yōu)先加載內(nèi)部類

        ChongQingCityBaseHolder


        當有程序調(diào)用

        com.lgli.create.single.singlev3.ChongQingCityBase#getInstance

        方法的時候,

        執(zhí)行

        ChongQingCityBaseHolder.CHONGQINGCITYBASE


        此時才會去執(zhí)行內(nèi)部類的


        private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase();


        所以這里的內(nèi)部類方式,其實也屬于懶漢式加載



        暴力破壞單例-->反射:


        有時候,可能一個單例對象,會被人誤用反射機制暴力破解,創(chuàng)建出多個實例,這里以為內(nèi)部類為例,代碼指出反射暴力創(chuàng)建對象導致單例失效的情況,其他懶漢/餓漢類似也可以通過此方法破解:


        package com.lgli.create.single.breaksingle;

        import java.lang.reflect.Constructor;
        /** * 反射破壞單例 * @author lgli */public class BreakSingle {
        public static void main(String[] args) throws Exception { //反射創(chuàng)建對象 //根據(jù)對象路徑獲取類 Class<?> aClass = Class.forName("com.lgli.create.single.breaksingle.ChongQingCityBase"); //獲取申明的沒有參數(shù)的構(gòu)造方法 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null); //設(shè)置強訪問 declaredConstructor.setAccessible(true); //獲取實例對象 ChongQingCityBase o = (ChongQingCityBase)declaredConstructor.newInstance(); //正常創(chuàng)建對象 ChongQingCityBase p = ChongQingCityBase.getInstance(); System.out.println(o); System.out.println(p); System.out.println(o==p); }}

        /** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase {
        private ChongQingCityBase (){
        }
        public static ChongQingCityBase getInstance(){ return ChongQingCityBase.ChongQingCityBaseHolder.CHONGQINGCITYBASE; }
        /** * 內(nèi)部類的單例 */ private static class ChongQingCityBaseHolder{ private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase(); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }



        運行可以獲得兩個不同的對象:


        f3682c7f90a35095d02afcd43f790ce4.webp


        這里可以看到通過反射實例化了一個對象,然后通過正常創(chuàng)建又獲取了一個對象。


        那么如何規(guī)避這個問題呢?




        可以在構(gòu)造方法中加入判斷:



        比如:


        1b9b95969b972d3dbfca2e8d8eae0d03.webp

        可以直接在構(gòu)造方法中,拋出異常,不允許構(gòu)建實例。


        這時候,反射機制構(gòu)建實例就會拋出異常,只能正常的構(gòu)建了。


        319275715d1b85d06a926fb63ee4aa17.webp


        除此之外,還可以通過序列化的方式,破壞單例


        暴力破壞單例-->序列化


        看下面代碼:


        package com.lgli.create.single.breaksinglev2;

        import java.io.*;
        /** * 序列化破壞單例 * @author lgli */public class SerializableSingle {
        public static void main(String[] args) { //根據(jù)正常構(gòu)建方法獲取的對象 ChongQingCityBase base1 = ChongQingCityBase.getInstance(); //序列化后反序列化的對象 ChongQingCityBase base2 = null; try{ //序列化對象 FileOutputStream fileOutputStream = new FileOutputStream("../ChongQingCityBase.obj"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(base1); objectOutputStream.flush(); objectOutputStream.close(); fileOutputStream.close(); //反序列化實例對象 FileInputStream fileInputStream = new FileInputStream("../ChongQingCityBase.obj"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); base2 = (ChongQingCityBase)objectInputStream.readObject(); fileInputStream.close(); objectInputStream.close(); //對比兩個結(jié)果對象 System.out.println(base1); System.out.println(base2); System.out.println(base1 == base2); }catch (Exception e){ e.printStackTrace(); } }

        }
        /** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase implements Serializable {

        private ChongQingCityBase (){ if(ChongQingCityBase.ChongQingCityBaseHolder.CHONGQINGCITYBASE != null){ throw new RuntimeException("不允許構(gòu)建多個實例"); } }
        public static ChongQingCityBase getInstance(){ return ChongQingCityBase.ChongQingCityBaseHolder.CHONGQINGCITYBASE; }
        /** * 內(nèi)部類的單例 */ private static class ChongQingCityBaseHolder{ private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase(); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }


        運行代碼結(jié)果輸出:


        e8395167c7f13b52ad5e556c241e7be8.webp



        那么針對這種破壞單例的方法,如何預防呢?


        這時候需要在實例中寫個方法:


        b5715cb2e0a7174abc576a6b2dcbf6f0.webp


        如上圖所示,這里需要寫這個readResolve方法,


        這時候運行程序:


        727c9fbe4f2883f341382b0af025fd4c.webp


        顯然,這個時候,只獲取了一個實例。


        那么這個是為啥呢?


        這里簡單描述下,畫個圖


        1471d1ee2d42c28c00f156d1a82c1267.webp

        這里是反序列化的時候,調(diào)用對象創(chuàng)建方法的源碼,在

        java.io.ObjectInputStream#readObject0

        調(diào)用

        java.io.ObjectInputStream#readOrdinaryObject

        之后,會返回這個創(chuàng)建的對象


        那么看下

        java.io.ObjectInputStream#readOrdinaryObject

        這個方法究竟做了些什么事呢?


        private Object readOrdinaryObject(boolean unshared)        throws IOException    {        if (bin.readByte() != TC_OBJECT) {            throw new InternalError();        }
        ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize();
        Class<?> cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); }
        Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); }
        passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); }
        if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); }
        handles.finish(passHandle);
        if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { // Filter the replacement object if (rep != null) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1); } } handles.setObject(passHandle, obj = rep); } }
        return obj; }


        上面代碼是JDK源碼,這里主要看關(guān)鍵部分


        b3d786f2d5d8c81abfbce0c3e1f1e9b6.webp



        這里先判斷是否滿足isInstantiable()


        這個方法其實是判斷是否有構(gòu)造方法的,這里就不往下看了


        很明顯,是有構(gòu)造方法的,雖然是private的,但是對JDK來說,這都不重要



        然后滿足有構(gòu)造方法,則創(chuàng)建實例對象,這時候創(chuàng)建的實例對象和正常創(chuàng)建的肯定是兩回事。所以序列化再反序列化后的對象,肯定不是同一個了,這里就理解到了



        再接著看下面的代碼:


        3a8e7b61222b5c8d3252ff5965a71925.webp


        這里緊接著上面的代碼走,有一個判斷,然后滿足判斷之后,則調(diào)用

        java.io.ObjectStreamClass#invokeReadResolve

        方法,實例化新的對象出來,


        當對象寫了readResolve方法后,就滿足了這里的if條件,然后會調(diào)用對象中的readResolve方法,而我們寫的readResolve方法就是返回單例對象,所以這里同樣返回了單例對象,覆蓋了前面所創(chuàng)建的實例對象:


        c4dd458a2514845da5484c5e9e647352.webp


        所以得到的對象是一個單例對象。



        枚舉單例-->最牛單例寫法


        最后介紹一個Java推薦的單例模式的寫法,枚舉單例


        看下代碼:



        package com.lgli.create.single.singlev4;

        import java.io.*;/** * * 枚舉單例 * @author lgli */public class Single {

        public static void main(String[] args) {
        //根據(jù)正常構(gòu)建方法獲取的對象 EnumSingle base1 = EnumSingle.getInstance(); base1.setObj(ChongQingCityBase.getInstance()); //序列化后反序列化的對象 EnumSingle base2 = null; try{ //序列化對象 FileOutputStream fileOutputStream = new FileOutputStream("../ChongQingCityBase.obj"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(base1); objectOutputStream.flush(); objectOutputStream.close(); fileOutputStream.close(); //反序列化實例對象 FileInputStream fileInputStream = new FileInputStream("../ChongQingCityBase.obj"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); base2 = (EnumSingle)objectInputStream.readObject(); fileInputStream.close(); objectInputStream.close();
        System.out.println(base1.getObj()); //對比兩個結(jié)果對象 System.out.println(base2.getObj()); System.out.println(base1 == base2); }catch (Exception e){ e.printStackTrace(); }


        }}

        /** * * 枚舉單例 */enum EnumSingle{ INSTANCE; private Object obj;
        public Object getObj() { return obj; }
        public void setObj(Object obj) { this.obj = obj; } public static EnumSingle getInstance(){ return INSTANCE; }}/** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase implements Serializable {
        private ChongQingCityBase (){
        }
        public static ChongQingCityBase getInstance(){ return ChongQingCityBase.ChongQingCityBaseHolder.CHONGQINGCITYBASE; }
        private static class ChongQingCityBaseHolder{
        private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase(); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }


        這里,申明了一個枚舉,

        同時枚舉類中有一個成員變量Object,

        用來設(shè)置初始化的單例對象的



        然后先測試下,這里通過序列化破壞單例,是否可以成功,


        運行上述代碼:


        2f33a727b5091b5642155109a072739b.webp


        這里,可以很明顯的看到,兩個對象實例是一樣的,即,在通過序列化,同時沒有寫readResolve方法,依然獲得了兩個一樣的對象,即序列化和反序列化并沒有破壞枚舉單例。



        為何枚舉單例可以防止序列化呢?



        同樣的,看下反序列化時,創(chuàng)建對象的時候,做了些什么事


        和前面的邏輯調(diào)用圖基本一致,只不過在最后一個是調(diào)用

        java.io.ObjectInputStream#readEnum


        源碼如下:


        d0e2cc08dcf38e2042bc2415c25d0bbf.webp



        這里,可以看到返回的對象是通過一個類名,和一個唯一的

        名字name,構(gòu)建了這個返回對象,很顯然,在同一個應(yīng)用中,這是唯一的,

        所以構(gòu)建出來的對象也是唯一的。


        那么線程安全么?


        這里使用反編譯工具,將寫的這個枚舉單例類反編譯出來,這里使用的是JAD,其安裝使用就不多說了,記住一點就好,在window環(huán)境下,需要把jad.exe放到本機jdk的bin文件下


        反編譯這個EnumSingle.class


        1a5462e486f398df771f600e1e84adf4.webp


        在當前目錄下生成了一個EnumSingle.jad文件


        打開這個文件:


        7da71458d69c6b99ae2ce3ff5366ef36.webp


        關(guān)鍵位置,如上圖后面一個圖標識所示,這種方式屬于餓漢式單例,所以一定是線程安全的


        然后又不能通過反序列化


        那么反射呢?


        測試下反射,這里使用反射獲取構(gòu)造方法是時候,獲取到的應(yīng)該是帶2個參數(shù)的構(gòu)造方法,String和int,上面截圖標識有構(gòu)造方法。


        看下代碼:


        package com.lgli.create.single.singlev4;

        import java.io.*;import java.lang.reflect.Constructor;
        /** * * 枚舉單例 * @author lgli */public class Single {
        public static void main(String[] args) {// serializableType(); reflectType(); }

        /** * 反射方式 */ public static void reflectType(){ try{
        Class<?> aClass = Class.forName("com.lgli.create.single.singlev4.EnumSingle"); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); EnumSingle lgli = (EnumSingle)declaredConstructor.newInstance("lgli", 520); System.out.println(lgli); }catch (Exception e){ e.printStackTrace();????????} }

        /** * 序列化和反序列化破幻枚舉單例方式 */ public static void serializableType(){ //根據(jù)正常構(gòu)建方法獲取的對象 EnumSingle base1 = EnumSingle.getInstance(); base1.setObj(ChongQingCityBase.getInstance());
        //序列化后反序列化的對象 EnumSingle base2 = null; try{ //序列化對象 FileOutputStream fileOutputStream = new FileOutputStream("../ChongQingCityBase.obj"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(base1); objectOutputStream.flush(); objectOutputStream.close(); fileOutputStream.close(); //反序列化實例對象 FileInputStream fileInputStream = new FileInputStream("../ChongQingCityBase.obj"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); base2 = (EnumSingle)objectInputStream.readObject(); fileInputStream.close(); objectInputStream.close();
        System.out.println(base1.getObj()); //對比兩個結(jié)果對象 System.out.println(base2.getObj()); System.out.println(base1 == base2); }catch (Exception e){ e.printStackTrace(); } }

        }

        /** * * 枚舉單例 */enum EnumSingle{
        INSTANCE; private Object obj;
        public Object getObj() { return obj; }
        public void setObj(Object obj) { this.obj = obj; }
        public static EnumSingle getInstance(){ return INSTANCE; }}/** * 重慶市基本信息 * @author lgli */class ChongQingCityBase extends CityBase implements Serializable {
        private ChongQingCityBase (){
        }
        public static ChongQingCityBase getInstance(){ return ChongQingCityBase.ChongQingCityBaseHolder.CHONGQINGCITYBASE; }
        private static class ChongQingCityBaseHolder{
        private static final ChongQingCityBase CHONGQINGCITYBASE = new ChongQingCityBase(); }}
        /** * 抽象城市基本信息類 */abstract class CityBase{
        }


        運行測試類,結(jié)果報錯了:


        c2ec1f72f9843b5ffe663fd4beb308ca.webp

        程序異常:Cannot reflectively create enum objects


        無法通過反射創(chuàng)建枚舉對象


        。。。。。。


        這說明什么呢?說明JDK在從根本上解決了妄圖通過反射去構(gòu)建枚舉對象的幻想!


        看下

        java.lang.reflect.Constructor#newInstance

        源碼:


        70d185e70efc9fc8bc58a6627d8fedac.webp


        所以,反射是不可行的。


        總結(jié)起來,通過枚舉創(chuàng)建單例,無論是多線程、序列化、反射都無法改變單例,所以枚舉應(yīng)該是最牛的吧。(#^.^#)


        其實還要一種單例,屬于線程內(nèi)單例,但是線程之間不是單例的-->

        ThreadLocal


        這里就先不詳細解釋了,后續(xù)會說到這個問題



        歡迎轉(zhuǎn)發(fā)關(guān)注,謝謝!



        點個贊咯!

        瀏覽 44
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久一区二区三区 | 国产精品中文字幕在线 | 中国美女操逼 | 一道本无码在线播放 | 操逼视频免费观看 | 美女天堂 | 久久 无码 一区二区三区四区 | 亚洲色女一区 | 美女干逼| 欧美日韩视频在线 |