Dart 语法篇(全)

Dart语言简介

对于Dart这一门语言,百度百科是这么解释的:Dart是谷歌开发的计算机编程语言,它被应用于web、服务器、移动应用和物联网等领域的开发,它是面向对象、类定义、单继承的语言,当然啦~ 楼主来了解这个语言就是为了了解用Dart来开发的Flutter应用。

Dart SDK 和 IDE的安装

要学某种语言,首先当然是SDK 和 IDE的安装
Dart SDK的安装可以参考官网的命令来安装:Dart官网
⚠️:如果在安装的过程中出错,可以看看https://blog.csdn.net/qq_40028324/article/details/105692357这篇文章能否帮你解决问题(毕竟楼主是依靠这篇文章来解决问题的 亲测有效~)

关于Dart的开发工具选择比较多,有IDEA、VS Code 等 楼主这里使用的是IDEA 每个开发工具的具体安装参考官网即可,由于楼主使用的是IDEA,我这里就拿IDEA来举例。

安装IDEA之后,打开IDEA,搜索Dart插件安装,到此 我们的Dart整个开发环境就搭建好了,接着就要进入正题了~

Hello World

// main 方法是程序入口
void main() {
print("Hello World"); // 输出:Hello World
}

一、Dart中的数据类型

1、变量和常量

变量:var
常量:const ⚠️:使用const声明的必须是编译期变量
关键字final:声明只能赋值一次的变量
🌰:

void main() {
  var a = 10;
  final b = 20;
  const c = 30;
}

2、数值型:num

num 只有int 和 double两种类型
常用的操作:+ - * / % ~/
常用属性:isNaN(是否不是数字),isEven,isOdd......
🌰:

void main() {
  num a = 12.5;
  int b = 20;

  print(b ~/a); // 1  ~/表示除之后对结果取整
  print(b/a); // 1.6   
  print((0.0/0.0).isNaN); // true
  print(b.isEven);// 是否是偶数 true
  print(b.isOdd);// 是否是奇数 false

}

3、字符串:String

""''都可以创建字符串
"""''' 创建多行的字符串
r创建一个原始的字符串
常用操作:+ * == []
常用属性:length、isEmpty、split()......
插值表达式:${expression}
🌰:

void main () {
  String str0 = "Hello"; // ''
  String str1 = '''Hello
  World!!!''';
  String str2 = r'Hello \n Dart';

  String str3 = 'Hello';
  String str4 = str3 + ' World';

  print(str0);
  print(str1);
  print(str2); // Hello \n Dart
  print(str4); // Hello World
  print(str3 * 2); // HelloHello
  print(str3 == str4); // false
  print(str3[4]); // o

  int a = 1;
  int b = 2;
  print("a + b = ${a+b}"); // a + b = 3
  print(str4.substring(0,3)); // Hel
  print(str4.split(' '));// [Hello, World]
  
}

4、布尔类型:bool

true、false

5、列表(数组):List

构造创建:var list = new List();
🌰:

void main () {

  var list = [1,2,3,"Dart",false]; // list里面的元素可以是多种类型 不要求是同一种类型
  print(list); // [1,2,3,"Dart",false]

  var list1 = const [1,2,3];
  // list1[1] = 6; // 运行会报异常

  list.insert(0, 999); 
  print(list); // [999,1,2,3,"Dart",false]

  // 遍历List
  list.forEach((element) {
    print(element);
  });
  
  print(list.indexOf(6)); // 如果没有找到对应的元素 返回结果是 -1

  var list2 = new List();
}

6、键值对:Map

创建Map:var language = {'first':''Dart,'second':'Java'};
创建不可变的Map:var language = const {'first':''Dart,'second':'Java'};
构造创建:var language = new Map();
常用的属性、操作:length、keys、values、forEach(遍历)、containsKey(是否包含某个key)......
asMap():把list转成map
🌰:

void main() {

  var map0 = {"first":"Dart",1:true, true:666,"OC":null};// key 和 value 都可以是任意类型
  print(map0["OC"]);// null

  var map = {"first":"Dart","second":"Swift","third":"Java"};
  map.forEach(func);

  var list = ["Dart","Swift","Java"];
  print(list.asMap()); // {0: Dart, 1: Swift, 2: Java}
}

void func(key,value) {
  print("key = $key, value = $value");
}

7、dynamic(动态型)

🌰:

void main() {

  var a;
  a = 10;
  a = "Dart";

  dynamic b = 10; // dynamic 表示是动态类型 如果写int b = 10; b = "Dart"是会报错的 因为int b = 10 默认b就是int类型
  b = "Dart";
}

二、运算符

1、算术运算符

常用的操作:+、-、*、/、~/、%
递增递减:++var、var++、--var、var--
🌰:

void main () {

  int a = 1;
  print(a + 1); // 2
  print(a++); // 1
  print(a); // 2
  print(--a);// 1
}

2、关系运算符

运算符:==、!=、>、<、>=、<=

3、逻辑运算符

运算符:!、&&、||

4、赋值运算符

基础运算符:=、??=
复合运算符:+=、 -= 、/= 、*= 、%= 、~/=
🌰:

void main () {
  int a = 10;
  int b;
  // ??= 表示如果有值就是原来的值 如果没有值就是赋的值
  b ??= 20;
  print(b); // 20
  int c = 10;
  c ??= 20;
  print(c); // 10
  print(a ~/= 5); // 2
}

5、条件表达式

三目运算符:?:
??运算符:expre1 ?? expre2 表示如果第一个表达式为nil 则取第二个表达式
🌰:

void main () {
  String a;
  String b = "Dart";
  String c = a ?? b;
  print(c); // Dart

}

三、控制语句

1、if 、if else

2、for 循环、for...in 语句

🌰:

void main () {
  var list = [1,2,3,4,5,6];
  for(int index = 0; index < list.length; index++) {
    // print(list[index]);
  }

  for(var item in list) {
    print(item);
  }
  
}

3、while、do...while

🌰:

void main() {

  int count = 0;
  while(count < 5) {
    print(count++); // 0 1 2 3 4
  }
  // count = 5

  do {
    print(count --);
  }while(count > 0 && count < 5); // 5 4 3 2 1
}

4、break、continue

break:终止循环
continue:跳出当前循环
🌰:

void main () {
  var list1 = [1,2,3];
  var list2 = [4,5,6];
  for(var item1 in list1) {
    for(var item2 in list2) {
      if(item2 == 5) {
        // break; // 4 4 4
        continue; // 46 46 46
      }
      print(item2);
    }
  }
}

5、switch...case语句

5.1 比较类型:num、String、编译期常量、对象、枚举
5.2 非空case必须有一个break
5.3 使用default来处理默认情况
5.4 可以用continue来加一个跳转标签(其他语言没有的)
🌰:

void main () {

  String language = "Python";
  switch(language) {
    Test: // 标签随便定义的
    case "Java":
      print("Java");
      break;
    case "Dart":
      print("Dart");
      break;
    case "Python":
      print("Python");
      continue Test;
      // break;
    default:
      print("None");
  }
  
  // 输出结果是:Python、Java
}

四、方法

1、方法的定义

返回类型 方法名 (参数1,参数2...){
方法体...
return 返回值
}

2、方法特性

2.1 方法也是对象 并且具有具体的类型Function
2.2 方法的返回值、参数类型都可以省略
2.3 方法都有返回值 如果没有返回值默认是null
🌰:

void main (List args) {
  printPerson("Sunny", 18); // 输出:name = Sunny,age = 18
}

 printPerson (name, age) {
   print("name = $name,age = $age");
 }

2.4 箭头语法: => expre 是return{expre}的缩写 ⚠️:只适用于一个表达式
🌰:

void main (List args) {
  print(printPerson("Sunny", 18)); // 输出:name = Sunny,age = 18
}

printPerson (name, age) => "name = $name,age = $age";

3、可选参数

3.1可选命名参数:{params1,params2,params3...}
3.2 可选位置参数:[params1,params2,params3...]
🌰:

void main () {

  printPerson("Sunny",age: 20);

  printPerson2("Sunshine",20,"Male");
}

// 可选命名参数{}
printPerson(String name,{int age, String gender}) {
  print("name = $name, age = $age, gender = $gender"); // 打印结果:name = Sunny, age = 20, gender = null

}

// 位置可选参数[]
printPerson2(String name,[int age, String gender]) {
  print("name = $name, age = $age, gender = $gender"); // 打印结果:name = Sunny, age = 20, gender = Male

}

⚠️:如果存在可选参数 可选参数必须要在必选参数后面

4、默认参数值

使用=在可选参数指定默认值 默认参数值只能是编译期常量
🌰:

void main () {

  printPerson("Sunny",gender: "Male");

}

// 可选命名参数{}
printPerson(String name,{int age = 20, String gender = "Female"}) {
  print("name = $name, age = $age, gender = $gender"); // 打印结果:name = Sunny, age = 20, gender = Male

}

5、方法对象

5.1 方法可以作为对象赋值给其他的对象
🌰:

void main() {

  var func = printHello;
  func();

  List list = [1,2,3,4];
  list.forEach(print);// 输出:1,2,3,4

void printHello() {
  print("Hello Dart");
}

5.2 方法可以作为参数传递给其他方法
🌰:

void main() {
  print(listPrint(["D","a","r","t"], timers)); // 打印结果:[DDD,aaa,rrr,ttt]
}

// 传入一个字符串 把这个字符串打印3次
String timers (str) {
  return str * 3;
}

List listPrint(List list,timers) {
  for(int index = 0; index < list.length; index++) {
    list[index] = timers(list[index]);
  }
  return list;
}

6、匿名方法

匿名方法的特性:
6.1 可赋值给变量 通过变量来进行调用
6.2 可以在其他方法中直接调用或传递给其他的方法

(参数1,参数2) {
方法体
return 返回值
}

🌰:

void main () {

  var func = (){
    print("Hello Dart");
  };
  func();

  print(listPrint(["1","2","3","4"], (str){ return str*3;}));// 打印结果:[111,222,333,444]
}

List listPrint(List list,timers) {
  for(int index = 0; index < list.length; index++) {
    list[index] = timers(list[index]);
  }
  return list;
}

7、闭包

闭包也是一个方法(对象),闭包定义在其他方法内部,闭包能够访问外部变量的局部变量 并持有其状态
🌰:

void main() {

  var func = funca();
  func(); // 0
  func(); // 1
  func(); // 2
}

funca () {
  int count = 0;
  printCount() {
    print(count++);
  }
  return printCount;
}

也可以是下面的写法:

void main() {

  var func = funca();
  func(); // 0
  func(); // 1
  func(); // 2
}

funca () {
  int count = 0;
  return () {
    print(count++);
  };
}

五、Dart面向对象编程

1、类与对象

使用关键词class来声明一个类
使用关键字new来创建一个类 new可忽略
所有的对象默认都继承Object

属性与方法:
默认会生成settergetter
使用final关键字声明的属性 只会生成getter
在Dart中方法是不能被重载的

类及成员的可见性:
在Dart中的可见性是一以library为单位的
默认情况下 每一个Dart文件就是一个库library
使用表示库的私有性(表示属性、方法的私有性也是通过来表示的)
使用import导入库
🌰:

import 'person.dart';

void main() {
  var person =  new Person();
  person.name = "Sunny";
  person.age = 18;
  // person.bookName = "123";
  person.work();
  print(person.bookName);
}

// person.dart文件
class Person {
  String name;
  int age;
  final String bookName = "《平凡的世界》";
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }
}

⚠️:如果Person前面使用了_ 就表示是私有,其他类就找不到这个类了

class _Person {

}

2、 计算属性

计算属性的值是通过计算得来的 本身并不存储值
计算属性赋值 其实是通过计算转换到其他的实例变量
🌰:

void main () {

  var rect = Rectangle();
  rect.width = 10;
  rect.height = 20;
  print(rect.area);

  rect.area = 200;
  print(rect.width);
}

class Rectangle {
  num width;
  num height;
  // 计算属性getter
  num get area {
    return width * height;
  }

  // 计算属性setter
  set area(value) {
     width = value/ 10;
  }
}

3、构造方法

如果没有自定义的构造方法,会有一个默认的构造方法
🌰:

void main() {
  var person = new Person();
}

class Person {
  
// 默认的构造方法
  Person () {
  }
}

如果存在自定义的构造方法,则默认的构造方法就无效
自定义构造方法的写法一🌰:

void main() {
  var person =  new Person("Sunny", 18);
  person.work();
}

class Person {
  String name;
  int age;
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }

  // 构造方法
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

自定义构造方法的写法二🌰:

void main() {
  var person =  new Person("Sunny", 18,"Female");
  person.work();
}

class Person {

  String name;
  int age;
  final gender;
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }

  Person(this.name, this.age,this.gender) {
    print(this.gender);// Female
  }
}

⚠️:如果对person的gender赋值,第一种构造方法就不能使用,因为不能对final修饰的属性赋值,但是第二种构造方式是可以的,因为第二种的构造方法赋值是在构造方法之前

构造方法不能重载,但是可以使用重命名的构造方法
🌰:

void main() {
  var person = new Person.withName("Sunny");
  person.work();
}

class Person {

  String name;
  int age;
  final gender;
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }

  Person.withName(this.name) {
     print(this.name); // Sunny
  }
}

4、常量构造方法

如果类是不可变的状态,可以把对象定义为编译时常量
使用const来声明构造方法,并且所有变量都是final
使用const声明对象,可以省略
🌰:

void main() {
  const person =  const Person("Sunny", 18,"Female");
  person.work();
}

class Person {
  final name;
  final age;
  final gender;
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }

  const Person(this.name, this.age,this.gender);

}

5、工厂构造方法

工厂构造方法类似于设计模式里面的工厂模式
在构造方法添加factory实现一个构造方法
在工厂构造方法里面可以返回对象
🌰:

void main() {

  var logger = Logger("Sunny");
  print(logger.name);
}

class Logger {
  final String name;
  static final Map<String,Logger> _cache = <String, Logger>{};
  factory Logger(String name) {
    if(_cache.containsKey(name)) {
      return _cache[name];
    }else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  // 表示这个方法是私有
  Logger._internal(this.name);
}

6、初始化列表

初始化列表会在构造方法之前执行
使用分隔初始化表达式
初始化列表常用于设置final变量的值
🌰:

void main() {
  // var person =  new Person("Sunny", 18,"Female");
  var person = new Person.withMap({"name":"Sunny","age":18});
  person.work();
}

class Person {
  String name;
  final age;
  final gender;
  void work() {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }
  
  Person(this.name, this.age,this.gender);
  Person.withMap(Map map):age = map["age"], gender = map["gender"] {
    this.name = map["name"];
    print("name = $name,age = $age,gender = $gender"); // name = Sunny,age = 18,gender = null
  }

}

7、静态成员

使用 static关键字来实现类级别的变量和函数
静态成员不能访问非静态成员,非静态成员可以访问静态成员
类中的常量需要使用static const声明
🌰:

void main() {
  var page = Page();
  Page.scrollDown(); // 不能通过page.scrollDown()来调用

}

class Page {
  static const maxPage = 10;
  static int currentPage = 1;
  // 下滑
  static void scrollDown() {
    currentPage = 1;
    print("scrollDown...");
  }

  // 上滑
  void scrollUp() {
    currentPage++;
    print("scrollUp...");
  }
}

8、对象操作符

条件成员访问:?
类型转换:as
是否是指定类型:is is!
级联操作:..
🌰:

void main () {
  // Person person;
  // person.work();// 会报错 因为person是空
  // person?.work(); // 不会报错 检查到person是nil 就不往下执行

  // var person;
  // person = "Sunny";
  // person = new Person();
  // (person as Person).work();
  // if(person is Person) {
  //   print("person is Person...");
  // }
  // if(person is! Person) {
  //   print("person is String...");
  // }

  var person = new Person();
  person..name = "Sunny"
  ..age = 18
  ..work();

}

class Person {
  String name;
  int age;
  void work() {
    print("working...");
  }
}

9、对象call方法(不建议使用)

如果类使用了call()方法 则该类的对象可以作为方法使用
🌰:

void main() {

  var person = new Person();
  person("Sunny",18);

}

class Person {
  String name;
  int age;
  call(String name,int age) {
    print("name = $name, age = $age, She is working...");// name = Sunny, age = 18, She is working...
  }
}

10、继承

使用extends表示继承一个类
继承可以继承父类可见的方法和属性,不会继承构造方法
子类能够复写父类的setter 和 getter
单继承、多态
🌰:

void main() {

  var student = new Student();
  student.name = "Sunny";
  student.age = 18;
  print(student.isAdult);
  student.study();
  student.run();
}

class Student extends Person {
  void study() {
    print("Student is studying...");
  }

  @override
  bool get isAdult => age >15; // 重写父类的方法

  @override
  void run() {
    super.run();
    print("Student is running...");
  }
}

Person:

class Person {
  String name;
  int age;
  String _birthday;
  bool get isAdult => age > 18;
  void run () {
    print("person is running...");
  }
}

11、继承中的构造方法

子类的构造方法默认会去调用父类的无名无参的构造方法
如果父类没有无名无参的构造方法 则需要显式调用父类的构造方法
在构造方法参数后面使用:显式调用父类的构造方法
🌰:

void main() {
  var student = new Student("Sunny", 18,"Female");
}

class Person {
  String name;
  int age;
  Person(this.name,this.age);
  Person.initWithNameAndAge(this.name,this.age);
}

class Student extends Person {
  final gender;
  // 方式1
  // Student(String name, int age) : super(name, age);// 使用快捷键可以自动补全

  // 方式2
  Student(String name, int age,String gender) :gender = gender, super.initWithNameAndAge(name,age);

  // 方式3
  // Student.initWithNameAndAge(String name, int age) : super.initWithNameAndAge(name,age);
}

⚠️:初始化列表和`super`的顺序不能交换 会报错

构造方法的执行顺序:父类的构造方法在子类的构造方法开始执行之前去调用

12、抽象类

抽象类使用 abstract表示 不能直接被实例化
抽象方法不适用 abstract 修饰 无实现
抽象类可以没有抽象方法
有抽象方法的类一定得声明为抽象类
🌰:

void main () {
  var student = new Student();
  student.run();
}

abstract class Person {
  void run();
}

class Student extends Person {
  @override
  void run() {
    // TODO: implement run
    print("Student is running....");
  }
}

13、接口

在Dart中 类和接口是统一的 类就是接口
每个类都隐式定义了一个包含所有实例成员的接口
如果是复用已有类的实现 使用extends
如果只是使用已有类的一些外在行为 使用接口implements
🌰:

void main () {
  var student = new Student();
  student.run();

}

abstract class Person {
  void run();
}

class Student implements Person {
  @override
  void run() {
    print("Student is running...");
  }
}

14、Mixins

Mixins 类似于 多继承 是在多继承中使用一个类代码的方式
Mixins不能有显式声明构造方法
Mixins的类只能继承自Object
使用关键字with连接一个或多个mixin
🌰:

void main () {
  var d = new D();
  d.a(); // 输出:C...a 跟mixins顺序有关 最后是C 所以会输出C类里面a的打印
}

class A {
  void a() {
    print("A....a");
  }
}

class B {
  void a() {
    print("B....a");
  }
  void b() {
    print("B....b");
  }
}

class C {
  void a() {
    print("C....a");
  }
  void c() {
    print("C....c");
  }
}

// 现在 如果希望一个类D 能够有ABC三个类的方法
class D extends A with B,C {

}

15、操作符覆写

覆写操作符需要在类中定义:

返回类型 operator 操作符 (参数1,参数2...) {
实现体
return 返回值
}

如果需要覆写 == 还需要覆写对象的hashCode getter方法
🌰:

void main () {
  var person1 = new Person();
  person1.age = 18;
  var person2 = new Person();
  person2.age = 20;
  print(person1 > person2); // false
}

class Person {
  int age;
  // 覆写操作符
  bool operator > (Person person) {
    return this.age > person.age;
  }
}

六、枚举和泛型

1、枚举

枚举是一种有穷序列集的数据类型
使用关键字 enum 来定义一个枚举
常用于代替常量 和 控制语句

Dart中枚举特性:
index 从0开始 依次累加
不能指定原始值
不能添加方法
🌰:

void main() {
  var cuurentSeason = Season.Summer;
  switch(cuurentSeason) {
    case Season.Spring:
      print("Spring...");
      break;
    case Season.Summer:
      print("Summer...");
      break;
    case Season.Automn:
      print("Automn...");
      break;
    case Season.Winter:
      print("Winter...");
      break;
      Default:
      print("None...");
  }
}

enum Season {
  Spring,
  Summer,
  Automn,
  Winter
}

2、泛型

Dart中类型是可选的 可使用泛型指定类型
使用泛型能有效的减少代码的重复
类的泛型
方法的泛型
🌰:

void main () {

    var utils = new Utils();
    utils.put<String>("Sunny");
    utils.put<int>(1);
}

class Utils {

  void put<T>(T element) {
    print(element);
  }
  
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容