Java 異常處理

1. 概述

在本文中, 我們將介紹Java 中異常處理的基礎(chǔ)知識(shí)肛炮,及其一些常見(jiàn)的坑.

2. 前置知識(shí)

2.1. 什么是異常岭辣?

為了更好的理解異常和處理異常忍啤,讓我們看一個(gè)真實(shí)的場(chǎng)景. 想象一下我們?cè)诰€購(gòu)物履磨,但是途中交付失敗渡贾,優(yōu)秀的公司可以優(yōu)雅的處理此問(wèn)題逗宜,并重新安排我們的包裹,保證包裹能夠按時(shí)送達(dá) . 同樣空骚,在Java中纺讲,代碼在執(zhí)行我們指令的時(shí)候也可能會(huì)遇到錯(cuò)誤,出色的exception 處理 可以處理錯(cuò)誤囤屹,并優(yōu)雅的重新路由程序熬甚,從而給用戶帶來(lái)良好的體驗(yàn).

2.2. 為什么要使用異常?

我們通常在理想得環(huán)境中編寫代碼: 文件系統(tǒng)始終包含我們得文件, 網(wǎng)絡(luò)狀況永遠(yuǎn)良好, 并且 JVM 始終具有足夠得內(nèi)存. 有時(shí)我們稱之為 “幸福之路”. 但是,在生產(chǎn)環(huán)境中, 文件系統(tǒng)可能會(huì)損壞, 網(wǎng)絡(luò)崩潰, 并且JVM 內(nèi)存不足. 我們代碼得優(yōu)劣取決于如何處理這些 “不愉快得道路”. 我們必須處理這些情況肋坚,因?yàn)樗鼈儠?huì)對(duì)應(yīng)用程序得流程產(chǎn)生負(fù)面影響乡括,并產(chǎn)生exceptions:

public static List<Player> getPlayers() throws IOException {
    Path path = Paths.get("players.dat");
    List<String> players = Files.readAllLines(path);
 
    return players.stream()
      .map(Player::new)
      .collect(Collectors.toList());
}

這段代碼選擇不處理 IOException, 而是將其傳遞給調(diào)用堆棧. 在理想環(huán)境下, 代碼可以正常工作. 但是, 如果缺少 players.dat 文件, 會(huì)發(fā)生什么呢?

Exception in thread "main" java.nio.file.NoSuchFileException: players.dat <-- players.dat file doesn't exist
    at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
    at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
    // ... more stack trace
    at java.nio.file.Files.readAllLines(Unknown Source)
    at java.nio.file.Files.readAllLines(Unknown Source)
    at Exceptions.getPlayers(Exceptions.java:12) <-- Exception arises in getPlayers() method, on line 12
    at Exceptions.main(Exceptions.java:19) <-- getPlayers() is called by main(), on line 19

如果不處理此異常, 則正常運(yùn)行得程序可能會(huì)完全停止運(yùn)行! 我們需要確保代碼在萬(wàn)一有異常得情況下能夠正常運(yùn)行. 需要說(shuō)明得是,異常也有好處, 那就是異常堆棧本身. 因?yàn)槎褩>哂兴菰垂δ? 因此我們經(jīng)持茄幔可以精確定位有問(wèn)題得代碼诲泌,而無(wú)需使用調(diào)試器.

3. Exception 層次結(jié)構(gòu)

exceptions 終歸也是屬于Java對(duì)象, 所有異常都是從 Throwable 拓展而來(lái):

              ---> Throwable <--- 
              |    (checked)     |
              |                  |
              |                  |
      ---> Exception           Error
      |    (checked)        (unchecked)
      |
RuntimeException
  (unchecked)

異常主要分為以下三類:

  • Checked exceptions
  • Unchecked exceptions / Runtime exceptions
  • Errors

Runtime and unchecked exceptions 是同一回事. 我們?cè)谑褂弥锌梢詫?duì)它們進(jìn)行互換.

3.1. Checked Exceptions

Checked exceptions 是Java 編譯器要求我們必須處理的 exceptions . 我們要么聲明式的將異常拋出到調(diào)用堆棧中, 要么我們必須自己處理它. 稍后我們將詳細(xì)討論這兩個(gè)方面. Oracle's documentation 告訴我們,當(dāng)我們期望方法調(diào)用者能夠合理的恢復(fù)時(shí), 請(qǐng)使用 checked exceptions . IOExceptionServletException 是 checked exceptions的示例.

3.2. Unchecked Exceptions

Unchecked exceptions 是Java編譯器不要求我們處理的異常. 簡(jiǎn)而言之铣鹏,如果我們創(chuàng)建一個(gè)繼承自 RuntimeException 的異常, 則屬于Unchecked exceptions; 反之, 屬于Checked exceptions. 盡管聽(tīng)起來(lái)好像比較簡(jiǎn)單, Oracle's documentation 很好的講解了這兩個(gè)概念, 例如區(qū)別 error (checked) 和 error (unchecked). NullPointerException, IllegalArgumentException,SecurityException 屬于 unchecked exceptions 的示例.

3.3. Errors

Errors 表示嚴(yán)重且通常不可恢復(fù)的情況, 例如庫(kù)不兼容, 無(wú)限遞歸, 內(nèi)存泄漏 . 即使它們不是繼承自 RuntimeException, 編譯器也不會(huì)對(duì)其進(jìn)行檢查. 在大多數(shù)情況下敷扫,我們不需要處理,實(shí)例化或是繼承 Errors. 通常诚卸,我們希望它們能夠一直向上傳遞. StackOverflowErrorOutOfMemoryError 屬于 errors 的示例.

4. Exceptions 處理

在 Java API 中, 有很多地方可能會(huì)出錯(cuò), 并且其中一些地方會(huì)在簽名或Javadoc 中都會(huì)聲明exceptions :

/**
 * @exception FileNotFoundException ...
 */
public Scanner(String fileName) throws FileNotFoundException {
   // ...
}

如上所述, 當(dāng)我們調(diào)用這些 “risky” 方法時(shí), 我們 必須 處理 checked exceptions, 并且 可能 會(huì)處理 unchecked exceptions. Java 提供了處理這些問(wèn)題的幾種方法:

4.1. throws

處理異常最簡(jiǎn)單的方法是重新拋出去:

public int getPlayerScore(String playerFile)
  throws FileNotFoundException {
  
    Scanner contents = new Scanner(new File(playerFile));
    return Integer.parseInt(contents.nextLine());
}

因?yàn)?FileNotFoundException 是一個(gè) checked exception, 所以重新拋出去是滿足編譯器最簡(jiǎn)單的方法, 但是 這意味著調(diào)用我們方法的其他人也必須要處理它! parseInt 會(huì)拋出 NumberFormatException, 但是因?yàn)樗?unchecked, 所以我們不是必須要處理它.

4.2. try-catch

如果想要嘗試自己處理異常, 我們可以使用 try-catch 語(yǔ)句塊. 我們可以通過(guò)重新拋出我們的異常來(lái)處理它(和throws不同的是葵第,拋出的異常類型我們可以自定義):

public int getPlayerScore(String playerFile) {
    try {
        Scanner contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException noFile) {
        throw new IllegalArgumentException("File not found");
    }
}

或通過(guò)執(zhí)行回復(fù)步驟:

public int getPlayerScore(String playerFile) {
    try {
        Scanner contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch ( FileNotFoundException noFile ) {
        logger.warn("File not found, resetting score.");
        return 0;
    }
}

4.3. finally

現(xiàn)在, 有時(shí)無(wú)論是否發(fā)生異常绘迁,我們都有需要執(zhí)行的代碼,這就是 finally 關(guān)鍵字的所在. 到目前為止卒密,在我們的示例中缀台,有一個(gè)討厭的錯(cuò)誤潛伏在陰影中,這是Java 默認(rèn)情況下不會(huì)將文件句柄返回給操作系統(tǒng) . 當(dāng)然栅受,無(wú)論我們是否能夠讀取文件, 我們都希望確保流的連接釋放. 下面讓我們先看看這種“l(fā)azy”方式:

public int getPlayerScore(String playerFile)
  throws FileNotFoundException {
    Scanner contents = null;
    try {
        contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } finally {
        if (contents != null) {
            contents.close();
        }
    }
}

在這里, finally 語(yǔ)句塊是我們希望Java運(yùn)行的代碼将硝,而不管讀取文件時(shí)發(fā)生了什么 . 即使 FileNotFoundException 拋出了調(diào)用堆棧 , Java 會(huì)在拋出異常前先調(diào)用 finally 塊中的內(nèi)容 . 我們還可以處理異常并確保資源被關(guān)閉:

public int getPlayerScore(String playerFile) {
    Scanner contents;
    try {
        contents = new Scanner(new File(playerFile));
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException noFile ) {
        logger.warn("File not found, resetting score.");
        return 0; 
    } finally {
        try {
            if (contents != null) {
                contents.close();
            }
        } catch (IOException io) {
            logger.error("Couldn't close the reader!", io);
        }
    }
}

因?yàn)?close 同樣也是一個(gè) “risky” 方法, 我們同樣需要捕獲它的 exception! 這看起來(lái)很復(fù)雜, 但是我們需要確保每一次的調(diào)用都能正確的處理可能出現(xiàn)的潛在問(wèn)題。

4.4. try-with-resources

幸運(yùn)的是, 從 Java 7 開(kāi)始, 在處理繼承AutoCloseable的類時(shí)屏镊,我們可以簡(jiǎn)化上述語(yǔ)法:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
      return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException e ) {
      logger.warn("File not found, resetting score.");
      return 0;
    }
}

當(dāng)我們?cè)?try 聲明中處理 AutoClosable 引用時(shí), 則不需要自己關(guān)閉資源. 但是我們依然可以使用 finally 語(yǔ)句塊來(lái)執(zhí)行我們想要的其他任意類型的資源閉關(guān)或清理工作.

4.5. 多個(gè) catch 語(yǔ)句塊

有時(shí), 代碼可能引發(fā)多個(gè)異常, 我們需要多個(gè) catch 語(yǔ)句塊分別處理對(duì)應(yīng)的異常:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
        return Integer.parseInt(contents.nextLine());
    } catch (IOException e) {
        logger.warn("Player file wouldn't load!", e);
        return 0;
    } catch (NumberFormatException e) {
        logger.warn("Player file was corrupted!", e);
        return 0;
    }
}

多次捕獲異骋捞郏可以使我們以不同的方式處理每個(gè)異常. 這里需要說(shuō)明的時(shí),我們沒(méi)有捕獲 FileNotFoundException, 是因?yàn)樗?extends IOException. 因?yàn)槲覀儾东@了 IOException, 所以 Java 也會(huì)處理其任何子類 . 但是而芥,如果需要將 FileNotFoundException 和范圍更廣的 IOException 區(qū)別對(duì)待律罢,如下所示:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile)) ) {
        return Integer.parseInt(contents.nextLine());
    } catch (FileNotFoundException e) {
        logger.warn("Player file not found!", e);
        return 0;
    } catch (IOException e) {
        logger.warn("Player file wouldn't load!", e);
        return 0;
    } catch (NumberFormatException e) {
        logger.warn("Player file was corrupted!", e);
        return 0;
    }
}

Java 允許我們分別處理子類異常, 請(qǐng)記住將它們放在捕獲列表中比父類更高的位置.

4.6. 合并 catch 塊

但是,當(dāng)我們錯(cuò)誤處理的方式是相同的時(shí)棍丐,我們不需要像上面一樣误辑,每個(gè)異常都單獨(dú)處理口锭, Java 7 引入了在同一塊中捕獲多個(gè)異常的功能:

public int getPlayerScore(String playerFile) {
    try (Scanner contents = new Scanner(new File(playerFile))) {
        return Integer.parseInt(contents.nextLine());
    } catch (IOException | NumberFormatException e) {
        logger.warn("Failed to load score!", e);
        return 0;
    }
}

5. Throwing Exceptions

如果我們不想自己處理異常霎桅,或者想要自定義異常以供其他人處理,那么我們需要了解 throw 關(guān)鍵字. 假設(shè)我們自定義 checked exception :

public class TimeoutException extends Exception {
    public TimeoutException(String message) {
        super(message);
    }
}

而且我們有一個(gè)方法可能需要花費(fèi)很長(zhǎng)時(shí)間來(lái)執(zhí)行:

public List<Player> loadAllPlayers(String playersFile) {
    // ... potentially long operation
}

5.1. Throwing a Checked Exception

就像從方法中返回一樣, 我們可以隨時(shí) throw . 當(dāng)然坯约,當(dāng)我們需要表示可能出現(xiàn)的問(wèn)題時(shí)秘案,我們應(yīng)該拋出:

public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
    while ( !tooLong ) {
        // ... potentially long operation
    }
    throw new TimeoutException("This operation took too long");
}

因?yàn)?TimeoutException 是 checked Exception, 所以我們還必須在簽名中使用 throws 關(guān)鍵字砰苍,以便我們方法的調(diào)用者知道如何去處理它.

5.2. Throwing an Unchecked Exception

如果我們想執(zhí)行如輸入驗(yàn)證之類的操作, 則可以使用unchecked exception :

public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
    if(!isFilenameValid(playersFile)) {
        throw new IllegalArgumentException("Filename isn't valid!");
    }
    
    // ...
}

因?yàn)?IllegalArgumentException 是 unchecked exception, 所以我們不必標(biāo)記該方法 , 雖然同樣能夠標(biāo)記會(huì)更好一點(diǎn) .

5.3. 封裝并且重新拋出

我們還可以選擇重新拋出一個(gè)已捕獲的異常:

public List<Player> loadAllPlayers(String playersFile) 
  throws IOException {
    try { 
        // ...
    } catch (IOException io) {      
        throw io;
    }
}

或者做一下封裝然后再拋出去:

public List<Player> loadAllPlayers(String playersFile) 
  throws PlayerLoadException {
    try { 
        // ...
    } catch (IOException io) {      
        throw new PlayerLoadException(io);
    }
}

對(duì)于將許多不同的異常合并為一個(gè)異常可能會(huì)很好的方式.

5.4. 重新拋出 Throwable 或 Exception

現(xiàn)在又一種特殊情況. 如果給定代碼塊可能引發(fā)的唯一可能的異常時(shí) unchecked exceptions, 那么我們可以捕獲并重新拋出 ThrowableException 阱高,而無(wú)需將它們添加到方法簽名上:

public List<Player> loadAllPlayers(String playersFile) {
    try {
        throw new NullPointerException();
    } catch (Throwable t) {
        throw t;
    }
}

雖然簡(jiǎn)單, 但上面的代碼無(wú)法拋出checked exception, 因此赚导,即使我們重新拋出 checked exception, 我們也不必使用throws 子句標(biāo)記簽名. 這對(duì)于代理類和方法很方便. 更多詳情請(qǐng)看 這里.

5.5. Inheritance

當(dāng)我們使用 throws 關(guān)鍵字標(biāo)記方法時(shí), 它將影響子類如何覆蓋我們的方法. 在我們的方法 throws a checked exception 的情況下:

public class Exceptions {
    public List<Player> loadAllPlayers(String playersFile) 
      throws TimeoutException {
        // ...
    }
}

子類可以具有 “小風(fēng)險(xiǎn)” signature:

public class FewerExceptions extends Exceptions {   
    @Override
    public List<Player> loadAllPlayers(String playersFile) {
        // overridden
    }
}

但不是 “多風(fēng)險(xiǎn)” signature:

public class MoreExceptions extends Exceptions {        
    @Override
    public List<Player> loadAllPlayers(String playersFile) throws MyCheckedException {
        // overridden
    }
}

這是因?yàn)楹霞s時(shí)在編譯期由引用類型確定的. 如果我創(chuàng)建一個(gè) MoreExceptions 實(shí)例且用 Exceptions 聲明:

Exceptions exceptions = new MoreExceptions();
exceptions.loadAllPlayers("file");

然后JVM 只會(huì)告訴我捕獲 TimeoutException, 這時(shí)錯(cuò)誤的,因?yàn)槲覀冃枰?MoreExceptions#loadAllPlayers 拋出一個(gè)不同的異常 . 簡(jiǎn)而言之赤惊,子類可以拋出的 checked exceptions 比父類少吼旧,但不能更多.

6. Anti-Patterns

6.1. 吞噬 Exceptions

現(xiàn)在, 我們有一種可以滿足編譯器的另一種處理異常的方式:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (Exception e) {} // <== catch and swallow
    return 0;
}

以上稱之為吞噬 exception. 在大多數(shù)情況下, 這樣做對(duì)我們而言意義不大,因?yàn)樗荒芙鉀Q問(wèn)題, 并且也使得其他使用該方法的代碼也同樣無(wú)法解決問(wèn)題. 有時(shí)候未舟,我們有信心代碼將永遠(yuǎn)不會(huì)發(fā)生 checked exception . 在這些情況下, 我們?nèi)匀粦?yīng)該至少添加一條注釋圈暗,說(shuō)明我們有意吃了該異常:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (IOException e) {
        // this will never happen
    }
}

我們可以吞噬異常的另一種方式是簡(jiǎn)單地將異常輸出到錯(cuò)誤流:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (Exception e) {
        e.printStackTrace();
    }
    return 0;
}

我們至少通過(guò)將錯(cuò)誤寫到某個(gè)地方供以后診斷,改善了完全吞噬異常的弊端. 不過(guò)裕膀,最好使用logger來(lái)記錄:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (IOException e) {
        logger.error("Couldn't load the score", e);
        return 0;
    }
}

盡管以這種方式處理異常對(duì)于我們來(lái)說(shuō)非常方便, 但是我們需要確保我們不會(huì)隱藏代碼調(diào)用者可以用來(lái)解決問(wèn)題的重要信息. 最后, 當(dāng)我們拋出新異常時(shí)厂置,可以不包括原來(lái)異常的原因,從而吞噬原來(lái)的異常:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (IOException e) {
        throw new PlayerScoreException();
    }
}

在這里, 我們?yōu)榱烁嬷{(diào)用者會(huì)出現(xiàn)異常而拋出一個(gè)沒(méi)有意義的異, 但是 我們沒(méi)有將 IOException 這個(gè)異常的原因包括在內(nèi). 因此, 我們丟失了調(diào)用者可以用來(lái)診斷問(wèn)題的重要信息. 我們最好這樣做:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch (IOException e) {
        throw new PlayerScoreException(e);
    }
}

請(qǐng)注意, 將 IOException 作為 PlayerScoreException 原因魂角,和隱藏異常原因有細(xì)微的差別.

6.2. Using return in a finally Block

吞噬異常的另一種方式是在 finally 塊中 return . 這是不好的, 因?yàn)橥ㄟ^(guò)突然返回 , the JVM 丟棄該異常, 即使該異常是由我們代碼拋出的:

public int getPlayerScore(String playerFile) {
    int score = 0;
    try {
        throw new IOException();
    } finally {
        return score; // <== the IOException is dropped
    }
}

根據(jù) Java Language Specification:

If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice.

If the finally block completes normally, then the try statement completes abruptly for reason R.

If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

6.3. Using throw in a finally Block

與在 finally 塊中使用 return 類似, 在 finally 塊中拋出的異常就優(yōu)先與catch 塊中出現(xiàn)的異常. 這將擦除 try 塊中的原始異常昵济,我們將丟失所有這些有價(jià)值的信息:

public int getPlayerScore(String playerFile) {
    try {
        // ...
    } catch ( IOException io ) {
        throw new IllegalStateException(io); // <== eaten by the finally
    } finally {
        throw new OtherException();
    }
}

6.4. Using throw as a goto

有些人還傾向于使用 throw 作為 goto 語(yǔ)句:

public void doSomething() {
    try {
        // bunch of code
        throw new MyException();
        // second bunch of code
    } catch (MyException e) {
        // third bunch of code
    }       
}

這很奇怪,因?yàn)榇a試圖使用異常用于流程控制,而不是錯(cuò)誤處理.

7. 常見(jiàn) Exceptions 和 Errors

一下是我們經(jīng)常遇到的一些常見(jiàn) exceptions 和 errors :

7.1. Checked Exceptions

  • IOException – 此異常通常表示網(wǎng)絡(luò), 文件系統(tǒng), 或 數(shù)據(jù)庫(kù)發(fā)生故障.

7.2. RuntimeExceptions

  • ArrayIndexOutOfBoundsException – 此異常表示我們?cè)噲D訪問(wèn)不存在的數(shù)組索引, 比如試圖從長(zhǎng)度為3的數(shù)組中獲取索引為5的內(nèi)容.

  • ClassCastException – 此異常表示我們?cè)噲D執(zhí)行非法轉(zhuǎn)換, 例如嘗試將 String 轉(zhuǎn)化為 List. 我們通常在轉(zhuǎn)化之前通過(guò) instanceof 檢查來(lái)避免這種情況.

  • IllegalArgumentException – 此異常通常是方法或構(gòu)造函數(shù)的參數(shù)無(wú)效.

  • IllegalStateException – 此異常表示內(nèi)部狀態(tài)(如對(duì)象狀態(tài))無(wú)效.

  • NullPointerException – 此異常表示我們嘗試引用 null 對(duì)象. 我們通常使用 Optional 檢查來(lái)避免這種這種情況.

  • NumberFormatException – 此異常表示我們?cè)噲D將 String 轉(zhuǎn)化為數(shù)字 , 但是字符串包含非法字符, 如將 “5f3” 轉(zhuǎn)化為數(shù)字.

7.3. Errors

  • StackOverflowError – 此異常表示棧跟蹤太深. 有時(shí)在大規(guī)模應(yīng)用中可能會(huì)發(fā)生這種情況; 但是, 通常來(lái)說(shuō)表示我們的代碼中發(fā)生了無(wú)限遞歸.

  • NoClassDefFoundError – 此異常表示類加載失敗访忿,原因是由于類不再 classpath 下, 或靜態(tài)初始化失敗.

  • OutOfMemoryError – 此異常表示 JVM 沒(méi)有足夠的內(nèi)存來(lái)分配更多的對(duì)象. 有時(shí), 這是因?yàn)閮?nèi)存泄漏引起的.

8. 結(jié)語(yǔ)

在本文中, 我們介紹了異常處理的基礎(chǔ)知識(shí)以及一些實(shí)踐示例. 所有的代碼請(qǐng)看這里 GitHub!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞧栗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子海铆,更是在濱河造成了極大的恐慌迹恐,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卧斟,死亡現(xiàn)場(chǎng)離奇詭異殴边,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)珍语,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門锤岸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人板乙,你說(shuō)我怎么就攤上這事是偷。” “怎么了募逞?”我有些...
    開(kāi)封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵蛋铆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我放接,道長(zhǎng)刺啦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任纠脾,我火速辦了婚禮洪燥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乳乌。我一直安慰自己,他們只是感情好市咆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布汉操。 她就那樣靜靜地躺著,像睡著了一般蒙兰。 火紅的嫁衣襯著肌膚如雪磷瘤。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天搜变,我揣著相機(jī)與錄音采缚,去河邊找鬼。 笑死挠他,一個(gè)胖子當(dāng)著我的面吹牛扳抽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贸呢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼镰烧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起楞陷,我...
    開(kāi)封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怔鳖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后固蛾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體结执,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年艾凯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了献幔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡览芳,死狀恐怖斜姥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沧竟,我是刑警寧澤铸敏,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站悟泵,受9級(jí)特大地震影響杈笔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜糕非,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一蒙具、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朽肥,春花似錦禁筏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至始腾,卻和暖如春州刽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浪箭。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工穗椅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奶栖。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓匹表,卻偏偏與公主長(zhǎng)得像门坷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桑孩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354