Java類(lèi)加載器(ClassLoader)機(jī)制詳解


大部分人平時(shí)不會(huì)直接接觸到ClassLoader,但ClassLoader作為Java的一個(gè)重要的核心特性卻又和平時(shí)的編碼工作息息相關(guān),了解ClassLoader的機(jī)制有助于我們更好的了解Java的工作機(jī)制,同時(shí)對(duì)于學(xué)習(xí)OSGI敌卓,Web服務(wù)器等工作原理也有幫助

ClassLoader定義

無(wú)論是寫(xiě)一個(gè)簡(jiǎn)單的單文件程序俐填,還是一個(gè)復(fù)雜的多模塊程序终佛,其大致都可分為下列幾步:

  1. 代碼人員將設(shè)計(jì)邏輯轉(zhuǎn)換為Java語(yǔ)言邏輯并生成.java文件
  2. Java編譯器將.java文件編譯為Java字節(jié)代碼(.class文件)
  3. ClassLoader加載.class文件并轉(zhuǎn)換成java.lang.Class類(lèi)的一個(gè)實(shí)例放入緩存俊嗽,每個(gè)這樣的實(shí)例用來(lái)表示一個(gè) Java 類(lèi)。后續(xù)通過(guò)此實(shí)例的 newInstance()方法就可以創(chuàng)建出該類(lèi)的對(duì)象

所以ClassLoader的主要作用就是加載.class文件以供運(yùn)行時(shí)使用

ClassLoader分類(lèi)

在Java中铃彰,ClassLoader可大致分為兩類(lèi)绍豁,第一類(lèi)為系統(tǒng)提供的,另外一類(lèi)是由開(kāi)發(fā)人員自行擴(kuò)展的牙捉,其中系統(tǒng)提供的ClassLoader大致有三種竹揍,它們分別為:

  • 引導(dǎo)類(lèi)加載器(Bootstrap ClassLoader);它用來(lái)加載 Java 的核心庫(kù)邪铲,如:rt.jar芬位、resources.jar等
  • 擴(kuò)展類(lèi)加載器(Extension ClassLoader);負(fù)責(zé)加載Java的擴(kuò)展類(lèi)庫(kù)带到,默認(rèn)加載JAVA_HOME/jre/lib/ext/目錄下的所有jar
  • 應(yīng)用類(lèi)加載器(App ClassLoader)昧碉;負(fù)責(zé)加載應(yīng)用程序classpath目錄下的所有jar和class文件

在這三種系統(tǒng)提供的ClassLoader中,引導(dǎo)類(lèi)加載器較為特殊阴孟,這一點(diǎn)在后續(xù)會(huì)提到;而由開(kāi)發(fā)人員自行擴(kuò)展的ClassLoader則需繼承java.lang.ClassLoader類(lèi)并根據(jù)需要重寫(xiě)特定方法税迷,一般重寫(xiě)findClass方法即可

ClassLoader工作機(jī)制

相信即便是不了解ClassLoader工作機(jī)制的人永丝,也聽(tīng)說(shuō)過(guò)雙親委派機(jī)制,雙親委派機(jī)制就是對(duì)ClassLoader的工作機(jī)制描述箭养,除了引導(dǎo)類(lèi)加載器之外慕嚷,所有的類(lèi)加載器都有一個(gè)父類(lèi)加載器(可以通過(guò) getParent()
方法可以查看,該父類(lèi)加載器與當(dāng)前類(lèi)加載器不是繼承關(guān)系毕泌,是關(guān)聯(lián)關(guān)系)喝检,如應(yīng)用類(lèi)加載器的父類(lèi)加載器是擴(kuò)展類(lèi)加載器,而擴(kuò)展類(lèi)加載器的父類(lèi)加載器是引導(dǎo)類(lèi)加載器

ClassLoader loader = ClassLoaderStructure.class.getClassLoader();//獲得加載當(dāng)前類(lèi)的類(lèi)加載器
while(loader != null) {
    System.out.println(loader);
    loader = loader.getParent();//獲得父類(lèi)加載器的引用
}
System.out.println(loader);

//運(yùn)行結(jié)果
sun.misc.Launcher$AppClassLoader@232204a1 //應(yīng)用類(lèi)加載器
sun.misc.Launcher$ExtClassLoader@14ae5a5 //擴(kuò)展類(lèi)加載器
null //引導(dǎo)類(lèi)加載器撼泛,由于應(yīng)到類(lèi)加載器不繼承與 java.lang.ClassLoader挠说,由原生代碼實(shí)現(xiàn),所以這里顯示是null

對(duì)于開(kāi)發(fā)人員編寫(xiě)的類(lèi)加載器來(lái)說(shuō)愿题,其父類(lèi)加載器是加載此類(lèi)加載器 Java 類(lèi)的類(lèi)加載器损俭。因?yàn)轭?lèi)加載器 Java 類(lèi)如同其它的 Java 類(lèi)一樣,也是要由類(lèi)加載器來(lái)加載的潘酗。一般來(lái)說(shuō)杆兵,開(kāi)發(fā)人員編寫(xiě)的類(lèi)加載器的父類(lèi)加載器是應(yīng)用類(lèi)加載器。類(lèi)加載器通過(guò)這種方式組織起來(lái)仔夺,形成樹(shù)狀結(jié)構(gòu)琐脏。樹(shù)的根節(jié)點(diǎn)就是引導(dǎo)類(lèi)加載器

C_S.png

當(dāng)一個(gè)ClassLoader實(shí)例需要加載某個(gè)類(lèi)時(shí),它會(huì)首先檢查這個(gè)類(lèi)是否已經(jīng)加載,這個(gè)過(guò)程是由下至上依次檢查日裙,若所有加載器均未加載吹艇,則先從頂層加載器開(kāi)始試圖加載,若加載失敗阅签,則把任務(wù)轉(zhuǎn)交給擴(kuò)展類(lèi)加載器進(jìn)行加載掐暮,如果也沒(méi)加載到,則轉(zhuǎn)交給應(yīng)用類(lèi)加載器進(jìn)行加載政钟,如果它依然沒(méi)有加載到的話(huà)路克,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡(luò)等URL中加載該類(lèi)养交,這個(gè)過(guò)程是由上至下的精算。如果它們都沒(méi)有加載到這個(gè)類(lèi)時(shí),則拋出ClassNotFoundException異常碎连。否則將這個(gè)找到的類(lèi)生成一個(gè)類(lèi)的定義灰羽,并將它加載到內(nèi)存當(dāng)中,最后返回這個(gè)類(lèi)在內(nèi)存中的Class實(shí)例對(duì)象鱼辙,這就是雙親委派的工作流程了

load.png

那為什么需要使用這種流程進(jìn)行類(lèi)的加載呢廉嚼?首先來(lái)看下面實(shí)例:

//待加載類(lèi)
public class Biz {
    private Biz instance;

    public void setInstance(Object instance) {
        this.instance = (Biz)instance; //類(lèi)型轉(zhuǎn)換
        System.out.println("instance inited");
    }
}

//自行實(shí)現(xiàn)的類(lèi)加載器
public class FileSystemClassLoader extends ClassLoader{
    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }
}

//調(diào)用代碼
public class Client {
    public static void main(String[] args) {
        String classDataRootPath = "D:\\temp"; //Biz.class放置于該目錄下
        FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
        FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
        String className = "classloader.whydelegation.Biz";
        try {
            Class<?> class1 = fscl1.loadClass(className);
            System.out.println("class1 ClassLoader is " + class1.getClassLoader());
            Object obj1 = class1.newInstance();
            Class<?> class2 = fscl2.loadClass(className);
            System.out.println("class2 ClassLoader is " + class2.getClassLoader());
            Object obj2 = class2.newInstance();
            class1.getMethod("setInstance", java.lang.Object.class).invoke(obj1, obj2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//運(yùn)行結(jié)果
class1 ClassLoader is classloader.whydelegation.FileSystemClassLoader@7f31245a
java.lang.reflect.InvocationTargetException
class2 ClassLoader is classloader.whydelegation.FileSystemClassLoader@135fbaa4
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at classloader.whydelegation.Client.main(Client.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassCastException: classloader.whydelegation.Biz cannot be cast to classloader.whydelegation.Biz
    at classloader.whydelegation.Biz.setInstance(Biz.java:10)
    ... 10 more

這段代碼示例通過(guò)兩個(gè)不同的類(lèi)加載器加載同一個(gè).class文件,最后將生成的實(shí)例進(jìn)行類(lèi)型轉(zhuǎn)換(Biz#setInstance中)倒戏,但報(bào)ClassCastException怠噪,原因就在于即便是同一個(gè).class文件被不同的類(lèi)加載器加載,最終得到的也是兩個(gè)不同的類(lèi)的示例杜跷,因?yàn)镴VM在判定兩個(gè)class是否相同時(shí)傍念,不僅要判斷兩個(gè)類(lèi)名是否相同,而且要判斷是否由同一個(gè)類(lèi)加載器加載的葛闷。只有兩者同時(shí)滿(mǎn)足的情況下憋槐,JVM才認(rèn)為這兩個(gè)class是相同的

再回到雙親委派機(jī)制, 它能保證公用的類(lèi)特別是Java核心類(lèi)庫(kù)只會(huì)被加載一次淑趾,保證Java 應(yīng)用所使用的都是同一個(gè)版本的 Java 核心庫(kù)的類(lèi)阳仔,如在加載一個(gè)類(lèi)的時(shí)候,會(huì)首先去其父級(jí)加載器查找該類(lèi)是否已經(jīng)加載過(guò)扣泊,若加載過(guò)驳概,則不會(huì)再次加載,同時(shí)保證該由父級(jí)加載器加載的類(lèi)由父級(jí)加載旷赖,而不會(huì)出現(xiàn)自行實(shí)現(xiàn)的類(lèi)加載器去加載核心類(lèi)庫(kù)的情況顺又,試想如果沒(méi)有雙親委派機(jī)制,那么對(duì)于java.lang.Object這種通用類(lèi)等孵,就會(huì)存在多個(gè)版本稚照,且互不兼容

定義自己的ClassLoader

因?yàn)镴ava中提供的默認(rèn)ClassLoader,只加載指定目錄下的jar和class,如果我們想加載其它位置的類(lèi)或jar時(shí)果录,比如:我要加載網(wǎng)絡(luò)上的一個(gè)class文件上枕,通過(guò)動(dòng)態(tài)加載到內(nèi)存之后,要調(diào)用這個(gè)類(lèi)中的方法實(shí)現(xiàn)我的業(yè)務(wù)邏輯弱恒。在這樣的情況下辨萍,默認(rèn)的ClassLoader就不能滿(mǎn)足我們的需求了,所以需要定義自己的ClassLoader返弹。定義自已的類(lèi)加載器分為兩步:

  • 繼承java.lang.ClassLoader
  • 重寫(xiě)父類(lèi)的findClass方法

有人可能有疑問(wèn)锈玉,ClassLoader類(lèi)有那么多方法,為什么偏偏只重寫(xiě)findClass方法义起?因?yàn)镴DK已經(jīng)在loadClass方法中幫我們實(shí)現(xiàn)了ClassLoader搜索類(lèi)的算法拉背,當(dāng)在loadClass方法中搜索不到類(lèi)時(shí),loadClass方法就會(huì)調(diào)用findClass方法來(lái)搜索類(lèi)默终,所以我們只需重寫(xiě)該方法即可椅棺。如沒(méi)有特殊的要求,一般不建議重寫(xiě)loadClass搜索類(lèi)的算法齐蔽;具體代碼示例見(jiàn)本文ClassLoader工作機(jī)制章節(jié)FileSystemClassLoader 類(lèi)的實(shí)現(xiàn)

其他

對(duì)于運(yùn)行在 Java EE?容器中的 Web 應(yīng)用來(lái)說(shuō)两疚,類(lèi)加載器的實(shí)現(xiàn)方式與一般的 Java 應(yīng)用有所不同。不同的 Web 容器的實(shí)現(xiàn)方式也會(huì)有所不同含滴。以 Apache Tomcat 來(lái)說(shuō)诱渤,每個(gè) Web 應(yīng)用都有一個(gè)對(duì)應(yīng)的類(lèi)加載器實(shí)例。該類(lèi)加載器也使用代理模式蛙吏,所不同的是它是自己首先嘗試去加載某個(gè)類(lèi)源哩,如果找不到再代理給父類(lèi)加載器鞋吉。這與一般類(lèi)加載器的順序是相反的鸦做。這是 Java Servlet 規(guī)范中的推薦做法,其目的是使得 Web 應(yīng)用自己的類(lèi)的優(yōu)先級(jí)高于 Web 容器提供的類(lèi)谓着。這種代理模式的一個(gè)例外是:Java 核心庫(kù)的類(lèi)是不在查找范圍之內(nèi)的泼诱。這也是為了保證 Java 核心庫(kù)的類(lèi)型安全

OSGi?是 Java 上的動(dòng)態(tài)模塊系統(tǒng)。它為開(kāi)發(fā)人員提供了面向服務(wù)和基于組件的運(yùn)行環(huán)境赊锚,并提供標(biāo)準(zhǔn)的方式用來(lái)管理軟件的生命周期治筒。OSGi 已經(jīng)被實(shí)現(xiàn)和部署在很多產(chǎn)品上,在開(kāi)源社區(qū)也得到了廣泛的支持舷蒲。Eclipse 就是基于 OSGi 技術(shù)來(lái)構(gòu)建的耸袜。OSGi 中的每個(gè)模塊(bundle)都包含 Java 包和類(lèi)。模塊可以聲明它所依賴(lài)的需要導(dǎo)入(import)的其它模塊的 Java 包和類(lèi)(通過(guò) Import-Package)牲平,也可以聲明導(dǎo)出(export)自己的包和類(lèi)堤框,供其它模塊使用(通過(guò) Export-Package)。也就是說(shuō)需要能夠隱藏和共享一個(gè)模塊中的某些 Java 包和類(lèi)。這是通過(guò) OSGi 特有的類(lèi)加載器機(jī)制來(lái)實(shí)現(xiàn)的蜈抓。OSGi 中的每個(gè)模塊都有對(duì)應(yīng)的一個(gè)類(lèi)加載器启绰。它負(fù)責(zé)加載模塊自己包含的 Java 包和類(lèi)。當(dāng)它需要加載 Java 核心庫(kù)的類(lèi)時(shí)(以 java開(kāi)頭的包和類(lèi))沟使,它會(huì)代理給父類(lèi)加載器(通常是啟動(dòng)類(lèi)加載器)來(lái)完成委可。當(dāng)它需要加載所導(dǎo)入的 Java 類(lèi)時(shí),它會(huì)代理給導(dǎo)出此 Java 類(lèi)的模塊來(lái)完成加載腊嗡。模塊也可以顯式的聲明某些 Java 包和類(lèi)着倾,必須由父類(lèi)加載器來(lái)加載

線(xiàn)程上下文類(lèi)加載器(context ClassLoader)是從 JDK 1.2 開(kāi)始引入的。類(lèi)java.lang.Thread中的方法 getContextClassLoader()setContextClassLoader(ClassLoader cl)用來(lái)獲取和設(shè)置線(xiàn)程的上下文類(lèi)加載器叽唱。如果沒(méi)有通過(guò) setContextClassLoader(ClassLoader cl)方法進(jìn)行設(shè)置的話(huà)屈呕,線(xiàn)程將繼承其父線(xiàn)程的上下文類(lèi)加載器。Java 應(yīng)用運(yùn)行的初始線(xiàn)程的上下文類(lèi)加載器是系統(tǒng)類(lèi)加載器棺亭。在線(xiàn)程中運(yùn)行的代碼可以通過(guò)此類(lèi)加載器來(lái)加載類(lèi)和資源虎眨。前面提到的類(lèi)加載器的代理模式并不能解決 Java 應(yīng)用開(kāi)發(fā)中會(huì)遇到的類(lèi)加載器的全部問(wèn)題。Java 提供了很多服務(wù)提供者接口(Service Provider Interface镶摘,SPI)嗽桩,允許第三方為這些接口提供實(shí)現(xiàn)。常見(jiàn)的 SPI 有 JDBC凄敢、JCE碌冶、JNDI、JAXP 和 JBI 等涝缝。這些 SPI 的接口由 Java 核心庫(kù)來(lái)提供扑庞,如 JAXP 的 SPI 接口定義包含在javax.xml.parsers包中。這些 SPI 的實(shí)現(xiàn)代碼很可能是作為 Java 應(yīng)用所依賴(lài)的 jar 包被包含進(jìn)來(lái)拒逮,可以通過(guò)類(lèi)路徑(CLASSPATH)來(lái)找到罐氨,如實(shí)現(xiàn)了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代碼經(jīng)常需要加載具體的實(shí)現(xiàn)類(lèi)滩援。如 JAXP 中的javax.xml.parsers.DocumentBuilderFactory類(lèi)中的 newInstance()方法用來(lái)生成一個(gè)新的 DocumentBuilderFactory的實(shí)例栅隐。這里的實(shí)例的真正的類(lèi)是繼承 自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實(shí)現(xiàn)所提供的玩徊。如在 Apache Xerces 中租悄,實(shí)現(xiàn)的類(lèi)是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而問(wèn)題在于恩袱,SPI 的接口是 Java 核心庫(kù)的一部分泣棋,是由引導(dǎo)類(lèi)加載器來(lái)加載的;SPI 實(shí)現(xiàn)的 Java 類(lèi)一般是由系統(tǒng)類(lèi)加載器來(lái)加載的畔塔。引導(dǎo)類(lèi)加載器是無(wú)法找到 SPI 的實(shí)現(xiàn)類(lèi)的潭辈,因?yàn)樗患虞d Java 的核心庫(kù)纪吮。它也不能代理給系統(tǒng)類(lèi)加載器,因?yàn)樗窍到y(tǒng)類(lèi)加載器的祖先類(lèi)加載器萎胰。也就是說(shuō)碾盟,類(lèi)加載器的代理模式無(wú)法解決這個(gè)問(wèn)題。線(xiàn)程上下文類(lèi)加載器正好解決了這個(gè)問(wèn)題技竟。如果不做任何的設(shè)置冰肴,Java 應(yīng)用的線(xiàn)程的上下文類(lèi)加載器默認(rèn)就是系統(tǒng)上下文類(lèi)加載器。在 SPI 接口的代碼中使用線(xiàn)程上下文類(lèi)加載器榔组,就可以成功的加載到 SPI 實(shí)現(xiàn)的類(lèi)熙尉。線(xiàn)程上下文類(lèi)加載器在很多 SPI 的實(shí)現(xiàn)中都會(huì)用到。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搓扯,一起剝皮案震驚了整個(gè)濱河市检痰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锨推,老刑警劉巖铅歼,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異换可,居然都是意外死亡椎椰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)沾鳄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慨飘,“玉大人,你說(shuō)我怎么就攤上這事译荞∪康模” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵吞歼,是天一觀的道長(zhǎng)圈膏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)浆熔,這世上最難降的妖魔是什么本辐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任桥帆,我火速辦了婚禮医增,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘老虫。我一直安慰自己叶骨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布祈匙。 她就那樣靜靜地躺著忽刽,像睡著了一般天揖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跪帝,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天今膊,我揣著相機(jī)與錄音,去河邊找鬼伞剑。 笑死斑唬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的黎泣。 我是一名探鬼主播恕刘,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抒倚!你這毒婦竟也來(lái)了褐着?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤托呕,失蹤者是張志新(化名)和其女友劉穎含蓉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體项郊,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谴餐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呆抑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岂嗓。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鹊碍,靈堂內(nèi)的尸體忽然破棺而出厌殉,到底是詐尸還是另有隱情,我是刑警寧澤侈咕,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布公罕,位于F島的核電站,受9級(jí)特大地震影響耀销,放射性物質(zhì)發(fā)生泄漏楼眷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一熊尉、第九天 我趴在偏房一處隱蔽的房頂上張望罐柳。 院中可真熱鬧,春花似錦狰住、人聲如沸张吉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肮蛹。三九已至勺择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伦忠,已是汗流浹背省核。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昆码,地道東北人芳撒。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像未桥,于是被迫代替她去往敵國(guó)和親笔刹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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