Java8中的Lambda表達(dá)式

作者:湯圓

個(gè)人博客:javalover.cc

前言

大家好啊损话,我是湯圓雕憔,今天給大家?guī)?lái)的是《Java8中的Lambda表達(dá)式》夺谁,希望對(duì)大家有幫助,謝謝

文章純屬原創(chuàng)肴盏,個(gè)人總結(jié)難免有差錯(cuò)威沫,如果有,麻煩在評(píng)論區(qū)回復(fù)或后臺(tái)私信伤哺,謝啦

簡(jiǎn)介

Lambda表達(dá)式是一個(gè)可傳遞的代碼塊燕侠,可以在以后執(zhí)行一次或多次;

下面貼個(gè)對(duì)比代碼:

// Java8之前:舊的寫(xiě)法
Runnable runnable = new Runnable() {
  @Override
  public void run() {
    System.out.println("old run");
  }
};
Thread t = new Thread(runnable);

// Java8之后:新的寫(xiě)法
Runnable runnable1 = ()->{
  System.out.println("lambda run");
};
Thread t1 = new Thread(runnable1);

可以看到立莉,有了lambda绢彤,代碼變得簡(jiǎn)潔多了

你可以把lambda當(dāng)作一個(gè)語(yǔ)法糖

下面讓我們一起來(lái)探索lambda的美好世界吧

目錄

下面列出本文的目錄

  • lambda的語(yǔ)法
  • 為啥引入lambda
  • 什么是函數(shù)式接口
  • 什么是行為參數(shù)化
  • 手寫(xiě)一個(gè)函數(shù)式接口
  • 常用的函數(shù)式接口
  • 什么是方法引用
  • 什么是構(gòu)造引用
  • lambda的組合操作

正文

1. lambda的語(yǔ)法

lambda語(yǔ)法

下面分別說(shuō)下語(yǔ)法中的三個(gè)組成部分

  • 參數(shù): ( Dog dog )
    • 參數(shù)類(lèi)型可省略(當(dāng)編譯器可以自動(dòng)推導(dǎo)時(shí)),比如Comparator<String> comparatorTest = (a, b)->a.length()-b.length();,可以推導(dǎo)出a,b都為String
    • 當(dāng)參數(shù)類(lèi)型可省略蜓耻,且只有一個(gè)參數(shù)時(shí)茫舶,括弧也可以省略(但是個(gè)人習(xí)慣保留)
  • 符號(hào): ->
  • 主體:{ System.out.println("javalover"); }
    • 如果是一條語(yǔ)句,則需要加大括號(hào)和分號(hào){;}(比如上圖所示)
    • 如果是一個(gè)表達(dá)式刹淌,則直接寫(xiě)饶氏,啥也不加(比如a.length()- b.length()

2. 為啥引入lambda

為了簡(jiǎn)化代碼

因?yàn)镴ava是面向?qū)ο笳Z(yǔ)言讥耗,所以在lambda出現(xiàn)之前,我們需要先構(gòu)造一個(gè)對(duì)象疹启,然后在對(duì)象的方法中實(shí)現(xiàn)具體的內(nèi)容古程,再把構(gòu)造的對(duì)象傳遞給某個(gè)對(duì)象或方法

但是有了lambda以后,我們可以直接將代碼塊傳遞給對(duì)象或方法

現(xiàn)在再回頭看下開(kāi)頭的例子

lambda減少了模板代碼

可以看到喊崖,用了lambda表達(dá)式后挣磨,少了很多模板代碼,只剩下一個(gè)代碼塊(最核心的部分)

3. 什么是函數(shù)式接口

就是只定義了一個(gè)抽象方法的接口

  • 正例:有多個(gè)默認(rèn)方法贷祈,但是如果只有一個(gè)抽象方法,那它就是函數(shù)式接口喝峦,示例代碼如下
@FunctionalInterface
public interface FunctionInterfaceDemo {
    void abstractFun();
    default void fun1(){
        System.out.println("fun1");    
    }
    default void fun2(){
        System.out.println("fun2");
    }   
}

這里的注解@FunctionalInterface可以省略势誊,但是建議加上,就是為了告訴編譯器谣蠢,這是一個(gè)函數(shù)式接口粟耻,此時(shí)如果該接口有多個(gè)抽象方法,那么編譯器就會(huì)報(bào)錯(cuò)

  • 反例:比如A extends B眉踱,A和B各有一個(gè)抽象方法挤忙,那么A就不是函數(shù)式接口,示例代碼如下
// 編譯器會(huì)報(bào)錯(cuò)谈喳,Multiple non-overriding abstract methods found in XXX
@FunctionalInterface
public interface NoFunctionInterfaceDemo extends FunctionInterfaceDemo{
  void abstractFun2();
}

上面的父接口FunctionInterfaceDemo中已經(jīng)有了一個(gè)抽象方法册烈,此時(shí)NoFunctionInterfaceDemo又定義了一個(gè)抽象方法,結(jié)果編譯器就提示了:存在多個(gè)抽象方法

在Java8之前婿禽,其實(shí)我們已經(jīng)接觸過(guò)函數(shù)式接口

比如Runnable 和 Comparable

只是沒(méi)有注解@FunctionalInterface赏僧。

那這個(gè)函數(shù)式接口要怎么用呢?

配合lambda食用扭倾,效果最佳(就是把lambda傳遞給函數(shù)式接口)淀零,示例代碼如下:

new Thread(() -> System.out.println("run")).start();

其中用到的函數(shù)式接口是Runnable

4. 什么是行為參數(shù)化

就是把行為定義成參數(shù),行為就是函數(shù)式接口

類(lèi)似泛型中的類(lèi)型參數(shù)化<T>膛壹,類(lèi)型參數(shù)化是把類(lèi)型定義成參數(shù)

行為參數(shù)化驾中,通俗點(diǎn)來(lái)說(shuō):

  • 就是用函數(shù)式接口形參
  • 然后傳入接口的各種實(shí)現(xiàn)內(nèi)容(即lambda表達(dá)式)作為實(shí)參
  • 最后在lambda內(nèi)實(shí)現(xiàn)各種行為(好像又回到多態(tài)的那一節(jié)了?這也是為啥多態(tài)是Java的三大特性的原因之一模聋,應(yīng)用太廣泛了)

這樣來(lái)看的話(huà)肩民,行為參數(shù)化和設(shè)計(jì)模式中的策略模式有點(diǎn)像了(后面章節(jié)會(huì)分別講常用的幾種設(shè)計(jì)模式)

下面我們手寫(xiě)一個(gè)函數(shù)式接口來(lái)加深理解吧

5. 手寫(xiě)一個(gè)函數(shù)式接口

下面我們循序漸進(jìn),先從簡(jiǎn)單的需求開(kāi)始

  • 第一步:比如我們想要讀取某個(gè)文件链方,那可以有如下方法:
public static String processFile() throws IOException {
    // Java7新增的語(yǔ)法此改,try(){},可自動(dòng)關(guān)閉資源侄柔,減少了代碼的臃腫
    try( BufferedReader bufferedReader = 
        new BufferedReader(new  FileReader("D:\\JavaProject\\JavaBasicDemo\\test.txt"))){
        return bufferedReader.readLine();
    }
}

可以看到共啃,核心的行為動(dòng)作就是 return bufferedReader.readLine();占调,表示讀取第一行的數(shù)據(jù)并返回

那如果我們想要讀取兩行呢?三行移剪?

  • 第二步:這時(shí)就需要用到上面的函數(shù)式接口了究珊,下面就是我們自己編寫(xiě)的函數(shù)式接口
@FunctionalInterface
interface FileReadInterface{
    // 這里接受一個(gè)BufferedReader對(duì)象,返回一個(gè)String對(duì)象
    String process(BufferedReader reader) throws IOException;
}

可以看到纵苛,只有一個(gè)抽象方法process()剿涮,它就是用來(lái)處理第一步中的核心動(dòng)作(讀取文件內(nèi)容)

至于想讀取多少內(nèi)容,那就需要我們?cè)趌ambda表達(dá)式中定義了

  • 第三步:接下來(lái)我們定義多個(gè)lambda表達(dá)式攻人,用來(lái)傳遞函數(shù)式接口取试,其中每個(gè)lambda表達(dá)式就代表了一種不同的行為,代碼如下:
// 讀取一行
FileReadInterface fileReadInterface = reader -> reader.readLine();
// 讀取兩行
FileReadInterface fileReadInterface2 = reader -> reader.readLine() + reader.readLine();

  • 第四步:我們需要修改第一步的processFile()怀吻,讓其接受一個(gè)函數(shù)式接口瞬浓,并調(diào)用其中的抽象方法,代碼如下:
// 參數(shù)為第二步我們自己手寫(xiě)的函數(shù)式接口
public static String processFile(FileReadInterface fileReadInterface) throws IOException {
        try( BufferedReader bufferedReader =
                 new BufferedReader(new FileReader("./test.txt"))){
                    // 這里我們不再自己定義行為蓬坡,而是交給函數(shù)式接口的抽象方法來(lái)處理猿棉,然后通過(guò)lambda表達(dá)式的傳入來(lái)實(shí)現(xiàn)多個(gè)行為
          return fileReadInterface.process(bufferedReader);
        }
    }
  • 第五步:拼接后,完整代碼如下:
public class FileReaderDemo {
    public static void main(String[] args) throws IOException {
                // 第三步: 
        // lambda表達(dá)式1 傳給 函數(shù)式接口:只讀取一行
        FileReadInterface fileReadInterface = reader -> reader.readLine();
                // lambda表達(dá)式2 傳給 函數(shù)式接口:只讀取兩行
        FileReadInterface fileReadInterface2 = reader -> reader.readLine() + reader.readLine();
        // 最后一步: 不同的函數(shù)式接口的實(shí)現(xiàn)屑咳,表現(xiàn)出不同的行為
        String str1 = processFile(fileReadInterface);
        String str2 = processFile(fileReadInterface2);
        System.out.println(str1);
        System.out.println(str2);
    }
    // 第四步: 讀取文件方法萨赁,接受函數(shù)式接口作為參數(shù)
    public static String processFile(FileReadInterface fileReadInterface) throws IOException {
        try( BufferedReader bufferedReader =
                 new BufferedReader(new FileReader("./test.txt"))){
                    // 調(diào)用函數(shù)式接口中的抽象方法來(lái)處理數(shù)據(jù)                   
          return fileReadInterface.process(bufferedReader);
        }
    }
    // 第一步:
  public static String processFile() throws IOException {
        try( BufferedReader bufferedReader =
                 new BufferedReader(new FileReader("./test.txt"))){
          return bufferReader.readLine();
        }
    }


}

// 第二步: 我們手寫(xiě)的函數(shù)式接口
@FunctionalInterface
interface FileReadInterface{
    String process(BufferedReader reader) throws IOException;
}

其實(shí)你會(huì)發(fā)現(xiàn),我們手寫(xiě)的這個(gè)函數(shù)式接口兆龙,其實(shí)就是Function<T>去除泛型化后的接口杖爽,如下所示:

@FunctionalInterface
public interface Function<T, R> {
    // 都是接受一個(gè)參數(shù),返回另一個(gè)參數(shù)
  R apply(T t);
}

下面我們列出Java中常用的一些函數(shù)式接口紫皇,你會(huì)發(fā)現(xiàn)自帶的已經(jīng)夠用了掂林,基本不會(huì)需要我們自己去寫(xiě)

這里的手寫(xiě)只是為了自己實(shí)現(xiàn)一遍,可以加深理解程度

6. 常用的函數(shù)式接口

常用的函數(shù)式接口

7. 什么是方法引用

我們先看一個(gè)例子

前面我們寫(xiě)的lambda表達(dá)式坝橡,其實(shí)還可以簡(jiǎn)化泻帮,比如

// 簡(jiǎn)化前
Function<Cat, Integer> function = c->c.getAge();
// 簡(jiǎn)化后
Function<Cat, Integer> function2 = Cat::getAge;

其中簡(jiǎn)化后的Cat::getAge,我們就叫做方法引用

方法引用就是引用類(lèi)或?qū)ο蟮姆椒?/strong>计寇;

下面我們列出方法引用的三種情況:

  1. Object::instanceMethod(對(duì)象的實(shí)例方法)
  2. Class::staticMethod(類(lèi)的靜態(tài)方法)
  3. Class::instanceMethod(類(lèi)的實(shí)例方法)

像我們上面舉的例子就是第三種:類(lèi)的實(shí)例方法

下面我們用代碼演示上面的三種方法:

public class ReferenceDemo {
    public static void main(String[] args) {
        // 第一種:引用對(duì)象的實(shí)例方法
        Cat cat = new Cat(1);
        Function<Cat, Integer> methodRef1 = cat::getSum; 
        // 第二種:引用類(lèi)的靜態(tài)方法
        Supplier<Integer> methodRef2 = Cat::getAverageAge;
        // 第三種:引用類(lèi)的實(shí)例方法
        Function<Cat, Integer> methodRef3 = Cat::getAge;
    }
}
class Cat {
    int age;

    public Cat(int age) {
        this.age = age;
    }

    // 獲取貓的平均年齡
    public static int getAverageAge(){
        return 15;
    }
    // 獲取兩只貓的年齡總和
    public int getSum(Cat cat){
        return cat.getAge() + this.getAge();
    }

    public int getAge() {
        return age;
    }    public void setAge(int age) {
        this.age = age;
    }
}

為啥要用這個(gè)方法引用呢锣杂?

方法引用好比lambda表達(dá)式的語(yǔ)法糖,語(yǔ)法更加簡(jiǎn)潔番宁,清晰

一看就知道是調(diào)用哪個(gè)類(lèi)或?qū)ο蟮哪膫€(gè)方法

8. 什么是構(gòu)造引用

上面介紹了方法引用元莫,就是直接引用某個(gè)方法

這里的構(gòu)造引用同理可得,就是引用某個(gè)類(lèi)的構(gòu)造方法

構(gòu)造引用的表達(dá)式為:Class::new蝶押,僅此一種

如果你有多個(gè)構(gòu)造函數(shù)踱蠢,那編譯器會(huì)自己進(jìn)行推斷參數(shù)(你看看,多好,多簡(jiǎn)潔)

比如下面的代碼:

// 這里調(diào)用 new Cat()
Supplier<Cat> constructRef1 = Cat::new;
// 這里調(diào)用 new Cat(Integer)
Function<Integer, Cat> constructRef2 = Cat::new;

9. lambda表達(dá)式中引入外部變量的限制

要求引入lambda表達(dá)式中的變量茎截,必須是最終變量苇侵,即該變量不會(huì)再被修改

比如下面的代碼:

public static void main(String[] args) {
  String str = "javalover.cc";
  Runnable runnable = ()->{
    str = "1";// 這里會(huì)報(bào)錯(cuò),因?yàn)樾薷牧藄tr引用的指向
    System.out.println(str);
  }
}

可以看到企锌,lambda表達(dá)式引用了外面的str引用榆浓,但是又在表達(dá)式內(nèi)部做了修改,結(jié)果就報(bào)錯(cuò)了

為啥要有這個(gè)限制呢撕攒?

為了線(xiàn)程安全陡鹃,因?yàn)閘ambda表達(dá)式有一個(gè)好處就是只在需要的時(shí)候才會(huì)執(zhí)行,而不是調(diào)用后立馬執(zhí)行

這樣就會(huì)存在多個(gè)線(xiàn)程同時(shí)執(zhí)行的并發(fā)問(wèn)題

所以Java就從根源上解決:不讓變量被修改抖坪,都是只讀的

那你可能好奇萍鲸,我不把str的修改代碼放到表達(dá)式內(nèi)部可以嗎?

也不行擦俐,道理是一樣的脊阴,只要lambda有用到這個(gè)變量,那這個(gè)變量不管是在哪里被修改捌肴,都是不允許的

不然的話(huà)蹬叭,我這邊先執(zhí)行了一次lambda表達(dá)式藕咏,結(jié)果你就改了變量值状知,那我第二次執(zhí)行l(wèi)ambda,不就亂了嗎

10. lambda的組合操作

最后是lambda的必殺技:組合操作

在這里叫組合或者復(fù)合都可以

概述:組合操作就是先用一個(gè)lambda表達(dá)式孽查,然后再在后面組合另一個(gè)lambda表達(dá)式饥悴,然后再在后面組合另另一個(gè)lambda表達(dá)式,然后盲再。西设。。有點(diǎn)像是鏈?zhǔn)讲僮?/p>

學(xué)過(guò)JS的都知道Promise答朋,里面的鏈?zhǔn)讲僮骶秃瓦@里的組合操作很像

用過(guò)Lombok的朋友贷揽,應(yīng)該很熟悉@Builder注解,其實(shí)就是構(gòu)造者模式

下面我們用代碼演示下組合操作:

// 重點(diǎn)代碼
public class ComposeDemo {
    public static void main(String[] args) {
        List<Dog> list = Arrays.asList(new Dog(1,2), new Dog(1, 1));
        // 1. 先按年齡排序(默認(rèn)遞增)
        // Dog::getAge, 上面介紹的方法引用
        // comparingInt, 是Comparator的一個(gè)靜態(tài)方法梦碗,返回Comparator<T>
        Comparator<Dog> comparableAge = Comparator.comparingInt(Dog::getAge);
        // 2. 如果有相同的年齡禽绪,則年齡相同的再按體重排序(如果年齡已經(jīng)比較出大小,則下面的體重就不會(huì)再去比較)
        Comparator<Dog> comparableWeight = Comparator.comparingInt(Dog::getWeight);;
        // 3. 調(diào)用list對(duì)象的sort方法排序洪规,參數(shù)是Comparator<? super Dog>
        list.sort(comparableAge.thenComparing(comparableWeight));
        System.out.println(list);
    }
}
// 非重點(diǎn)代碼
class Dog{
    private int age;
    private int weight;

    public Dog(int age, int weight) {
        this.age = age;
        this.weight = weight;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "age=" + age +
                ", weight=" + weight +
                '}';
    }
}

輸出:[Dog{age=1, weight=1}, Dog{age=1, weight=2}]

比較的流程如下所示:

組合操作

總結(jié)

  1. lambda的語(yǔ)法: 參數(shù)+符合+表達(dá)式或語(yǔ)句印屁,比如(a,b)->{System.out.println("javalover.cc");}

  2. 函數(shù)式接口:只有一個(gè)抽象方法,最好加@FunctionalInterface斩例,這樣編譯器可及時(shí)發(fā)現(xiàn)錯(cuò)誤雄人,javadoc也說(shuō)明這是一個(gè)函數(shù)式接口(可讀性)

  3. 行為參數(shù)化:就是函數(shù)式接口作為參數(shù),然后再將lambda表達(dá)式傳給函數(shù)式接口念赶,通過(guò)不同的lambda內(nèi)容實(shí)現(xiàn)不同的行為

  4. 方法引用:lambda的語(yǔ)法糖础钠,總共有三種:

    • Object::instanceMethod(對(duì)象的實(shí)例方法)

    • Class::staticMethod(類(lèi)的靜態(tài)方法)

    • Class::instanceMethod(類(lèi)的實(shí)例方法)

  5. 構(gòu)造引用:就一種恰力,編譯器自己可判斷是哪個(gè)構(gòu)造函數(shù),語(yǔ)法為Class::new

  6. 在lambda中引入外部變量珍坊,必須保證這個(gè)變量是最終變量牺勾,即不再被修改

  7. lambda的組合操作,就是鏈?zhǔn)讲僮髡舐M合是通過(guò)函數(shù)式接口的靜態(tài)方法來(lái)組合(靜態(tài)方法會(huì)返回另一個(gè)函數(shù)式接口的對(duì)象)

比如list.sort(comparableAge.thenComparing(comparableWeight));

后記

最后驻民,感謝大家的觀(guān)看,謝謝

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末履怯,一起剝皮案震驚了整個(gè)濱河市回还,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叹洲,老刑警劉巖柠硕,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異运提,居然都是意外死亡蝗柔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)民泵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)癣丧,“玉大人,你說(shuō)我怎么就攤上這事栈妆⌒脖啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鳞尔,是天一觀(guān)的道長(zhǎng)嬉橙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)寥假,這世上最難降的妖魔是什么市框? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮糕韧,結(jié)果婚禮上枫振,老公的妹妹穿的比我還像新娘。我一直安慰自己兔沃,他們只是感情好蒋得,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著乒疏,像睡著了一般额衙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天窍侧,我揣著相機(jī)與錄音县踢,去河邊找鬼。 笑死伟件,一個(gè)胖子當(dāng)著我的面吹牛硼啤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斧账,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谴返,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了咧织?” 一聲冷哼從身側(cè)響起嗓袱,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎习绢,沒(méi)想到半個(gè)月后渠抹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闪萄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年梧却,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片败去。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡放航,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出为迈,到底是詐尸還是另有隱情三椿,我是刑警寧澤缺菌,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布葫辐,位于F島的核電站,受9級(jí)特大地震影響伴郁,放射性物質(zhì)發(fā)生泄漏耿战。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一焊傅、第九天 我趴在偏房一處隱蔽的房頂上張望剂陡。 院中可真熱鬧,春花似錦狐胎、人聲如沸鸭栖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晕鹊。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間溅话,已是汗流浹背晓锻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留飞几,地道東北人砚哆。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像屑墨,于是被迫代替她去往敵國(guó)和親躁锁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容