<Dart基础>Dart语法(上)

Dart基础系列:

说明:
Dart系列的开篇(Dart简介)总结了Dart语言的相关知识,涉及到一些基础的概念,并介绍了如何搭建其开发环境。从这一篇开始,我将对Dart的语法进行总结,分为上下两篇,其中本篇的内容如下:

内容:

  • 关键字
  • 数据类型
    变量与常量
    数据类型(内置类型)
  • 操作符
    算数运算符;关系运算符;逻辑运算符;赋值运算符;
    位和移位操作符;条件表达式;类型判定操作符;其他操作符
    级联操作符
  • 控制语句
  • 注释

一、关键字

关键字
abstract [1] do import [1] super
as [1] var continue dynamic [1]
assert [2] else interface sync *[2]
enum is implements [1] export [1]
async *[2] this library [1] throw
await new covariant true
break null external [1] factory [1]
case try extends typedef [1]
catch false operator [1] switch
class final finally void
const part [1] rethrow while
in for return with
mixin get [1] set [1] yield *[2]
default if static [1] deferred [1]

带有[1] 的关键字是 内置关键字。避免把内置关键字当做标识符使用。 也不要把内置关键字 用作类名字和类型名字。 有些内置关键字是为了方便把 JavaScript 代码移植到 Dart 而存在的。 例如,如果 JavaScript 代码中有个变量的名字为 factory, 在移植到 Dart 中的时候,你不必重新命名这个变量。

带有[2] 的关键字,是在 Dart 1.0 发布以后又新加的,用于 支持异步相关的特性。 你不能在标记为 async*、或者 sync* 的方法体内 使用 asyncawait、或者 yield 作为标识符。 详情请参考:异步支持

所以其他单词都是 保留词。 你不能用保留词作为关键字。

二、数据类型

1、变量与常量

  • 变量声明与初始化:
    1.使用var 声明变量,此时没有明确类型,编译的时候根据值明确类型
    2.明确变量类型,来帮助 Dart 去捕获异常以及 让代码运行的更高效
    3.未初始化时,变量默认值为null
    4.如果对象不限于单一类型(没有明确的类型),请使用Object或dynamic关键字
      var name = ‘Bob’; 
      Object name1 = '张三';
       //动态类型
      dynamic name2 = '李四';
      // 显示声明将被推断类型, 可以使用String显示声明字符串类型
      String name3 = 'Bob' ;
    
  • final and const
    1.被finalconst 修饰的变量只能赋值一次,不可更改变量值
    2.一个 final 变量只能被初始化一次; const变量是一个编译时常量,(Const变量是隐式的final),如果 const 变量在类中,请定义为 static const。
    3.被final或者const修饰的变量,变量类型可以省略,建议指定数据类型。
    4.被final修饰的顶级变量或类变量在第一次声明的时候就需要初始化,否则会提示错误:
    // The final variable 'outSideFinalName' must be initialized.
    final String outSideFinalName
    
    5.const关键字不只是声明常数变量,您也可以使用它来创建常量值,以及声明创建常量值的构造函数,任何变量都可以有一个常量值。
     // 注意: [] 创建的是一个空的list集合
     // const []创建一个空的、不可变的列表(EIL)。
     var varList = const []; // varList 当前是一个EIL
     final finalList = const []; // finalList一直是EIL
     const constList = const []; // constList 是一个编译时常量的EIL
    
     // 可以更改非final,非const变量的值
     // 即使它曾经具有const值
     varList = ["haha"];
    
     // 不能更改final变量或const变量的值
     // 这样写,编译器提示:a final variable, can only be set once
     // finalList = ["haha"];
     // 这样写,编译器提示:Constant variables can't be assigned a value  
     // constList = ["haha"];
    
    注意:实例变量可以为 final 但是不能是 const 。

2、数据类型(内置类型)

2.1、num(数值)

1、int:其取值通常位于 -253 和 253 之间
2、double:64-bit (双精度) 浮点数,符合 IEEE 754 标准。
3、数值型操作:

  • 运算符:+-*/~/%
    其中~/为整除
  • 常用属性:isNaN (是否非数字),isEven(是否为偶数),isOdd(是否为奇数)
  • 常用方法:abs()round()floor()ceil()toInt()toDouble()
     int a = 1;
     print(a);
     
     double b = 1.12;
     print(b);
     
     // String -> int
     int one = int.parse('1');
     // 输出3
     print(one + 2);
     
     // String -> double
     var onePointOne = double.parse('1.1');
     // 输出3.1
     print(onePointOne + 2);
    
     // int -> String
     String oneAsString = 1.toString();
     // The argument type 'int' can't be assigned to the parameter type 'String'
     //print(oneAsString + 2);
     // 输出 1 + 2
     print('$oneAsString + 2');
     // 输出 1 2
     print('$oneAsString 2');
    
     // double -> String 注意括号中要有小数点位数,否则报错
     String piAsString = 3.14159.toStringAsFixed(2);
     // 截取两位小数, 输出3.14
     print(piAsString);
     
     String aString = 1.12618.toStringAsFixed(2);
     // 检查是否四舍五入,输出1.13,发现会做四舍五入
     print(aString);
    
2.2、String(字符串)

1、String的创建:
- 使用单引号'',双引号""
- 使用三个 引号'''或双引号""" 创建多行字符串
- 使用r 创建原始 raw 字符串
var s1 = '单引号字符串'; var s2 = "双引号字符串"; var s3 = ''' 单引号多创建的多行的字符串, 第二行 '''; var s4 = """ 双引号多创建的多行的字符串, 第二行 """; //使用r创建原始’raw‘字符串,\n不换行 var s5 = r"使用r创建原始’raw‘字符串,\n不换行";
2、字符串操作:

  • 运算符:+*==[]

     String a = "hello";
     String b = a + " Dart!";
     //hello Dart!
     print(b);
     
     String c = b*2;
     //hello Dart!hello Dart!
     print(c);
     
     String d = "hello Dart!";
     //true
     print(d==b);
     
     String e = d[0];
     //h
     print(e);
    
  • 插值表达式:${expression}

    int e1 = 1;
    int e2 = 2;
    //a = 1
    print("a = $a");
    //e1 + e2 = 3
    print("e1 + e2 = ${e1 + e2}");
    
  • 常用属性:length(长度),isEmpty(是否为空),isNotEmpty(是否非空)

  • 常用方法:contains()subString()startsWith()endsWith()indexOf()lastIndexOf()toLowerCase()toUpperCase()trim()trimLeft()trimRight()split()replaceXxx()

  • 更多API详见官网:String class

3、StringBuffer

  • 使用 StringBuffer 创建字符串,当调用其 toString() 函数时,才会创建一个 新的 String 对象。
  • write() :将参数字符串加入StringBuffer 对象中
  • writeAll() :此函数有一个可选的参数来指定每个字符串的分割符
    var sb = new StringBuffer();
    //这里用到了级联操作符
    sb..write('Use a StringBuffer for ')
      ..writeAll(['efficient', 'string', 'creation'], ' ')
      ..write('.');
    
    var fullString = sb.toString();
    //Use a StringBuffer for efficient string creation.
    printI(fullString);
    
2.3、bool(布尔型)

1、使用bool表示布尔型
2、布尔值只有个truefalse,它们都是编译时常量。
3、Dart的类型安全意味着您不能使用 if(nonbooleanValue) 或 assert(nonbooleanValue) 等代码, 相反Dart使用的是显式的检查值。

2.4、List(数组、列表)

1、List的创建:

  • 创建List:
    var lvs = [1, 2, 3];
    
  • 创建不可变的List:
    //其中的值不能修改
    var lcs = const [1, 2, 3];
    //报错:unsupported operation: Cannot modify for an unmodifiable list
    lcs[0] = 5;
    
  • 构造创建:
    var lns = new List();
    

2、常用操作:

  • [] 取值,length 数组长度

  • 方法:
    add() 添加元素,insert() 在指定位置插入,
    remove() 移除元素,clear() 清空数组,
    indexOf() 获取指定元素位置,lastIndexOf() 倒序获取指定元素位置,
    sort() 排序,sublist() 截取数组,shuffle()
    asMap() 转换为Map,forEach() 循环遍历,
    更多API详见官网:List<E> class

     //创建一个int类型的list
     List list = [10, 7, 23];
     // 输出[10, 7, 23]
     print(list);
     
     // 使用List的构造函数,也可以添加int参数,表示List固定长度,不能进行添加 删除操作
     var fruits = new List();
     
     // 添加元素
     fruits.add('apples');
     
     // 添加多个元素
     fruits.addAll(['oranges', 'bananas']);
     
     List subFruits = ['apples', 'oranges', 'banans'];
     // 添加多个元素
     fruits.addAll(subFruits);
     
     // 输出: [apples, oranges, bananas, apples, oranges, banans]
     print(fruits);
     
     // 获取List的长度
     print(fruits.length);
     
     // 获取第一个元素
     print(fruits.first);
     
     // 获取元素最后一个元素
     print(fruits.last);
     
     // 利用索引获取元素
     print(fruits[0]);
     
     // 查找某个元素的索引号
     print(fruits.indexOf('apples'));
     
     // 删除指定位置的元素,返回删除的元素
     print(fruits.removeAt(0));
    
     // 删除指定元素,成功返回true,失败返回false
     // 如果集合里面有多个“apples”, 只会删除集合中第一个改元素
     fruits.remove('apples');
    
     // 删除最后一个元素,返回删除的元素
     fruits.removeLast();
    
     // 删除指定范围(索引)元素,含头不含尾
     fruits.removeRange(start,end);
    
     // 删除指定条件的元素(这里是元素长度大于6)
     fruits.removeWhere((item) => item.length >6);
    
     // 删除所有的元素
     fruits.clear();
    
  • 说明:List 是泛型类型,所以可以指定里面所保存的数据类型

       // This list should contain only strings.
       var fruits = new List<String>();
          
       fruits.add('apples');
       var fruit = fruits[0];
       assert(fruit is String);
       
       // Generates static analysis warning, num is not a string.
       fruits.add(5);  // BAD: Throws exception in checked mode.
    
2.5、Set(无序集合)

1、Dart 中的 Set 是一个无序集合,里面不能保护重复的数据。 由于是无序的,所以无法通过索引来从 set 中获取数据。
2、相关方法:
add() 添加一个元素, addAll() 添加一个数组
contains() 是否包含一个元素,containsAll() 是否包含多个元素
intersection() 获取两个集合的交集
更多API详见官网:Set<E> class

   void main() {
     var sts = new Set();
     sts.add("1");
     sts.addAll(['gold', 'titanium', 'xenon']);
     sts.add("1");  //重复添加相同元素被覆盖
   
     //{1, gold, titanium, xenon}
     print(sts);
   
     //titanium
     print("" + sts.elementAt(2));
     
     //sts==>{1, gold, titanium, xenon}
     print("sts==>$sts");

     var sts2 = new Set.from(['xenon', 'argon']);
     //sts2==>{xenon, argon}
     print("sts2==>$sts2");

     var sts3 = sts.intersection(sts2);
     //sts3==>{xenon}
     print("sts3==>$sts3");
   }

注: List<E>Set<E>都继承自EfficientLengthIterable<T> ,并最终继承Iterable<E>类。

2.6、Map(集合)

1、说明:
· map 通常也被称之为 字典或者 hash ,也是一个无序的集合,里面 包含一个 key-value 对。map 把 key 和 value 关联起来可以 方便获取数据。和 JavaScript 不同的是, Dart objects 不是 maps。
· 键和值都可以是任何类型的对象。
· 每个 键 只出现一次, 而一个值则可以出现多次。
2、创建:

     //方式一:
     var map1 = {
       "one" : [1, "strValue", true],
       "two" : "twoValue",
     };
     //创建不可变Map
     var mapConst = const {
       "one" : [1, "strValue", true],
       "two" : "twoValue",
     };

     //方式二:未指定泛型类型
     var map2 = new Map();

     //方式二:指定泛型类型
     var map3 = new Map<int, String>();

3、常用属性/方法:
length Map长度, keys 返回所有key值,values 返回所有的value值
isEmpty() 是否为空Map, isNotEmpty() 是否为非空Map
[] 新增一个元素,remove() 移除一个元素,并将元素返回
containsKey() 是否包含一个key
putIfAbsent(key, function) 设置 key 的值,但是只有该 key 在 map 中不存在的时候才设置这个值,否则 key 的值保持不变。该函数需要 一个方法返回 value
forEach() 遍历Map

     var map1 = {
       "one" : [1, "strValue", true],
       "two" : "twoValue",
     };
     
     //null
     print(map1["x"]);
     //[1, strValue, true]
     print(map1["one"]);

     //true
     print(map1.containsKey("one"));

     var result = map1.remove("two");
     //result==>twoValue,,map1==>{one: [1, strValue, true]}
     print("result==>$result,,map1==>$map1");

     //提示:the argument type 'int' can't be assigned to be parameter type 'String'
     //map1[1] = 123;

     var map11 = {
         "one" : [1, "strValue", true],
         "two" : "twoValue",
         2 : new List()
       };
     //不会提示错误
     map11[1] = 123;

     //新增一个元素
     map1["three"] = "addValue";

     var keys = map1.keys;
     //keys==>(one, three)
     print("keys==>$keys");
     var values = map1.values;
     //values==>([1, strValue, true], addValue)
     print("values==>$values");

     map1.putIfAbsent("four", ()=> 1+2);
     map1.putIfAbsent("four", ()=> "返回一个value");
     //map1==>{one: [1, strValue, true], three: addValue, four: 3}
     print("map1==>$map1");
     print("==========================");

     /*
     one==>[1, strValue, true]
     three==>addValue
     four==>3
     */
     map1.forEach((key, value)=> {
       print("$key==>$value")
     });

     print("==========================");
     /*
     one==[1, strValue, true]
     three==addValue
     four==3
      */
     for(var me in map1.entries) {
       print("${me.key}==${me.value}");
     }

说明:
1、从上面的代码可以看出,在map中,如果初始的key值都是同一类型,则在通过[] 方式添加的时候,只能添加相同类型的值作为key。
2、虽然 Map 没有实现 Iterable,但是 Map 的 keys 和 values 属性实现了 Iterable。

  Iterable<K> get keys;
  Iterable<V> get values;

3、和Java一样,在Map中也有一个内部类MapEntry<K, V>,可以通过其属性entries获取到MapEntry<K, V> 类型的迭代器Iterable,以此来进行遍历。

2.7、dynamic(动态类型)

1、注意:
dynamic并非Dart中的内置类型,
没有指定类型的变量的类型为 dynamic
dynamic 一般在使用泛型时使用
2、示例:

dynamic a = 10;
//a==>10
print("a==>$a");
a = "六六六";
//a==>六六六
print("a==>$a");

var ls = new List<dynamic>();  //泛型,会在后面总结
ls.add("哈哈");
ls.add(333);
//ls==>[哈哈, 333]
print("ls==>$ls");
2.8、runes(符号文字)[不常用]
  • 简介:
    在 Dart 中,runes 代表字符串的 UTF-32 code points。

    Unicode 为每一个字符、标点符号、表情符号等都定义了 一个唯一的数值。 由于 Dart 字符串是 UTF-16 code units 字符序列, 所以在字符串中表达 32-bit Unicode 值就需要 新的语法了。

    可以实现一些表情的符号文字。

  • Unicode code point的表示方式:

    \uXXXX
    

    这里的 XXXX 是4个 16 进制的数。

  • 示例:

    main() {
      var clapping = '\u{1f44f}';
      print(clapping);
      print(clapping.codeUnits);
      print(clapping.runes.toList());
    
      Runes input = new Runes(
          '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
      print(new String.fromCharCodes(input));
    }
    
    //打印结果:===============================
    👏
    [55357, 56399]
    [128079]
    ♥  😅  😎  👻  🖖  👍
    
2.9、Symbols(标志)[不常用]
  • 简介:
    一个 Symbol object 代表 Dart 程序中声明的操作符或者标识符。

    此标识符常用于对于通过名字来引用标识符的情况,特别是混淆后的代码, 标识符的名字被混淆了,但是 Symbol 的名字不会改变。

    Symbol 字面量定义的是编译时常量。

  • 使用:
    使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号:

    #radix
    #bar
    

关于 symbols 的详情,请参考 dart:mirrors - reflection

三、操作符

1、算数运算符:
  • + 加、- 减、* 乘、/ 除、~/ 取整、% 取模(余)、-expr 负数
  • ++expr 先递增、expr++ 后递增、--expr 先递减、expr--后递减
2、关系运算符:
  • ==相等、!= 不等、>=大于等于、>大于、<=小于等于、< 小于
3、逻辑运算符:
  • ! 取反、&& 并且、|| 或者
4、赋值运算符:
  • =等于、+= 加等于、-= 减等于、
    *= 乘等于、/= 除等于、
    %= 取模(余)等于、~/= 取整等于
    <<= 左移等于、>>= 右移等于
    ^= 非等于、&=且等于、|=或等于
    ??= 判空等于:如果??=左边的变量为无值,则将右边的值赋值给变量,否则不赋值
    int a = 10;
    int b;
    a ??= 20;
    b ??=5;
    //a = 10      a有值10,所以不会赋值20
    print("a = $a");
    //b = 5        b无值,则将5赋值给b
    print("b = $b");
    
5、位和移位操作符

& 且、| 或、^ 非、<< 左移、>> 右移

5、条件表达式

condition ? expr1 : expr2 三目运算符
expr1 ?? expr2 返回其中不为空的表达式执行结果

String a = "Hello";
String b = "";
String c = "Dart";
String d = a ?? b;
String e = b ?? c;
//d = Hello
print("d = $d");
//e = Dart
print("e = $e");
6、类型判定操作符

asis、和 is! 操作符是在运行时判定对象
as:类型转换
is:如果对象是指定的类型返回 True
is!:如果对象是指定的类型返回 False
具体例子参考面向对象的总结

7、其他操作符
  • ():使用方法。代表调用一个方法
  • []:访问 List。访问 list 中特定位置的元素
    .:访问 Member(成员)。访问元素,例如 foo.bar 代表访问 foo 的 bar 成员
    ?.:条件成员访问。和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foonull 则返回 null,否则返回 bar 成员
8、级联操作符

..:级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。使用级联操作符可以避免创建临时变量,并且写出来的代码看起来更加流畅。(具体详见面向对象中的例子)

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

说明:
第一个方法 querySelector() 返回了一个 selector 对象。后面的级联操作符都是调用这个对象的成员,并忽略每个操作所返回的值。

四、控制语句

  • if...else,if...else if,if...else if...else
        int score = 95;
        if (score > 90) {
            print("优秀");
        } else if (score > 60) {
            print("及格");
        } else {
            print("完蛋玩意儿");
        }
        //优秀
    
  • for,for...in
    var list = [1, 2, 3, 4, 5];
    for (var item in list) {
       print(item);
    }
    
  • while do-while
        int count = 5;
        while(count > 0) {
            print("index => $count");
            count--;
        }
    
        int length = 5;
        do {
            print("index ==> $length");
            length--;
        } while(length > 0);
    
    
  • break continue
     List ls = [1, 2, 3, 4, 5, 6, 7, 8, 9];
     for (var i = 1; i<=ls.length; i++) {
         if (i%4==0) {
             break;
         }
         if (i%3==0) {
             continue;
         }
         print("index ==> ${ls[i]}");
     }
     
     //index ==> 2
     //index ==> 3
    
  • switch...case
    其中可以使用跳转标签(自定义标签名称)
    Stirng lang = “Dart";
    switch (lang) {
      D:
      case "Dart":
           print("This is Dart!");
           break;
      case "Java":
           print("This is Java!");
           break;
      case "Flutter":
           print("This is Flutter!");
           continue D;
           //break;  //不需要了
      default:
           print("This is NONE!");
           break;
    }
    
    打印结果为:
    This is Flutter!
    This is Dart!
    
  • assert (断言)
    1、如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。
    2、断言只在检查模式下运行有效,如果在生产模式 运行,则断言不会执行。
    3、assert 方法的参数可以为任何返回布尔值的表达式或者方法。
    如果返回的值为 true, 断言执行通过,执行结束。 如果返回值为 false, 断言执行失败,会抛出一个异常 AssertionError
       // Make sure the variable has a non-null value.
       assert(text != null);
       
       // Make sure the value is less than 100.
       assert(number < 100);
       
       // Make sure this is an https URL.
       assert(urlString.startsWith('https'));
    

四、注释

  • Dart 支持单行注释、多行注释和 文档注释。

  • 单行注释:
    单行注释以 // 开始。 // 后面的一行内容 为 Dart 代码注释。

       main() {
         // TODO: refactor into an AbstractLlamaGreetingFactory?
         print('Welcome to my Llama farm!');
       }
    
  • 多行注释:
    多行注释以 /* 开始, */ 结尾。 多行注释可以嵌套。

       main() {
         /*
          * This is a lot of work. Consider raising chickens.
       
         Llama larry = new Llama();
         larry.feed();
         larry.exercise();
         larry.clean();
          */
       }
    
  • 文档注释:
    文档注释可以使用 /// 开始, 也可以使用 /** 开始 并以 */ 结束。

    在文档注释内, Dart 编译器忽略除了中括号以外的内容。 使用中括号可以引用 classesmethodsfieldstop-level variablesfunctions、 和 parameters。中括号里面的名字使用 当前注释出现地方的语法范围查找对应的成员。

       /// A domesticated South American camelid (Lama glama).
       ///
       /// Andean cultures have used llamas as meat and pack
       /// animals since pre-Hispanic times.
       class Llama {
         String name;
       
         /// Feeds your llama [Food].
         ///
         /// The typical llama eats one bale of hay per week.
         void feed(Food food) {
           // ...
         }
       
         /// Exercises your llama with an [activity] for
         /// [timeLimit] minutes.
         void exercise(Activity activity, int timeLimit) {
           // ...
         }
       }
    

    使用 SDK 中的 文档生成工具可以解析文档并生成 HTML 网页。 关于生成的文档示例,请参考 Dart API 文档。 关于如何 组织文档的建议,请参考 Dart 文档注释指南。

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

推荐阅读更多精彩内容