大概優(yōu)秀的java程序員都要會(huì)分析class文件吧

相信大家在學(xué)java的時(shí)候都會(huì)聽到這樣的一些結(jié)論:

  1. enum 是一個(gè)類
  2. 泛型的實(shí)現(xiàn)使用了類型擦除技術(shù)
  3. 非靜態(tài)內(nèi)部類持有外部類的引用
  4. 需要將自由變量聲明成final才能給匿名內(nèi)部類訪問

...

初學(xué)的時(shí)候的時(shí)候可能在書上讀過,但是很容易就會(huì)忘記,等到踩坑踩多了,就會(huì)形成慢慢記住肠缔。但是很多的同學(xué)也只是記住了而已,對(duì)于實(shí)際的原理或者原因并不了解投剥。

這篇文章的目的主要就是教會(huì)大家查看java的字節(jié)碼,然后懂得去分析這些結(jié)論背后的原理。

枚舉最后會(huì)被編譯成一個(gè)類

我們先從簡(jiǎn)單的入手.

java的新手對(duì)于枚舉的理解可能是:存儲(chǔ)幾個(gè)固定值的集合,例如下面的Color枚舉,使用的時(shí)候最多也就通過ordinal()方法獲取下枚舉的序號(hào)或者從Color.values()里面使用序號(hào)拿到一個(gè)Color:

public enum Color {
    RED,
    GREEN,
    BLUE
}

int index = Color.BLUE.ordinal();
Color color = Color.values()[index];

如果是從C/C++過來的人比如我,很容易形成這樣一種固定的思維:枚舉就是一種被命名的整型的集合老虫。

在c/c++里面這種想法還能說的過去,但是到了java就大錯(cuò)特錯(cuò)了,錯(cuò)過了java枚舉的一些好用的特性亲雪。

還是拿我們上面的Color枚舉,顏色我們經(jīng)常使用0xFF0000這樣的16進(jìn)制整型或者“#FF0000”這樣的字符串去表示撞蜂。

在java中,我們可以這樣將這個(gè)Color枚舉和整型還有字符串關(guān)聯(lián)起來:

public enum Color {
    RED(0xFF0000, "#FF0000"),
    GREEN(0x00FF00, "#00FF00"),
    BLUE(0x0000FF, "#0000FF");

    private int mIntVal;
    private String mStrVal;

    Color(int intVal, String strVal) {
        mIntVal = intVal;
        mStrVal = strVal;
    }

    public int getIntVal() {
        return mIntVal;
    }

    public String getStrVal() {
        return mStrVal;
    }
}

System.out.println(Color.RED.getIntVal());
System.out.println(Color.RED.getStrVal());

可以看到我們給Color這個(gè)枚舉,增加了兩個(gè)成員變量用來存整型和字符串的表示,然后還提供兩個(gè)get方法給外部獲取远剩。

甚至進(jìn)一步的,枚舉的一種比較常用的技巧就是在static塊中創(chuàng)建映射:

public enum Color {
    RED(0xFF0000, "#FF0000"),
    GREEN(0x00FF00, "#00FF00"),
    BLUE(0x0000FF, "#0000FF");

    private static final Map<String, Color> sMap = new HashMap<>();

    static {
        for (Color color : Color.values()) {
            sMap.put(color.getStrVal(), color);
        }
    }

    public static Color getFromStrVal(String strVal){
        return sMap.get(strVal);
    }

    private int mIntVal;
    private String mStrVal;

    Color(int intVal, String strVal) {
        mIntVal = intVal;
        mStrVal = strVal;
    }

    public int getIntVal() {
        return mIntVal;
    }

    public String getStrVal() {
        return mStrVal;
    }
}

System.out.println(Color.getFromStrVal("#FF0000").getIntVal());
System.out.println(Color.RED.getIntVal());

看起來是不是感覺和一個(gè)類的用法很像?"enum 是一個(gè)類"這樣句話是不是講的很有道理茴恰。

當(dāng)然用法和類很像并不能說明什么竖配。

接下來就到了我們這篇文章想講的第一個(gè)關(guān)鍵知識(shí)點(diǎn)了。

反編譯class文件

首先我們還是將Color簡(jiǎn)化回最初的樣子,然后保存在Color.java文件中:

// Color.java
public enum Color {
    RED,
    GREEN,
    BLUE
}

然后通過javac命令進(jìn)行編譯,得到Color.class

javac Color.java

得到的class文件就是jvm可以加載運(yùn)行的文件,里面都是一些java的字節(jié)碼里逆。

java其實(shí)默認(rèn)提供了一個(gè)javap命令,給我們?nèi)ゲ榭碿lass文件里面的代碼用爪。例如,在Color.class所在的目錄使用下面命令:

javap Color

可以看到下面的輸出:

Compiled from "Color.java"
public final class Color extends java.lang.Enum<Color> {
  public static final Color RED;
  public static final Color GREEN;
  public static final Color BLUE;
  public static Color[] values();
  public static Color valueOf(java.lang.String);
  static {};
}

是不是有種恍然大明白的感覺?Color在class文件里面實(shí)際上是被編譯成了一個(gè)繼承java.lang.Enum的類,而我們定義的RED原押、GREEN、BLUE實(shí)際上是這個(gè)類的靜態(tài)成員變量偎血。

這么去看的話我們那些加成員變量诸衔、加方法的操作是不是就變得很常規(guī)了?

所以說"enum 是一個(gè)類"的意思其實(shí)是enum會(huì)被java編譯器編譯成一個(gè)繼承java.lang.Enum的類!

java運(yùn)行時(shí)棧幀

相信大家都知道,java虛擬機(jī)里面的方法調(diào)用是以方法棧的形式去執(zhí)行的.壓人棧內(nèi)的元素就叫做棧幀.

<深入理解java虛擬機(jī)>一書中是這么介紹棧幀的:

棧幀(Stack Frame)是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的虛擬機(jī)棧(Virtual Machine Stack)的棧元素颇玷。棧幀存儲(chǔ)了方法的局部變量表笨农,操作數(shù)棧,動(dòng)態(tài)連接和方法返回地址等信息帖渠。第一個(gè)方法從調(diào)用開始到執(zhí)行完成谒亦,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

也就是說,java方法的調(diào)用,其實(shí)是一個(gè)個(gè)棧幀入棧出棧的過程,而棧幀內(nèi)部又包含了局部變量表,操作數(shù)棧等部分:

1.png

局部變量表和操作數(shù)棧是棧幀內(nèi)進(jìn)行執(zhí)行字節(jié)碼的重要部分.

局部變量表顧名思義,就是用來保存方法參數(shù)和方法內(nèi)部定義的局部變量的一段內(nèi)存區(qū)域.

而操作數(shù)棧也是一個(gè)后入先出的棧,程序運(yùn)行過程中各種字節(jié)碼指令往其中壓入和彈出棧進(jìn)行運(yùn)算的.

java字節(jié)碼分析

我們用一個(gè)簡(jiǎn)單的代碼做demo:

// Test.java
public class Test {                                                              
    public static void main(String[] args) {                                     
        int a = 12;                                                              
        int b = 21;                                                              
        int c = a + b;                                                           
        System.out.println(String.valueOf(c));                                   
    }                                                                            
}

首先使用javac命令編譯代碼,然后使用javap命令查看字節(jié)碼:

javac Test.java
javap Test

得到下面的輸出:

Compiled from "Test.java"
public class Test {
  public Test();
  public static void main(java.lang.String[]);
}

可以看到這里只有方法的聲明,并沒有具體的代碼執(zhí)行過程.這是因?yàn)閳?zhí)行過程都被編譯成一個(gè)個(gè)字節(jié)碼指令了.

我們可以用javap -c命令被這些指令也顯示出來:

javap -c Test

輸出為:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        12
       2: istore_1
       3: bipush        21
       5: istore_2
       6: iload_1
       7: iload_2
       8: iadd
       9: istore_3
      10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: iload_3
      14: invokestatic  #3                  // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      17: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: return
}

我們來一步步分析main方法里面的字節(jié)碼指令:

// 將12這個(gè)常量壓入操作數(shù)棧
0: bipush        12

// 彈出操作數(shù)棧頂?shù)脑?保存到局部變量表第1個(gè)位置中,即將12從棧頂彈出,保存成變量1,此時(shí)棧已空
2: istore_1

// 將21這個(gè)常量壓入操作數(shù)棧
3: bipush        21

// 彈出操作數(shù)棧頂?shù)脑?保存到局部變量表第2個(gè)位置中,即將21從棧頂彈出,保存成變量2,此時(shí)棧已空
5: istore_2

// 從局部變量表獲取第1個(gè)位置的元素,壓入操作數(shù)棧中,即將12壓入棧中
6: iload_1

// 從局部變量表獲取第2個(gè)位置的元素,壓入操作數(shù)棧中,即將21壓入棧中
7: iload_2

// 彈出操作數(shù)棧頂?shù)膬蓚€(gè)元素,進(jìn)行加法操作,得到的結(jié)果再壓入棧中,即彈出21和12相加得到33,再壓入棧中
8: iadd

// 彈出操作數(shù)棧頂?shù)脑?保存到局部變量表第3個(gè)位置中,即將33從棧頂彈出,保存成變量3,此時(shí)棧已空
9: istore_3

// 讀取System中的靜態(tài)成員變量out壓入棧中
10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

// 從局部變量表獲取第3個(gè)位置的元素,壓入操作數(shù)棧中,即將33壓入棧中
13: iload_3

// 彈出棧頂?shù)?3,執(zhí)行String.valueOf方法,并將得到的返回值"33"壓回棧中
14: invokestatic  #3                  // Method java/lang/String.valueOf:(I)Ljava/lang/String;

// 彈出棧頂?shù)?33"和System.out變量去執(zhí)行println方法
17: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

// 退出方法
20: return

上面的的流程比較復(fù)雜空郊,可以結(jié)合下面這個(gè)動(dòng)圖理解一下:

gif1.gif

如果看的比較仔細(xì)的同學(xué)可能會(huì)有疑問份招,為什么舉報(bào)變量表里一開始位置0就會(huì)有個(gè)String[]在那呢?

其實(shí)這個(gè)字符串?dāng)?shù)組就是傳入的參數(shù)args,jvm會(huì)把參數(shù)都?jí)喝缗e報(bào)變量表給方法去使用,如果調(diào)用的是非靜態(tài)方法,還會(huì)將該方法的調(diào)用對(duì)象也一起壓入棧中.

可能有同學(xué)一開始會(huì)對(duì)istore狞甚、iload...這些字節(jié)碼指令的作用不那么熟悉,或者有些指令不知道有什么作用锁摔。不過這個(gè)沒有關(guān)系,不需要死記硬背哼审,遇到的時(shí)候搜索一下就是了谐腰。

類型擦除的原理

泛型是java中十分好用且常用的技術(shù),之前也有寫過兩篇博客 《java泛型那些事》,《再談Java泛型》總結(jié)過.感興趣的同學(xué)可以去看看.

這里我們就從編譯出來的class文件里面看看泛型的實(shí)現(xiàn):

public class Test {                                                              
    public static void main(String[] args) {                                     
        foo(1);                                                                  
    }                                                                            

    public static <T> T foo(T a){                                                
        return a;                                                                
    }                                                                            
}

讓我們使用"javap -c"命令看看它生成的class文件是怎樣的:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: invokestatic  #3                  // Method foo:(Ljava/lang/Object;)Ljava/lang/Object;
       7: pop
       8: return

  public static <T> T foo(T);
    Code:
       0: aload_0
       1: areturn
}

可以看到雖然聲明部分還是可以看到泛型的影子:

public static <T> T foo(T);

但是在調(diào)用的時(shí)候?qū)嶋H上是

Method foo:(Ljava/lang/Object;)Ljava/lang/Object;

main 方法中先用iconst_1將常量1壓入棧中,然后用Integer.valueOf方法裝箱成Integer最后調(diào)用參數(shù)和返回值都是Object的foo方法.

所以說泛型的實(shí)現(xiàn)原理實(shí)際上是將類型都變成了Obejct,所以才能接收所有繼承Object的類型,但是像int,char這種不是繼承Object的類型是不能傳入的.

然后由于類型最后都被擦除剩下Object了,所以jvm是不知道原來輸入的類型的,于是乎下面的這種代碼就不能編譯通過了:

public <T> T foo(){
    return new T(); // 編譯失敗,因?yàn)門的類型最后會(huì)被擦除,變成Object
}

非靜態(tài)內(nèi)部類持有外部類的引用的原因

我們都知道非靜態(tài)內(nèi)部類是持有外部類的引用的,所以在安卓中使用Handler的話一般會(huì)聲明成靜態(tài)內(nèi)部類,然后加上弱引用去防止內(nèi)存泄露.

接下來就讓我們一起看看非靜態(tài)內(nèi)部類是怎么持有外部類的引用的。先寫一個(gè)簡(jiǎn)單的例子:

public class Test {
    public void foo() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(String.valueOf(Test.this));
            }
        };
    }
}

通過javac命令編譯之后發(fā)現(xiàn)得到了兩個(gè)class文件:

Test$1.class Test.class

Test.class文件好理解應(yīng)該就是Test這個(gè)類的定義,那Test$1.class定義的Test$1類又是從哪里來的呢涩盾?

這里還有個(gè)大家可能忽略的知識(shí)點(diǎn),java里面變量名類名是可以包含$符號(hào)的,例如下面的代碼都是合法且可以通過編譯并且正常運(yùn)行的

int x$y = 123;
System.out.println(x$y);

回到正題,讓我們先來用"javap -c"命令看看Test.class里面的內(nèi)容:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void foo();
    Code:
       0: new           #2                  // class Test$1
       3: dup
       4: aload_0
       5: invokespecial #3                  // Method Test$1."<init>":(LTest;)V
       8: astore_1
       9: return
}

我們來解析下foo方法:

// new一個(gè)Test$1類的對(duì)象,壓入棧中
0: new           #2                  // class Test$1

// 復(fù)制一份棧頂?shù)脑貕喝霔V?即現(xiàn)在棧里面有兩個(gè)相同的Test\$1對(duì)象
3: dup

// 將局部變量表位置為0的元素壓入棧中,由于foo方法不是靜態(tài)方法,所以這個(gè)元素實(shí)際上就是Test對(duì)象,即this
4: aload_0

// 調(diào)用Test$1(Test)這個(gè)構(gòu)造方法,它有一個(gè)Test類型的參數(shù),我們傳入的就是棧頂?shù)腡est對(duì)象,同時(shí)我們會(huì)將棧頂?shù)诙€(gè)元素Test$1對(duì)象也傳進(jìn)去(也就是說用這個(gè)Test$1對(duì)象去執(zhí)行構(gòu)造方法)十气。于是我們就彈出了棧頂?shù)囊粋€(gè)Test對(duì)象和一個(gè)Test$1對(duì)象
5: invokespecial #3                  // Method Test$1."<init>":(LTest;)V

// 將棧剩下的最后一個(gè)Test$1保存到局部變量表的位置1中。
8: astore_1

// 退出方法
9: return

根據(jù)上面的字節(jié)碼,我們可以逆向得到下面的代碼:

public class Test {
    public void foo() {
        Runnable r = new Test$1(this);
    }
}

接著我們?cè)賮砜纯碩est$1.class:

Compiled from "Test.java"
class Test$1 implements java.lang.Runnable {
  final Test this$0;

  Test$1(Test);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LTest;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void run();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #1                  // Field this$0:LTest;
       7: invokestatic  #4                  // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      13: return
}

這里定義了一個(gè)實(shí)現(xiàn)Runnable接口的Test$1類旁赊,它有一個(gè)參數(shù)為Test的構(gòu)造方法和一個(gè)run方法桦踊。然后還有一個(gè)Test類型的成員變量this$0。繼續(xù)解析這個(gè)兩個(gè)方法的字節(jié)碼:

  Test$1(Test);
    Code:
        // 將局部變量表中位置為0的元素壓入棧中,由于這個(gè)方法不是靜態(tài)的,所以這個(gè)元素就是Test$1的this對(duì)象
       0: aload_0
       
       // 將局部變量表位置為1的元素壓入棧中,這個(gè)元素就是我們傳入的參數(shù)Test對(duì)象
       1: aload_1
       
       // 這里彈出棧頂?shù)膬蓚€(gè)元素,第一個(gè)Test對(duì)象,賦值給第二元素Test$1對(duì)象的this$0成員變量终畅。也就是把我們傳進(jìn)來的Test對(duì)象保存給成員變量 this$0
       2: putfield      #1                  // Field this$0:LTest;
       
       // 將局部變量表中位置為0的元素壓入棧中,還是Test$1的this對(duì)象
       5: aload_0
       
       // 使用棧頂Test$1的this對(duì)象去初始化
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       
       // 退出方法
       9: return

  public void run();
    Code:
        //拿到System的靜態(tài)成員變量out壓入棧中
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       
       // 將局部變量表中位置為0的元素壓入棧中,由于這個(gè)方法不是靜態(tài)的,所以這個(gè)元素就是Test$1的this對(duì)象 
       3: aload_0
       
       // 彈出棧頂Test$1的this對(duì)象,獲取它的this$0成員變量,壓入棧中
       4: getfield      #1                  // Field this$0:LTest;
       
       // 彈出棧頂?shù)膖his$0對(duì)象執(zhí)行String.valueOf方法,得到的String對(duì)象壓入棧中
       7: invokestatic  #4                  // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
       
       // 彈出棧頂?shù)腟tring對(duì)象和System.out對(duì)象去執(zhí)行println方法,即調(diào)用System.out.println打印這個(gè)String對(duì)象
       10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      
       // 退出方法
       13: return

來來來,我們繼續(xù)腦補(bǔ)它的源代碼:

public class Test$1 implements java.lang.Runnable {
    final Test this$0;

    public Test$1(Test test) {
        this$0 = test;
    }

    @Override
    public void run() {
        System.out.println(String.valueOf(this$0));
    }
}

所以我們通過字節(jié)碼,發(fā)現(xiàn)下面這個(gè)代碼:

public class Test {
    public void foo() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(String.valueOf(Test.this));
            }
        };
    }
}

編譯之后最終會(huì)生成兩個(gè)類:

public class Test {
    public void foo() {
        Runnable r = new Test$1(this);
    }
}

public class Test$1 implements java.lang.Runnable {
    final Test this$0;

    public Test$1(Test test) {
        this$0 = test;
    }

    @Override
    public void run() {
        System.out.println(String.valueOf(this$0));
    }
}

這就是非靜態(tài)內(nèi)部類持有外部類的引用的原因啦籍胯。

到這里這篇文章想講的東西就已經(jīng)都講完了,還剩下一個(gè)問題就當(dāng)做作業(yè)讓同學(xué)們自己嘗試這去分析吧:

需要將自由變量聲明成final才能給匿名內(nèi)部類訪問

最后編輯于
?著作權(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)離奇詭異理朋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)绿聘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門嗽上,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熄攘,你說我怎么就攤上這事∨不” “怎么了浅萧?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)哲思。 經(jīng)常有香客問我洼畅,道長(zhǎng),這世上最難降的妖魔是什么棚赔? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任帝簇,我火速辦了婚禮,結(jié)果婚禮上忆嗜,老公的妹妹穿的比我還像新娘己儒。我一直安慰自己,他們只是感情好捆毫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布闪湾。 她就那樣靜靜地躺著,像睡著了一般绩卤。 火紅的嫁衣襯著肌膚如雪途样。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天濒憋,我揣著相機(jī)與錄音何暇,去河邊找鬼。 笑死凛驮,一個(gè)胖子當(dāng)著我的面吹牛裆站,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播黔夭,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宏胯,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了本姥?” 一聲冷哼從身側(cè)響起肩袍,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婚惫,沒想到半個(gè)月后氛赐,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一篙悯、第九天 我趴在偏房一處隱蔽的房頂上張望铃绒。 院中可真熱鬧,春花似錦颠悬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峡谊。三九已至刊苍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間班缰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工埠忘, 沒想到剛下飛機(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