Flutter_一小時入門Dart

本文適合有代碼基礎的人,如果沒在代碼基礎請文章底部來源從頭開始學習

示例

// 定義一個函數
printInteger(int aNumber) {
  print('The number is $aNumber.'); // 打印到控制臺。
}

// 應用從這里開始執行。
// 程序開始執行函數,該函數是特定的、必須的、頂級函數。
main() {
  //定義變量,通過這種方式定義變量不需要指定變量類型。
  var number = 42;
  printInteger(number); // 調用函數。
}

重要的概念

在學習 Dart 語言時, 應該基于以下事實和概念:

  • 任何保存在變量中的都是一個 對象 , 并且所有的對象都是對應一個 類 的實例。 無論是數字,函數和 null 都是對象。所有對象繼承自 Object 類。

  • 盡管 Dart 是強類型的,但是 Dart 可以推斷類型,所以類型注釋是可選的。 在上面的代碼中, number 被推斷為 int 類型。 如果要明確說明不需要任何類型, 需要使用特殊類型 dynamic 。

  • Dart 支持泛型,如 List <int> (整數列表)或 List <dynamic> (任何類型的對象列表)。

  • Dart 支持頂級函數(例如 main() ), 同樣函數綁定在類或對象上(分別是 靜態函數 和 實例函數 )。 以及支持函數內創建函數 ( 嵌套 或 局部函數 ) 。

  • 類似地, Dart 支持頂級 變量 , 同樣變量綁定在類或對象上(靜態變量和實例變量)。 實例變量有時稱為字段或屬性。

  • 與 Java 不同,Dart 沒有關鍵字 “public” , “protected” 和 “private” 。 如果標識符以下劃線(_)開頭,則它相對于庫是私有的。 有關更多信息,參考 庫和可見性。

  • 標識符 以字母或下劃線(_)開頭,后跟任意字母和數字組合。

  • Dart 語法中包含 表達式( expressions )(有運行時值)和 語句( statements )(沒有運行時值)。 例如,條件表達式 condition ? expr1 : expr2 的值可能是 expr1 或 expr2 。 將其與 if-else 語句 相比較,if-else 語句沒有值。 一條語句通常包含一個或多個表達式,相反表達式不能直接包含語句。

  • Dart 工具提示兩種類型問題:警告錯誤。 警告只是表明代碼可能無法正常工作,但不會阻止程序的執行。 錯誤可能是編譯時錯誤或者運行時錯誤。 編譯時錯誤會阻止代碼的執行; 運行時錯誤會導致代碼在執行過程中引發 [異常](#exception)。

Dart關鍵字

key 作用域 備注
abstract 2 抽象方法/抽象類
dynamic 2
implements 2
show 1
as 2
else
import 2
static 2
assert
enum
in
super
async 1
export 2
interface 2
switch
await 3
extends
is
sync 1
break
external 2
library 2
this
case
factory 2
mixin 2
throw
catch
false
new
true
class
final
null
try
const
finally
on 1
typedef 2
continue
for
operator 2
var
covariant 2
Function 2
part 2
void
default
get 2
rethrow
while
deferred 2
hide 1
return
with
do
if
set 2
yield 3

避免使用這些單詞作為標識符。 但是,如有必要,標有上標的關鍵字可以用作標識符:

  • 帶有 1 上標的單詞為 上下文關鍵字, 僅在特定位置具有含義。 他們在任何地方都是有效的標識符。

  • 帶有 2 上標的單詞為 內置標識符, 為了簡化將 JavaScript 代碼移植到 Dart 的工作, 這些關鍵字在大多數地方都是有效的標識符, 但它們不能用作類或類型名稱,也不能用作 import 前綴。

  • 帶有 3 上標的單詞是與 Dart 1.0 發布后添加的異步支持相關的更新,作為限制類保留字。
    不能在標記為 async ,async* 或 sync* 的任何函數體中使用 await 或 yield 作為標識符。

關鍵字表中的剩余單詞都是保留字。 不能將保留字用作標識符。

abstract

  • 抽象方法
    實例方法, getter, 和 setter 方法可以是抽象的, 只定義接口不進行實現,而是留給其他類去實現。 抽象方法只存在于 抽象類 中。

定義一個抽象函數,使用分號 (;) 來代替函數體:

abstract class Doer {
  // 定義實例變量和方法 ...

  void doSomething(); // 定義一個抽象方法。
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供方法實現,所以這里的方法就不是抽象方法了...
  }
}

調用抽象方法會導致運行時錯誤。

  • 抽象類
    使用 abstract 修飾符來定義 抽象類 — 抽象類不能實例化。 抽象類通常用來定義接口,以及部分實現。 如果希望抽象類能夠被實例化,那么可以通過定義一個 工廠構造函數 來實現。

抽象類通常具有 抽象方法。 下面是一個聲明具有抽象方法的抽象類示例:

// 這個類被定義為抽象類,
// 所以不能被實例化。
abstract class AbstractContainer {
  // 定義構造行數,字段,方法...

  void updateChildren(); // 抽象方法。
}

dynamic

使用 dynamic 注解替換推斷失敗的情況。
Dart 允許在許多地方省略類型注解,并嘗試推斷類型。在某些情況下,如果推斷失敗了,會默認指定為 dynamic 類型。如果 dynamic 類型與期望相同,那么從技術的角度來講,這是獲取類型最簡潔 的方式。

但是,這種方式是最不清晰的。任何一個閱讀代碼的人,當看到一個類型確實的成員時,是沒有辦法 知道,編寫的人是希望它是 dynamic 類型,還是期望它是其他的什么類型,或者閱讀的人就簡單的 認為是編寫的人忘記了指定類型。

當 dynamic 是你期望的類型,就應該指明它,這樣能讓你的意圖更清晰。

dynamic mergeJson(dynamic original, dynamic changes) => ...

在Dart 2之前,本規則恰恰是相反的:不要 為隱性類型的成員指定 dynamic 注解?;趶婎愋拖到y 和類型推斷,現在的開發者更希望 Dart 的行為類似于推斷的靜態類型語言?;谶@種心理模型,我們發現 代碼區域慢慢地失去了靜態類型所具有的安全及性能。

implements

每個類都隱式的定義了一個接口,接口包含了該類所有的實例成員及其實現的接口。 如果要創建一個 A 類,A 要支持 B 類的 API ,但是不需要繼承 B 的實現, 那么可以通過 A 實現 B 的接口。

一個類可以通過 implements 關鍵字來實現一個或者多個接口, 并實現每個接口要求的 API。 例如:

// person 類。 隱式接口里面包含了 greet() 方法聲明。
class Person {
  // 包含在接口里,但只在當前庫中可見。
  final _name;

  // 不包含在接口里,因為這是一個構造函數。
  Person(this._name);

  // 包含在接口里。
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// person 接口的實現。
class Impostor implements Person {
  get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}
下面示例演示一個類如何實現多個接口: Here’s an example of specifying that a class implements multiple interfaces:

class Point implements Comparable, Location {...}

show hide

導入庫的一部分
如果你只使用庫的一部分功能,則可以選擇需要導入的 內容。例如:

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

as

  • 類型判定運算符
    as, is, 和 is! 運算符用于在運行時處理類型檢查:
|Operator|  Meaning|
| --- | ---| 
|as|    Typecast (也被用于指定庫前綴)|
|is |True if the object has the specified type|
|is!|   False if the object has the specified type|

例如, obj is Object 總是 true。 但是只有 obj 實現了 T 的接口時, obj is T 才是 true。

使用 as 運算符將對象強制轉換為特定類型。 通常,可以認為是 is 類型判定后,被判定對象調用函數的一種縮寫形式。 請考慮以下代碼:

```
if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}
```
使用 as 運算符進行縮寫:

```
(emp as Person).firstName = 'Bob';
```
######提示:以上代碼并不是等價的。 如果 emp 為 null 或者不是 Person 對象, 那么第一個 is 的示例,后面將不回執行; 第二個 as 的示例會拋出異常。
  • 指定庫前綴
    如果導入兩個存在沖突標識符的庫, 則可以為這兩個庫,或者其中一個指定前綴。 例如,如果 library1 和 library2 都有一個 Element 類, 那么可以通過下面的方式處理:

    import 'package:lib1/lib1.dart';
    import 'package:lib2/lib2.dart' as lib2;
    
    // 使用 lib1 中的 Element。
    Element element1 = Element();
    
    // 使用 lib2 中的 Element。
    lib2.Element element2 = lib2.Element();
    

if else

Dart 支持 if - else 語句,其中 else 是可選的, 比如下面的例子, 另參考 conditional expressions.

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

和 JavaScript 不同, Dart 的判斷條件必須是布爾值,不能是其他類型。

import

使用庫
通過 import 指定一個庫命名空間中的內如如何在另一個庫中使用。 例如,Dart Web應用程序通常使用 dart:html 庫,它們可以像這樣導入:

import 'dart:html';

import 參數只需要一個指向庫的 URI。 對于內置庫,URI 擁有自己特殊的dart: 方案。 對于其他的庫,使用系統文件路徑或者 package: 方案 。 package: 方案指定由包管理器(如 pub 工具)提供的庫。例如:

import 'package:test/test.dart';
提示: URI 代表統一資源標識符。 URL(統一資源定位符)是一種常見的URI。

static

類變量和方法
使用 static 關鍵字實現類范圍的變量和方法。

  • 靜態變量
    靜態變量(類變量)對于類級別的狀態是非常有用的:

    class Queue {
      static const initialCapacity = 16;
      // ···
    }
    
    void main() {
      assert(Queue.initialCapacity == 16);
    }
    

靜態變量只到它們被使用的時候才會初始化。

提示: 代碼準守風格推薦指南 中的命名規則, 使用 lowerCamelCase 來命名常量。
  • 靜態方法
    靜態方法(類方法)不能在實例上使用,因此它們不能訪問 this 。 例如:

    import 'dart:math';
    
    class Point {
      num x, y;
      Point(this.x, this.y);
    
      static num distanceBetween(Point a, Point b) {
        var dx = a.x - b.x;
        var dy = a.y - b.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
    
    void main() {
      var a = Point(2, 2);
      var b = Point(4, 4);
      var distance = Point.distanceBetween(a, b);
      assert(2.8 < distance && distance < 2.9);
      print(distance);
    }
    
提示: 對于常見或廣泛使用的工具和函數, 應該考慮使用頂級函數而不是靜態方法。

靜態函數可以當做編譯時常量使用。 例如,可以將靜態方法作為參數傳遞給常量構造函數。

asset

如果 assert 語句中的布爾條件為 false , 那么正常的程序執行流程會被中斷。 在本章中包含部分 assert 的使用, 下面是一些示例:

// 確認變量值不為空。
assert(text != null);

// 確認變量值小于100。
assert(number < 100);

// 確認 URL 是否是 https 類型。
assert(urlString.startsWith('https'));
提示: assert 語句只在開發環境中有效, 在生產環境是無效的; Flutter 中的 assert 只在 debug 模式 中有效。 開發用的工具,例如 dartdevc 默認是開啟 assert 功能。 其他的一些工具, 例如 dart 和 dart2js, 支持通過命令行開啟 assert : --enable-asserts。

assert 的第二個參數可以為其添加一個字符串消息。

assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');

assert 的第一個參數可以是解析為布爾值的任何表達式。 如果表達式結果為 true , 則斷言成功,并繼續執行。 如果表達式結果為 false , 則斷言失敗,并拋出異常 (AssertionError) 。

enum

枚舉類型
枚舉類型也稱為 enumerations 或 enums , 是一種特殊的類,用于表示數量固定的常量值。

使用枚舉
使用 enum 關鍵字定義一個枚舉類型:

enum Color { red, green, blue }

枚舉中的每個值都有一個 index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)。 例如,第一個枚舉值的索引是 0 , 第二個枚舉值的索引是 1。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

使用枚舉的 values 常量, 獲取所有枚舉值列表( list )。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

可以在 switch 語句 中使用枚舉, 如果不處理所有枚舉值,會收到警告:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 沒有這個,會看到一個警告。
    print(aColor); // 'Color.blue'
}

枚舉類型具有以下限制:

  • 枚舉不能被子類化,混合或實現。
  • 枚舉不能被顯式實例化。
    有關更多信息,參考 Dart language specification 。

for in

for 循環
進行迭代操作,可以使用標準 for 語句。 例如:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('!');
}

閉包在 Dart 的 for 循環中會捕獲循環的 index 索引值, 來避免 JavaScript 中常見的陷阱。 請思考示例代碼:

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());

和期望一樣,輸出的是 0 和 1。 但是示例中的代碼在 JavaScript 中會連續輸出兩個 2 。

I如果要迭代一個實現了 Iterable 接口的對象, 可以使用 forEach() 方法, 如果不需要使用當前計數值, 使用 forEach() 是非常棒的選擇;

candidates.forEach((candidate) => candidate.interview());

實現了 Iterable 的類(比如, List 和 Set)同樣也支持使用 for-in 進行迭代操作 iteration :

var collection = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}

extends super

擴展類(繼承)
使用 extends 關鍵字來創建子類, 使用 super 關鍵字來引用父類:

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

async await

  • 異步支持
    Dart 庫中包含許多返回 Future 或 Stream 對象的函數. 這些函數在設置完耗時任務(例如 I/O 曹組)后, 就立即返回了,不會等待耗任務完成。 使用 async 和 await 關鍵字實現異步編程。 可以讓你像編寫同步代碼一樣實現異步操作。
- 處理 Future
可以通過下面兩種方式,獲得 Future 執行完成的結果:

使用 async 和 await.
使用 Future API,具體描述,參考 庫概覽.
使用 async 和 await 關鍵字的代碼是異步的。 雖然看起來有點想同步代碼。 例如,下面的代碼使用 await 等待異步函數的執行結果。

```
await lookUpVersion();
```
要使用 await , 代碼必須在 異步函數(使用 async 標記的函數)中:

```
Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}
```
提示: 雖然異步函數可能會執行耗時的操作, 但它不會等待這些操作。 相反,異步函數只有在遇到第一個 await 表達式(詳情見)時才會執行。 也就是說,它返回一個 Future 對象, 僅在await表達式完成后才恢復執行。

使用 try, catch, 和 finally 來處理代碼中使用 await 導致的錯誤。

```
try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}
```
在一個異步函數中可以多次使用 await 。 例如,下面代碼中等待了三次函數結果:

```
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
```

在 await 表達式 中, 表達式 的值通常是一個 Future 對象; 如果不是,這是表達式的值會被自動包裝成一個 Future 對象。 Future 對象指明返回一個對象的承諾(promise)。 await 表達式 執行的結果為這個返回的對象。 await 表達式會阻塞代碼的執行,直到需要的對象返回為止。

如果在使用 await 導致編譯時錯誤, 確認 await 是否在一個異步函數中。 例如,在應用的 main() 函數中使用 await , main() 函數的函數體必須被標記為 async :

Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}
  • 聲明異步函數
    函數體被 async 標示符標記的函數,即是一個異步函數。 將 async 關鍵字添加到函數使其返回Future。 例如,考慮下面的同步函數,它返回一個 String :

     String lookUpVersion() => '1.0.0';
    

    例如,將來的實現將非常耗時,將其更改為異步函數,返回值是 Future 。

    Future<String> lookUpVersion() async => '1.0.0';
    
    注意,函數體不需要使用Future API。 如有必要, Dart 會創建 Future 對象。

    如果函數沒有返回有效值, 需要設置其返回類型為 Future<void> 。

  • 處理 Stream
    當需要從 Stream 中獲取數據值時, 可以通過一下兩種方式:

    使用 async 和 一個 異步循環 (await for)。
    使用 Stream API, 更多詳情,參考 in the library tour。
    提示: 在使用 await for 前,確保代碼清晰, 并且確實希望等待所有流的結果。 例如,通常不應該使用 await for 的UI事件偵聽器, 因為UI框架會發送無窮無盡的事件流。

    一下是異步for循環的使用形式:

    await for (varOrType identifier in expression) {
      // Executes each time the stream emits a value.
    }
    

    上面 表達式 返回的值必須是 Stream 類型。 執行流程如下:

    1. 等待,直到流發出一個值。
    2. 執行 for 循環體,將變量設置為該發出的值
    3. 重復1和2,直到關閉流。

    使用 break 或者 return 語句可以停止接收 stream 的數據, 這樣就跳出了 for 循環, 并且從 stream 上取消注冊。 如果在實現異步 for 循環時遇到編譯時錯誤, 請檢查確保 await for 處于異步函數中。 例如,要在應用程序的 main() 函數中使用異步 fo r循環, main() 函數體必須標記為 async` :

    Future main() async {
      // ...
      await for (var request in requestServer) {
        handleRequest(request);
      }
      // ...
    }
    

有關異步編程的更多信息,請參考 dart:async 部分。 同時也可參考文章 Dart Language Asynchrony Support: Phase 1 和 Dart Language Asynchrony Support: Phase 2, 以及 Dart language specification 。

switch case

在 Dart 中 switch 語句使用 == 比較整數,字符串,或者編譯時常量。 比較的對象必須都是同一個類的實例(并且不可以是子類), 類必須沒有對 == 重寫。 枚舉類型 可以用于 switch 語句。

提示: 在 Dart 中 Switch 語句僅適用于有限的情況下, 例如在 interpreter 或 scanner 中。

在 case 語句中,每個非空的 case 語句結尾需要跟一個 break 語句。 除 break 以外,還有可以使用 continue, throw,者 return。

當沒有 case 語句匹配時,執行 default 代碼:

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

下面的 case 程序示例中缺省了 break 語句,導致錯誤:

var command = 'OPEN';
switch (command) {
  case 'OPEN':
    executeOpen();
    // ERROR: 丟失 break

  case 'CLOSED':
    executeClosed();
    break;
}

但是, Dart 支持空 case 語句, 允許程序以 fall-through 的形式執行。

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // Empty case falls through.
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

在非空 case 中實現 fall-through 形式, 可以使用 continue 語句結合 lable 的方式實現:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
  // Continues executing at the nowClosed label.

  nowClosed:
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

case 語句可以擁有局部變量, 這些局部變量只能在這個語句的作用域中可見。

sync

生成器
當您需要延遲生成( lazily produce )一系列值時, 可以考慮使用生成器函數。 Dart 內置支持兩種生成器函數:

  • Synchronous 生成器: 返回一個 Iterable 對象。
  • Asynchronous 生成器: 返回一個 Stream 對象。

通過在函數體標記 sync*, 可以實現一個同步生成器函數。 使用 yield 語句來傳遞值:

Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
通過在函數體標記 async*, 可以實現一個異步生成器函數。 使用 yield 語句來傳遞值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

如果生成器是遞歸的,可以使用 yield* 來提高其性能:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

有關生成器的更多信息,請參考文章 Dart Language Asynchrony Support: Phase 2 。

break continue

使用 break 停止程序循環:

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

使用 continue 跳轉到下一次迭代:

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}

如果對象實現了 Iterable 接口 (例如,list 或者 set)。 那么上面示例完全可以用另一種方式來實現:

candidates
    .where((c) => c.yearsExperience >= 5)
    .forEach((c) => c.interview());

Mixin

為類添加功能: Mixin

Mixin 是復用類代碼的一種途徑, 復用的類可以在不同層級,之間可以不存在繼承關系。

通過 with 后面跟一個或多個混入的名稱,來 使用 Mixin , 下面的示例演示了兩個使用 Mixin 的類:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

通過創建一個繼承自 Object 且沒有構造函數的類,來 實現 一個 Mixin 。 如果 Mixin 不希望作為常規類被使用,使用關鍵字 mixin 替換 class 。 例如:

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

指定只有某些類型可以使用的 Mixin - 比如, Mixin 可以調用 Mixin 自身沒有定義的方法 - 使用 on 來指定可以使用 Mixin 的父類類型:

mixin MusicalPerformer on Musician {
  // ···
}

版本提示: mixin 關鍵字在 Dart 2.1 中被引用支持。 早期版本中的代碼通常使用 abstract class 代替。 更多有關 Mixin 在 2.1 中的變更信息,請參見 Dart SDK changelog 和 2.1 mixin specification 。

提示: 對 Mixin 的一些限制正在被移除。 關于更多詳情,參考 proposed mixin specification.

有關 Dart 中 Mixin 的理論演變,參考 A Brief History of Mixins in Dart.

Final Const

Final 和 Const
使用過程中從來不會被修改的變量, 可以使用 final 或 const, 而不是 var 或者其他類型, Final 變量的值只能被設置一次; Const 變量在編譯時就已經固定 (Const 變量 是隱式 Final 的類型.) 最高級 final 變量或類變量在第一次使用時被初始化。

提示: 實例變量可以是 final 類型但不能是 const 類型。 必須在構造函數體執行之前初始化 final 實例變量 —— 在變量聲明中,參數構造函數中或構造函數的初始化列表中進行初始化。

創建和設置一個 Final 變量:

final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

final 不能被修改:

name = 'Alice'; // Error: 一個 final 變量只能被設置一次。

如果需要在編譯時就固定變量的值,可以使用 const 類型變量。 如果 Const 變量是類級別的,需要標記為 static const。 在這些地方可以使用在編譯時就已經固定不變的值,字面量的數字和字符串, 固定的變量,或者是用于計算的固定數字:

const bar = 1000000; // 壓力單位 (dynes/cm2)
const double atm = 1.01325 * bar; // 標準氣壓

Const 關鍵字不僅可以用于聲明常量變量。 還可以用來創建常量值,以及聲明創建常量值的構造函數。 任何變量都可以擁有常量值。

var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

聲明 const 的初始化表達式中 const 可以被省略。 比如上面的 baz。 有關更多信息,參考 DON’T use const redundantly。

非 Final , 非 const 的變量是可以被修改的,即使這些變量 曾經引用過 const 值。

foo = [1, 2, 3]; // 曾經引用過 const [] 常量值。

Const 變量的值不可以修改:

baz = [42]; // Error: 常量變量不能賦值修改。

更多關于使用 const 創建常量值,參考 Lists, Maps, 和 Classes。

throw catch finnaly on rethrow

異常
Dart 代碼可以拋出和捕獲異常。 異常表示一些未知的錯誤情況。 如果異常沒有被捕獲, 則異常會拋出, 導致拋出異常的代碼終止執行。

和 Java 有所不同, Dart 中的所有異常是非檢查異常。 方法不會聲明它們拋出的異常, 也不要求捕獲任何異常。

Dart 提供了 Exception 和 Error 類型, 以及一些子類型。 當然也可以定義自己的異常類型。 但是,此外 Dart 程序可以拋出任何非 null 對象, 不僅限 Exception 和 Error 對象。

  • throw
    下面是關于拋出或者 引發 異常的示例:

    throw FormatException('Expected at least 1 section');
    也可以拋出任意的對象:

    throw 'Out of llamas!';
    提示: 高質量的生產環境代碼通常會實現 Error 或 Exception 類型的異常拋出。

    因為拋出異常是一個表達式, 所以可以在 => 語句中使用,也可以在其他使用表達式的地方拋出異常:

    void distanceTo(Point other) => throw UnimplementedError();
    catch
    捕獲異??梢员苊猱惓@^續傳遞(除非重新拋出( rethrow )異常)。 可以通過捕獲異常的機會來處理該異常:

    try {
      breedMoreLlamas();
    } on OutOfLlamasException {
      buyMoreLlamas();
    }
    

    通過指定多個 catch 語句,可以處理可能拋出多種類型異常的代碼。 與拋出異常類型匹配的第一個 catch 語句處理異常。 如果 catch 語句未指定類型, 則該語句可以處理任何類型的拋出對象:

    try {
      breedMoreLlamas();
    } on OutOfLlamasException {
      // 一個特殊的異常
      buyMoreLlamas();
    } on Exception catch (e) {
      // 其他任何異常
      print('Unknown exception: $e');
    } catch (e) {
      // 沒有指定的類型,處理所有異常
      print('Something really unknown: $e');
    }
    
  • on catch

    如上述代碼所示,捕獲語句中可以同時使用 on 和 catch ,也可以單獨分開使用。 使用 on 來指定異常類型, 使用 catch 來 捕獲異常對象。

    catch() 函數可以指定1到2個參數, 第一個參數為拋出的異常對象, 第二個為堆棧信息 ( 一個 StackTrace 對象 )。

    try {
      // ···
    } on Exception catch (e) {
      print('Exception details:\n $e');
    } catch (e, s) {
      print('Exception details:\n $e');
      print('Stack trace:\n $s');
    }
    
  • rethrow
    如果僅需要部分處理異常, 那么可以使用關鍵字 rethrow 將異常重新拋出。

    void misbehave() {
      try {
        dynamic foo = true;
        print(foo++); // Runtime error
      } catch (e) {
        print('misbehave() partially handled ${e.runtimeType}.');
        rethrow; // Allow callers to see the exception.
      }
    }
    
    void main() {
      try {
        misbehave();
      } catch (e) {
        print('main() finished handling ${e.runtimeType}.');
      }
    }
    
    
  • finally
    不管是否拋出異常, finally 中的代碼都會被執行。 如果 catch 沒有匹配到異常, 異常會在 finally 執行完成后,再次被拋出:

    try {
      breedMoreLlamas();
    } finally {
      // Always clean up, even if an exception is thrown.
      cleanLlamaStalls();
    }
    

    任何匹配的 catch 執行完成后,再執行 finally :

    try {
      breedMoreLlamas();
    } catch (e) {
      print('Error: $e'); // Handle the exception first.
    } finally {
      cleanLlamaStalls(); // Then clean up.
    }
    

    更多詳情,請參考 Exceptions 章節。

Typedefs

在 Dart 中,函數也是對象,就想字符和數字對象一樣。 使用 typedef ,或者 function-type alias 為函數起一個別名, 別名可以用來聲明字段及返回值類型。 當函數類型分配給變量時,typedef會保留類型信息。

請考慮以下代碼,代碼中未使用 typedef :

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

// Initial, broken implementation. // broken ?
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // 雖然知道 compare 是函數,
  // 但是函數是什么類型 ?
  assert(coll.compare is Function);
}

當把 f 賦值給 compare 的時候,類型信息丟失了。 f 的類型是 (Object, Object) → int (這里 → 代表返回值類型), 但是 compare 得到的類型是 Function 。如果我們使用顯式的名字并保留類型信息, 這樣開發者和工具都可以使用這些信息:

typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

// Initial, broken implementation.
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}

提示: 目前,typedefs 只能使用在函數類型上, 我們希望將來這種情況有所改變。

由于 typedefs 只是別名, 他們還提供了一種方式來判斷任意函數的類型。例如:

typedef Compare<T> = int Function(T a, T b);

int sort(int a, int b) => a - b;

void main() {
  assert(sort is Compare<int>); // True!
}

Function

Dart 是一門真正面向對象的語言, 甚至其中的函數也是對象,并且有它的類型 Function 。 這也意味著函數可以被賦值給變量或者作為參數傳遞給其他函數。 也可以把 Dart 類的實例當做方法來調用。 有關更多信息,參考 Callable classes.

Deferred

延遲加載庫
Deferred loading (也稱之為 lazy loading) 可以讓應用在需要的時候再加載庫。 下面是一些使用延遲加載庫的場景:

減少 APP 的啟動時間。
執行 A/B 測試,例如 嘗試各種算法的 不同實現。
加載很少使用的功能,例如可選的屏幕和對話框。
要延遲加載一個庫,需要先使用 deferred as 來導入:

import 'package:greetings/hello.dart' deferred as hello;

當需要使用的時候,使用庫標識符調用 loadLibrary() 函數來加載庫:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

在前面的代碼,使用 await 關鍵字暫停代碼執行一直到庫加載完成。 關于 async 和 await 的更多信息請參考 異步支持。

在一個庫上你可以多次調用 loadLibrary() 函數。但是該庫只是載入一次。

使用延遲加載庫的時候,請注意一下問題:

延遲加載庫的常量在導入的時候是不可用的。 只有當庫加載完畢的時候,庫中常量才可以使用。
在導入文件的時候無法使用延遲庫中的類型。 如果你需要使用類型,則考慮把接口類型移動到另外一個庫中, 讓兩個庫都分別導入這個接口庫。
Dart 隱含的把 loadLibrary() 函數導入到使用 deferred as 的命名空間 中。 loadLibrary() 方法返回一個 Future。

變量類型

Dart 語言支持以下內建類型:

  1. Number
  2. String
  3. Boolean
  4. List (也被稱為 Array)
  5. Map
  6. Set
  7. Rune (用于在字符串中表示 Unicode 字符)
  8. Symbol
    這些類型都可以被初始化為字面量。 例如, 'this is a string' 是一個字符串的字面量, true 是一個布爾的字面量。

因為在 Dart 所有的變量終究是一個對象(一個類的實例), 所以變量可以使用 構造函數 進行初始化。 一些內建類型擁有自己的構造函數。 例如, 通過 Map() 來構造一個 map 變量。

  • Number

    Dart 語言的 Number 有兩種類型:

    • int

      整數值不大于64位, 具體取決于平臺。 在 Dart VM 上, 值的范圍從 -263 到 263 - 1. Dart 被編譯為 JavaScript 時,使用 JavaScript numbers, 值的范圍從 -253 到 253 - 1.

    • double

      64位(雙精度)浮點數,依據 IEEE 754 標準。

      int 和 double 都是 num. 的亞類型。 num 類型包括基本運算 +, -, /, 和 *, 以及 abs(), ceil(), 和 floor(), 等函數方法。 (按位運算符,例如?,定義在 int 類中。) 如果 num 及其亞類型找不到你想要的方法, 嘗試查找使用 dart:math 庫。

      整數類型不包含小數點。 下面是定義整數類型字面量的例子:

      var x = 1;
      var hex = 0xDEADBEEF;
      

      如果一個數字包含小數點,那么就是小數類型。 下面是定義小數類型字面量的例子:

      var y = 1.1;
      var exponents = 1.42e5;
      

      從 Dart 2.1 開始,必要的時候 int 字面量會自動轉換成 double 類型。

      double z = 1; // 相當于 double z = 1.0.
      
      版本提示: 在 2.1 之前,在 double 上下文中使用 int 字面量是錯誤的。

      以下是將字符串轉換為數字的方法,反之亦然:

      // String -> int
      var one = int.parse('1');
      assert(one == 1);
      
      // String -> double
      var onePointOne = double.parse('1.1');
      assert(onePointOne == 1.1);
      
      // int -> String
      String oneAsString = 1.toString();
      assert(oneAsString == '1');
      
      // double -> String
      String piAsString = 3.14159.toStringAsFixed(2);
      assert(piAsString == '3.14');
      

      int 特有的傳統按位運算操作,移位(<<, >>),按位與(&)以及 按位或(|)。 例如:

      assert((3 << 1) == 6); // 0011 << 1 == 0110
      assert((3 >> 1) == 1); // 0011 >> 1 == 0001
      assert((3 | 4) == 7); // 0011 | 0100 == 0111
      

      數字類型字面量是編譯時常量。 在算術表達式中,只要參與計算的因子是編譯時常量, 那么算術表達式的結果也是編譯時常量。

      const msPerSecond = 1000;
      const secondsUntilRetry = 5;
      const msUntilRetry = secondsUntilRetry * msPerSecond;
      
    
    
  • String

    Dart 字符串是一組 UTF-16 單元序列。 字符串通過單引號或者雙引號創建。

    var s1 = 'Single quotes work well for string literals.';
    var s2 = "Double quotes work just as well.";
    var s3 = 'It\'s easy to escape the string delimiter.';
    var s4 = "It's even easier to use the other delimiter.";
    

    字符串可以通過 ${expression} 的方式內嵌表達式。 如果表達式是一個標識符,則 {} 可以省略。 在 Dart 中通過調用就對象的 toString() 方法來得到對象相應的字符串。

    var s = 'string interpolation';
    
    assert('Dart has $s, which is very handy.' ==
        'Dart has string interpolation, ' +
            'which is very handy.');
    assert('That deserves all caps. ' +
            '${s.toUpperCase()} is very handy!' ==
        'That deserves all caps. ' +
            'STRING INTERPOLATION is very handy!'); 
    
    提示: == 運算符用來測試兩個對象是否相等。 在字符串中,如果兩個字符串包含了相同的編碼序列,那么這兩個字符串相等。 units.

    可以使用 + 運算符來把多個字符串連接為一個,也可以把多個字面量字符串寫在一起來實現字符串連接:

    var s1 = 'String '
        'concatenation'
        " works even over line breaks.";
    assert(s1 ==
        'String concatenation works even over '
        'line breaks.');
    
    var s2 = 'The + operator ' + 'works, as well.';
    assert(s2 == 'The + operator works, as well.');
    

    使用連續三個單引號或者三個雙引號實現多行字符串對象的創建:

    var s1 = '''
    You can create
    multi-line strings like this one.
    ''';
    
    var s2 = """This is also a
    multi-line string.""";
    

    使用 r 前綴,可以創建 “原始 raw” 字符串:

    var s = r"In a raw string, even \n isn't special.";
    

    參考 Runes 來了解如何在字符串中表達 Unicode 字符。

    一個編譯時常量的字面量字符串中,如果存在插值表達式,表達式內容也是編譯時常量, 那么該字符串依舊是編譯時常量。 插入的常量值類型可以是 null,數值,字符串或布爾值。

    // const 類型數據
    const aConstNum = 0;
    const aConstBool = true;
    const aConstString = 'a constant string';
    
    // 非 const 類型數據
    var aNum = 0;
    var aBool = true;
    var aString = 'a string';
    const aConstList = [1, 2, 3];
    
    const validConstString = '$aConstNum $aConstBool $aConstString'; //const 類型數據
    // const invalidConstString = '$aNum $aBool $aString $aConstList'; //非 const 類型數據
    

    更多關于 string 的使用, 參考 字符串和正則表達式.

    • Boolean
      Dart 使用 bool 類型表示布爾值。 Dart 只有字面量 true and false 是布爾類型, 這兩個對象都是編譯時常量。

      Dart 的類型安全意味著不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。 而是應該像下面這樣,明確的進行值檢查:

      // 檢查空字符串。
      var fullName = '';
      assert(fullName.isEmpty);
      
      // 檢查 0 值。
      var hitPoints = 0;
      assert(hitPoints <= 0);
      
      // 檢查 null 值。
      var unicorn;
      assert(unicorn == null);
      
      // 檢查 NaN 。
      var iMeantToDoThis = 0 / 0;
      assert(iMeantToDoThis.isNaN);
      
  • List
    幾乎每種編程語言中最常見的集合可能是 array 或有序的對象集合。 在 Dart 中的 Array 就是 List 對象, 通常稱之為 List 。

    Dart 中的 List 字面量非常像 JavaScript 中的 array 字面量。 下面是一個 Dart List 的示例:

    var list = [1, 2, 3];
    
    提示: Dart 推斷 list 的類型為 List<int> 。 如果嘗試將非整數對象添加到此 List 中, 則分析器或運行時會引發錯誤。 有關更多信息,請閱讀 類型推斷。

    Lists 的下標索引從 0 開始,第一個元素的索引是 0。 list.length - 1 是最后一個元素的索引。 訪問 List 的長度和元素與 JavaScript 中的用法一樣:

    var list = [1, 2, 3];
    assert(list.length == 3);
    assert(list[1] == 2);
    
    list[1] = 1;
    assert(list[1] == 1);
    

    在 List 字面量之前添加 const 關鍵字,可以定義 List 類型的編譯時常量:

    var constantList = const [1, 2, 3];
    // constantList[1] = 1; // 取消注釋會引起錯誤。
    

    List 類型包含了很多 List 的操作函數。 更多信息參考 泛型 和 集合.

  • Set
    在 Dart 中 Set 是一個元素唯一且無需的集合。 Dart 為 Set 提供了 Set 字面量和 Set 類型。

    版本提示: 雖然 Set 類型 一直是 Dart 的核心部分, 但在 Dart2.2 中才引入了 Set 字面量 。

    下面是通過字面量創建 Set 的一個簡單示例:

    var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
    

    Note: Dart 推斷 halogens 類型為 Set<String> 。如果嘗試為它添加一個 錯誤類型的值,分析器或執行時會拋出錯誤。更多內容,參閱 類型推斷。

    要創建一個空集,使用前面帶有類型參數的 {} ,或者將 {} 賦值給 Set 類型的變量:

    var names = <String>{};
    // Set<String> names = {}; // 這樣也是可以的。
    // var names = {}; // 這樣會創建一個 Map ,而不是 Set 。
    

    是 Set 還是 Map ? Map 字面量語法同 Set 字面量語法非常相似。 因為先有的 Map 字母量語法,所以 {} 默認是 Map 類型。 如果忘記在 {} 上注釋類型或賦值到一個未聲明類型的變量上, 那么 Dart 會創建一個類型為 Map<dynamic, dynamic> 的對象。

    使用 add() 或 addAll() 為已有的 Set 添加元素:

    var elements = <String>{};
    elements.add('fluorine');
    elements.addAll(halogens);
    

    使用 .length 來獲取 Set 中元素的個數:

    var elements = <String>{};
    elements.add('fluorine');
    elements.addAll(halogens);
    assert(elements.length == 5);
    

    在 Set 字面量前增加 const ,來創建一個編譯時 Set 常量:

    final constantSet = const {
      'fluorine',
      'chlorine',
      'bromine',
      'iodine',
      'astatine',
    };
    // constantSet.add('helium'); // Uncommenting this causes an error.
    

    更多關于 Set 的內容,參閱 Generic 及 Set。

  • Map
    通常來說, Map 是用來關聯 keys 和 values 的對象。 keys 和 values 可以是任何類型的對象。在一個 Map 對象中一個 key 只能出現一次。 但是 value 可以出現多次。 Dart 中 Map 通過 Map 字面量 和 Map 類型來實現。

    下面是使用 Map 字面量的兩個簡單例子:

    var gifts = {
      // Key:    Value
      'first': 'partridge',
      'second': 'turtledoves',
      'fifth': 'golden rings'
    };
    
    var nobleGases = {
      2: 'helium',
      10: 'neon',
      18: 'argon',
    };
    
    提示: Dart 會將 gifts 的類型推斷為 Map<String, String>, nobleGases 的類型推斷為 Map<int, String> 。 如果嘗試在上面的 map 中添加錯誤類型,那么分析器或者運行時會引發錯誤。 有關更多信息,請閱讀類型推斷。。

    以上 Map 對象也可以使用 Map 構造函數創建:

    var gifts = Map();
    gifts['first'] = 'partridge';
    gifts['second'] = 'turtledoves';
    gifts['fifth'] = 'golden rings';
    
    var nobleGases = Map();
    nobleGases[2] = 'helium';
    nobleGases[10] = 'neon';
    nobleGases[18] = 'argon';
    
    提示: 這里為什么只有 Map() ,而不是使用 new Map()。 因為在 Dart 2 中,new 關鍵字是可選的。 有關更多信息,參考 構造函數的使用。

    類似 JavaScript ,添加 key-value 對到已有的 Map 中:

    var gifts = {'first': 'partridge'};
    gifts['fourth'] = 'calling birds'; // Add a key-value pair
    

    類似 JavaScript ,從一個 Map 中獲取一個 value:

    var gifts = {'first': 'partridge'};
    assert(gifts['first'] == 'partridge');
    

    如果 Map 中不包含所要查找的 key,那么 Map 返回 null:

    var gifts = {'first': 'partridge'};
    assert(gifts['fifth'] == null);
    

    使用 .length 函數獲取當前 Map 中的 key-value 對數量:

    var gifts = {'first': 'partridge'};
    gifts['fourth'] = 'calling birds';
    assert(gifts.length == 2);
    

    創建 Map 類型運行時常量,要在 Map 字面量前加上關鍵字 const。

    final constantMap = const {
      2: 'helium',
      10: 'neon',
      18: 'argon',
    };
    
    // constantMap[2] = 'Helium'; // 取消注釋會引起錯誤。
    

    更名多關于 Map 的內容,參考 Generics and Maps.

  • Rune
    在 Dart 中, Rune 用來表示字符串中的 UTF-32 編碼字符。

    Unicode 定義了一個全球的書寫系統編碼, 系統中使用的所有字母,數字和符號都對應唯一的數值編碼。 由于 Dart 字符串是一系列 UTF-16 編碼單元, 因此要在字符串中表示32位 Unicode 值需要特殊語法支持。

    表示 Unicode 編碼的常用方法是, \uXXXX, 這里 XXXX 是一個4位的16進制數。 例如,心形符號 (?) 是 \u2665。 對于特殊的非 4 個數值的情況, 把編碼值放到大括號中即可。 例如,emoji 的笑臉 (?) 是 \u{1f600}。

String 類有一些屬性可以獲得 rune 數據。 屬性 codeUnitAt 和 codeUnit 返回16位編碼數據。 屬性 runes 獲取字符串中的 Rune 。

######提示: 謹慎使用 list 方式操作 Rune 。 這種方法很容易引發崩潰, 具體原因取決于特定的語言,字符集和操作。 有關更多信息,參考 How do I reverse a String in Dart? on Stack Overflow.
  • Symbol

    一個 Symbol 對象表示 Dart 程序中聲明的運算符或者標識符。 你也許永遠都不需要使用 Symbol ,但要按名稱引用標識符的 API 時, Symbol 就非常有用了。 因為代碼壓縮后會改變標識符的名稱,但不會改變標識符的符號。 通過字面量 Symbol ,也就是標識符前面添加一個 # 號,來獲取標識符的 Symbol 。

      #radix
      #bar
    

    Symbol 字面量是編譯時常量。

控制流程語句

你可以通過下面任意一種方式來控制 Dart 程序流程:

  • if and else

  • for loops

  • while and do-while loops

  • break and continue

  • switch and case

  • assert

使用 try-catch 和 throw 也可以改變程序流程, 詳見 Exceptions。

文章節選總結自來源

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容