<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 16127字,需瀏覽 33分鐘

           ·

          2021-02-05 12:26

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

          我焚香感動了誰

          邀明月,讓回憶皎潔

          愛在月光下完美

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

          我等待蒼老了誰

          紅塵醉,微醺的歲月

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



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



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


          餓漢式單例:


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



          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)在堆中實例化了這個對象,


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


          所以,懶漢式單例應運而生


          懶漢式單例:


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


          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); //設置強訪問 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,

          用來設置初始化的單例對象的



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


          運行上述代碼:


          2f33a727b5091b5642155109a072739b.webp


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



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



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


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

          java.io.ObjectInputStream#readEnum


          源碼如下:


          d0e2cc08dcf38e2042bc2415c25d0bbf.webp



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

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

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


          那么線程安全么?


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


          反編譯這個EnumSingle.class


          1a5462e486f398df771f600e1e84adf4.webp


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


          打開這個文件:


          7da71458d69c6b99ae2ce3ff5366ef36.webp


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


          然后又不能通過反序列化


          那么反射呢?


          測試下反射,這里使用反射獲取構(gòu)造方法是時候,獲取到的應該是帶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)建單例,無論是多線程、序列化、反射都無法改變單例,所以枚舉應該是最牛的吧。(#^.^#)


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

          ThreadLocal


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



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



          點個贊咯!

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人妖乱伦视频 | 国产成人无码在线高清播放 | 福利视频久久久久 | 夜夜躁狠狠躁日日躁麻豆老人 | 亚洲日韩欧美在线中文18 |