Kotlin學(xué)習(xí)筆記(一)

本文介紹了Kotlin入門應(yīng)該知道一些基本語法概念。包括變量、常量、函數(shù)、空安全、類定義、類繼承、數(shù)據(jù)類、接口定義、冒號、可見性、擴展函數(shù)、Anko、對象表達式和聲明、Lambda表達式、when表達式、with函數(shù)、內(nèi)聯(lián)函數(shù)、Kotlin Android Extensions等。

本文首發(fā):http://yuweiguocn.github.io/

《送孟浩然之廣陵》
故人西辭黃鶴樓,煙花三月下?lián)P州。
孤帆遠影碧空盡,唯見長江天際流。
—唐,李白

本文所有用例基于Android Studio 3.0.1、Kotlin 1.2版本。

引入

在項目根目錄下 build.gradle 文件中添加 kotlin 插件依賴:

buildscript {
    ext.gradle_plugin_version = '3.0.1'
    ext.kotlin_version = '1.2.0'
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:$gradle_plugin_version"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

在主 module 下 build.gradle 文件中添加 kotlin 依賴:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
...
...
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

如果開啟了 Data Binding,還需要添加如下依賴:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
    dataBinding {
        enabled = true
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    kapt "com.android.databinding:compiler:$gradle_plugin_version"
}

變量

在 kotlin 中一切皆為對象,沒有像 Java 中的原始基本類型。在 kotlin 中使用 var 修飾的為變量。例如我們定義一個 Int 類型的變量并賦值為1:

var a: Int = 1
a += 1

由于 kotlin 編譯器可以自動推斷出變量的類型,所以我們通常不需要指定變量的類型

var s = "String" //類型為String
var a = 1 //類型為Int

在 kotlin 中分號不是必須的,不使用分號是一個不錯的實踐

常量

在 kotlin 中使用 val 修飾的為常量。這和 java 中的 final 很相似。在 kotlin 中有一個重要的概念是:盡可能地使用 val

val s = "String" //類型為String
val ll = 22L //類型為Long
val d = 2.5 //類型為Double
val f = 5.5F //類型為Float

函數(shù)

定義一個函數(shù)接受兩個 Int 型參數(shù),返回值類型為 Int :

fun sum(a: Int, b: Int): Int {
    return a + b
}

只有一個表達式作為函數(shù)體,以及自推導(dǎo)型的返回值:

fun sum(a: Int, b: Int) = a + b

函數(shù)的參數(shù)可以指定默認值:

fun sum(a: Int, b: Int = 10) = a + b

var c = sum(10) //調(diào)用

Unit 表示無返回值,對應(yīng) java 中 void:

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

Unit 的返回類型可以省略:

fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

空安全

在 kotlin 中,默認定義的變量不能為 null 的,這可以避免很多的 NullPointerException。

var a: String ="abc"
a = null //編譯錯誤

指定一個變量可null是通過在類型的最后增加一個問號

var b: String? = "abc"
b = null

當(dāng)變量聲明為可空時,在調(diào)用它的屬性時無法通過編譯:

var b: String? = "abc"
val l = b.length //編譯錯誤

在這種情況下,我們可以使用安全操作符 ?.

var b: String? = "abc"
val l = b?.length 

如果 b 不為空則返回長度,否則返回空,這個表達式的的類型是 Int?

我們還可以使用 ?: 操作符,當(dāng)前面的值不為空取前面的值,否則取后面的值,這和java中三目運算符類似。

val a:Int? = null
val myString = a?.toString() ?: ""

因為在Kotlin中 throw 和 return 都是表達式,他們可以用在Elvis operator操作符的右邊:

val myString = a?.toString() ?: return false
val myString = a?.toString() ?: throw IllegalStateException()

如果你確定該變量不為空,可以使用 !! 操作符:

var b: String? = "abc"
val l = b!!.length 

使用 !! 操作符可以跳過限制檢查通過編譯,此時如果變量為空會拋出空指針異常。如果大量使用此操作符,顯然不是很好的處理。

類定義

使用 class 定義一個類。類的聲明包含類名,類頭(指定類型參數(shù),主構(gòu)造函數(shù)等等),以及類主體,用大括
號包裹。類頭和類體是可選的;如果沒有類體可以省略大括號。

class MainActivity{
}

在 Kotlin 中類可以有一個主構(gòu)造函數(shù)以及多個二級構(gòu)造函數(shù)。主構(gòu)造函數(shù)是類頭的一部分:跟在類名后面(可以有可選的類型參數(shù))。

class Person constructor(firstName: String) {
}

如果主構(gòu)造函數(shù)沒有注解或可見性說明,則 constructor 關(guān)鍵字是可以省略:

class Person(name: String, surname: String)

構(gòu)造函數(shù)的函數(shù)體可以寫在 init 塊中:

class Customer(name: String) {
    init {
        logger.info("Customer initialized with value ${name}")
    }
}

注意主構(gòu)造函數(shù)的參數(shù)可以用在初始化塊內(nèi),也可以用在類的屬性初始化聲明處:

class Customer(name: String) {
    val customerKry = name.toUpperCase()
}

事實上,聲明屬性并在主構(gòu)造函數(shù)中初始化,在 Kotlin 中有更簡單的語法:

class Person(val firstName: String, val lastName: String, var age: Int) {
}

就像普通的屬性,在主構(gòu)造函數(shù)中的屬性可以是可變的( var )或只讀的( val )。

類繼承

Kotlin 中所有的類都有共同的父類 Any,它是一個沒有父類聲明的類的默認父類:

class Example // 隱式繼承于 Any

Any 不是 java.lang.Object ;事實上它除了 equals() , hashCode() 以及 toString() 外沒有任何成員了。

默認情況下,kotlin 中所有的類都是不可繼承 (final) 的,所以我們只能繼承那些明確聲明為 openabstract 的類,當(dāng)我們只有單個構(gòu)造器時,我們需要在從父類繼承下來的構(gòu)造器中指定需要的參數(shù)。這是用來替換Java中的 super 調(diào)用的。

open class Example(name: String)

class MyExample(name: String, age: Int) : Example(name)

數(shù)據(jù)類

數(shù)據(jù)類是一種非常強大的類,它可以讓你避免創(chuàng)建Java中的用于保存狀態(tài)但又操作非常簡單的POJO的模版代碼。它們通常只提供了用于訪問它們屬性的簡單的getter和setter。定義一個新的數(shù)據(jù)類非常簡單:

data class Forecast(val date: Date, val temperature: Float, val details: String)

編譯器會自動根據(jù)主構(gòu)造函數(shù)中聲明的所有屬性添加如下方法:

  • equals(): 它可以比較兩個對象的屬性來確保他們是相同的。
  • hashCode(): 我們可以得到一個hash值,也是從屬性中計算出來的。
  • toString(): 格式是 "User(name=john, age=42)"
  • copy(): 你可以拷貝一個對象,可以根據(jù)你的需要去修改里面的屬性。
  • componentN()函數(shù) 對應(yīng)按聲明順序出現(xiàn)的所有屬性

定義數(shù)據(jù)類需要注意的地方:

  • 主構(gòu)造函數(shù)應(yīng)該至少有一個參數(shù)。
  • 數(shù)據(jù)類的變量屬性只能是 varval 的。
  • 數(shù)據(jù)類不能是 abstract,open,sealed,或者 inner 。

復(fù)制數(shù)據(jù)類并修改某一屬性值:

val f1 = Forecast(Date(), 27.5f, "Shiny day")
val f2 = f1.copy(temperature = 30f)

映射對象的每一個屬性到一個變量中,這個過程就是我們知道的多聲明。這就是為什么會有 componentN 函數(shù)被自動創(chuàng)建。使用上面的 Forecast 類舉個例子:

val f1 = Forecast(Date(), 27.5f, "Shiny day")
val (date, temperature, details) = f1

上面這個多聲明會被編譯成下面的代碼:

val date = f1.component1()
val temperature = f1.component2()
val details = f1.component3()

這個特性背后的邏輯是非常強大的,它可以在很多情況下幫助我們簡化代碼。舉個例子, Map 類含有一些擴展函數(shù)的實現(xiàn),允許它在迭代時使用key和value:

for ((key, value) in map) {
    Log.d("map", "key:$key, value:$value")
}

接口定義

Kotlin 的接口很像 java 8。它們都可以包含抽象方法,以及方法的實現(xiàn)。和抽象類不同的是,接口不能保存狀態(tài)。可以有屬性但必須是抽象的,或者提供訪問器的實現(xiàn)。
接口用關(guān)鍵字 interface 來定義:

interface Bar {
    fun bar()
    fun foo() {
        //函數(shù)體是可選的
    }
}

冒號

在冒號區(qū)分類型和父類型中要有空格,在實例和類型之間是沒有空格的:

interface Foo<out T : Any> : Bar {
    fun foo(a: Int): T
}

可見性

在 kotlin 中,默認修飾符為 public

修飾符 說明
private 當(dāng)前類可見
protected 成員自己和繼承它的成員可見
internal 當(dāng)前 module 可見
public 所有地方可見

擴展函數(shù)

擴展函數(shù)數(shù)是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權(quán)限。這是一個在缺少有用函的類上擴展的方法。在Java中,通常會實現(xiàn)很多帶有static方法的工具類。Kotlin中擴展函數(shù)的一個優(yōu)勢是我們不需要在調(diào)用方法的時候把整個對象當(dāng)作參數(shù)傳入。擴展函數(shù)表現(xiàn)得就像是屬于這個類的一樣,而且我們可以使用 this 關(guān)鍵字和調(diào)用所有public方法。

舉個例子,我們可以創(chuàng)建一個toast函數(shù),這個函數(shù)不需要傳入任何context,它可以被任何Context或者它的子類調(diào)用,比如Activity或者Service:

fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}

這個方法可以在Activity內(nèi)部直接調(diào)用:

toast("Hello world!")
toast("Hello world!", Toast.LENGTH_LONG)

擴展函數(shù)也可以是一個屬性。所以我們可以通過相似的方法來擴展屬性。下面的例子展示了使用他自己的getter/setter生成一個屬性的方式。Kotlin由于互操作性的特性已經(jīng)提供了這個屬性,但理解擴展屬性背后的思想是一個很不錯的練習(xí):

public var TextView.text: CharSequence
    get() = getText()
    set(v) = setText(v)

擴展函數(shù)并不是真正地修改了原來的類,它是以靜態(tài)導(dǎo)入的方式來實現(xiàn)的。擴展函數(shù)可以被聲明在任何文件中,因此有個通用的實踐是把一系列有關(guān)的函數(shù)放在一個新建的文件里。

Anko

Anko是JetBrains開發(fā)的一個強大的庫。它主要的目的是用來替代以前XML的方式來使用代碼生成UI布局。Anko包含了很多的非常有幫助的函數(shù)和屬性來避免讓你寫很多的模版代碼。通過查看Anko源碼學(xué)習(xí)kotlin語言是一種不錯的方法。Anko能幫助我們簡化代碼,比如,實例化Intent,Activity之間的跳轉(zhuǎn),F(xiàn)ragment的創(chuàng)建,數(shù)據(jù)庫的訪問,Alert的創(chuàng)建等等。

github地址:https://github.com/Kotlin/anko

添加Anko的依賴:

// 主工程目錄下build.gradle文件中聲明版本
buildscript {
    ext.anko_version = '0.10.0'
}

// module的下build.gradle文件中添加依賴
dependencies {
    compile "org.jetbrains.anko:anko:$anko_version"
}

例如執(zhí)行Activity的跳轉(zhuǎn):

startActivity<MainActivity>()

//傳遞Intent參數(shù)
startActivity<NewHomeActivity>("name1" to "value1","name2" to "value2")

在Activity中顯示Toast:

toast("Hello world!")
longToast(R.id.hello_world)

線程切換:

async {
    val response = URL("http://yuweiguocn.github.io").readText()
    uiThread {
        textView.text = response
    }
}

對象表達式和聲明

有時候我們想要創(chuàng)建一個對當(dāng)前類有一點小修改的對象,但不想重新聲明一個子類。java 用匿名內(nèi)部類的概念解決這個問題。Kotlin 用對象表達式和對象聲明巧妙的實現(xiàn)了這一概念。

window.addMouseListener(object: MouseAdapter () {
    override fun mouseClicked(e: MouseEvent) {
        //...
    }
})

像 java 的匿名內(nèi)部類一樣,對象表達式可以訪問閉合范圍內(nèi)的變量 (和 java 不一樣的是,這些變量不用是 final 修飾的)

fun countClicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0
    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }
        override fun mouseEntered(e: MouseEvent){
            enterCount++
        }
    })
}

單例模式是一種很有用的模式,Kotln 中聲明它很方便,其中init代碼塊對應(yīng)java中static代碼塊。

object DataProviderManager {
    init { //對應(yīng)java中static代碼塊
    
    }

    fun registerDataProvider(provider: DataProvider) {
        // ...
    }
    val allDataProviders: Collection<DataProvider>
    get() = // ...
}

這叫做對象聲明,跟在 object 關(guān)鍵字后面是對象名。和變量聲明一樣,對象聲明并不是表達式,而且不能作為右值用在賦值語句。想要訪問這個類,直接通過名字來使用這個類:

// in kotlin
DataProviderManager.registerDataProvider(...)

// in java
DataProviderManager.INSTANCE.registerDataProvider(...)

注意:對象聲明不可以是局部的(比如不可以直接在函數(shù)內(nèi)部聲明),但可以在其它對象的聲明或非內(nèi)部類中進行內(nèi)嵌入。

我們需要一個類里面有一些靜態(tài)的屬性、常量或者函數(shù),我們可以使用伴隨對象。這個對象被這個類的所有對象所共享,就像java中的靜態(tài)屬性或者方法。在類聲明內(nèi)部可以用 companion 關(guān)鍵字標(biāo)記對象聲明:

class MyClass {
    companion object Factory {
        val URL = "http://yuweiguocn.github.io/"
        fun create(): MyClass = MyClass()
    }
}

伴隨對象的成員可以通過類名做限定詞直接使用:

// in kotlin
val instance = MyClass.create()
val url = MyClass.URL

// in java
MyClass c = MyClass.INSTANCE.create()
String url = MyClass.INSTANCE.getURL()

在使用了 companion 關(guān)鍵字時,伴隨對象的名字可以省略。

class MyClass {
    companion object {
        fun create(): MyClass = MyClass()
    }
}

注意:在kotlin中沒有 new 關(guān)鍵字

對象表達式和聲明的區(qū)別:

  • 對象表達式在我們使用的地方立即初始化并執(zhí)行的
  • 對象聲明是懶加載的,是在我們第一次訪問時初始化的。
  • 伴隨對象是在對應(yīng)的類加載時初始化的,和 Java 的靜態(tài)初始是對應(yīng)的。

Lambda表達式

Lambda表達式是一種很簡單的方法,去定義一個匿名函數(shù)。Lambda是非常有用的,因為它們避免我們?nèi)懸恍┌四承┖瘮?shù)的抽象類或者接口,然后在類中去實現(xiàn)它們。在Kotlin,我們把一個函數(shù)作為另一個函數(shù)的參數(shù)。

我們用Android中非常典型的例子去解釋它是怎么工作的: View.setOnClickListener() 方法。如果我們想用Java的方式去增加點擊事件的回調(diào),我首先要編寫一個 OnClickListener 接口:

public interface OnClickListener {
    void onClick(View v);
}

然后我們要編寫一個匿名內(nèi)部類去實現(xiàn)這個接口:

view.setOnClickListener(new OnClickListener(){
    @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
    }
});

我們將把上面的代碼轉(zhuǎn)換成Kotlin(使用了Anko的toast函數(shù)):

view.setOnClickListener(object : OnClickListener {
    override fun onClick(v: View) {
        toast("Click")
    }
}

Kotlin允許Java庫的一些優(yōu)化,Interface中包含單個函數(shù)可以被替代為一個函數(shù)。如果我們這么去定義了,它會正常執(zhí)行:

fun setOnClickListener(listener: (View) -> Unit)

一個lambda表達式通過參數(shù)的形式被定義在箭頭的左邊(普通圓括號包圍),然后在箭頭的右邊返回結(jié)果值。當(dāng)我們定義了一個方法,我們必須使用大括號包圍。如果左邊的參數(shù)沒有用到,我們甚至可以省略左邊的參數(shù)。

view.setOnClickListener({ view -> toast("Click")})

view.setOnClickListener({ toast("Click") })

如果這個函數(shù)只接收一個參數(shù),我們可以使用it引用,而不用去指定左邊的參數(shù):

view.setOnClickListener({ toast("Click" + it.id)})

如果這個函數(shù)的最后一個參數(shù)是一個函數(shù),我們可以把這個函數(shù)移動到圓括號外面:

view.setOnClickListener() { toast("Click") }

并且,最后,如果這個函數(shù)只有一個參數(shù),我們可以省略這個圓括號:

view.setOnClickListener { toast("Click") }

When表達式

when 表達式與Java中的 switch/case 類似,但是要強大得多。這個表達式會去試圖匹配所有可能的分支直到找到滿意的一項。然后它會運行右邊的表達式。與Java的 switch/case 不同之處是參數(shù)可以是任何類型,并且分支也可以是一個條件。

對于默認的選項,我們可以增加一個 else 分支,它會在前面沒有任何條件匹配時再執(zhí)行。條件匹配成功后執(zhí)行的代碼也可以是代碼塊:

when (x){
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("I'm a block")
        print("x is neither 1 nor 2")
    }
}

因為它是一個表達式,它也可以返回一個值。我們需要考慮什么時候作為一個表達式使用,它必須要覆蓋所有分支的可能性或者實現(xiàn) else 分支。否則它不會被編譯成功:

val result = when (x) {
    0, 1 -> "binary"
    else -> "error"
}

with函數(shù)

with是一個非常有用的函數(shù),包含在Kotlin的標(biāo)準(zhǔn)庫中。它接收一個對象和一個擴展函數(shù)作為它的參數(shù),然后使這個對象擴展這個函數(shù)。這表示所有我們在括號中編寫的代碼都是作為對象(第一個參數(shù))的一個擴展函數(shù),我們可以就像作為this一樣使用所有它的public方法和屬性。當(dāng)我們針對同一個對象做很多操作的時候這個非常有利于簡化代碼。

data class Person(val name: String, val age: Int)

val p = Person("growth",25)
with(p){
    var info = “$name - $age” 
}

內(nèi)聯(lián)函數(shù)

下面是with函數(shù)的定義:

inline fun <T> with(t: T, body: T.() -> Unit) { t.body() }

這個函數(shù)接收一個 T 類型的對象和一個被作為擴展函數(shù)的函數(shù)。它的實現(xiàn)僅僅是讓這個對象去執(zhí)行這個函數(shù)。因為第二個參數(shù)是一個函數(shù),所以我們可以把它放在圓括號外面,所以我們可以創(chuàng)建一個代碼塊,在這這個代碼塊中我們可以使用 this 和直接訪問所有的public的方法和屬性。

內(nèi)聯(lián)函數(shù)與普通的函數(shù)有點不同。一個內(nèi)聯(lián)函數(shù)會在編譯的時候被替換掉,而不是真正的方法調(diào)用。這在一些情況下可以減少內(nèi)存分配和運行時開銷。舉個例子,如果我們有一個函數(shù),只接收一個函數(shù)作為它的參數(shù)。如果是一個普通的函數(shù),內(nèi)部會創(chuàng)建一個含有那個函數(shù)的對象。另一方面,內(nèi)聯(lián)函數(shù)會把我們調(diào)用這個函數(shù)的地方替換掉,所以它不需要為此生成一個內(nèi)部的對象。

inline fun supportsLollipop(code: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        code()
    }
}

它只是檢查版本,然后如果滿足條件則去執(zhí)行。現(xiàn)在我們可以這么做:

supportsLollipop {
    window.setStatusBarColor(Color.BLACK)
}

Kotlin Android Extensions

Kotlin Android Extensions是另一個kotlin團隊研發(fā)的可以讓開發(fā)更簡單的插件。該插件依賴于 kotlin 標(biāo)準(zhǔn)庫,當(dāng)前僅僅包括了view的綁定,這可以讓我們省去findViewById操作。

使用該插件非常簡單,修改module的build.gradle文件:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

例如在布局文件中定義一了個id為tvTest的TextView,在Activity的setContentView之后就可以直接使用該TextView了:

class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvTest.text = "hello world"
    }
}

參考

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

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