Java 泛型詳解

[參考資料]http://www.reibang.com/p/31b44188b973

??最近項目組在進行泛型代碼編寫時遇到很多困難幸缕,討論下來發(fā)現(xiàn)大家對這個概念都是一知片解,然而在我們的項目開發(fā)過程中,又會有大量需要用到泛型來簡化代碼、增加復用性的場景奖年。因此闲勺,決定用一次share來增強大家對Java泛型的理解旬蟋,提升項目組的代碼質(zhì)量。

1.什么是泛型

??Java在1.5之后加入了泛型的概念敬肚。泛型毕荐,即“參數(shù)化類型”。泛型的本質(zhì)是為了參數(shù)化類型(在不創(chuàng)建新的類型的情況下艳馒,通過泛型指定的不同類型來控制形參具體限制的類型)憎亚。也就是說在泛型使用過程中,操作的數(shù)據(jù)類型被指定為一個參數(shù)弄慰,這種參數(shù)類型可以用在類第美、接口和方法中,分別被稱為泛型類陆爽、泛型接口什往、泛型方法。

舉個例子

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size(); i++){
    String item = (String)arrayList.get(i);
    System.out.println("泛型測試慌闭,item = " + item);
}

毫無疑問别威,程序運行會以崩潰結(jié)束:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Test.main(GenericTest.java:17)

??ArrayList可以存放任意類型,例子中添加了一個String類型驴剔,添加了一個Integer類型省古,再使用時都以String的方式使用,因此程序崩潰了丧失。為了解決類似這樣的問題(在編譯階段就可以解決)豺妓,泛型應運而生。
??將之前第一行聲明list的代碼修改一下,編譯器就會在編譯階段幫我們提前發(fā)現(xiàn)類似的問題琳拭。

List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在編譯階段载佳,編譯器就會報錯

??如上面所說泛型只在代碼編譯階段有效,來看下面的代碼:

List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
     System.out.println("泛型測試,類型相同");
}

??我們發(fā)現(xiàn)對于編譯器來說臀栈,stringArrayList和integerArrayList其實是同一類型的對象蔫慧。這是因為代碼在編譯之后采取了類似于去泛型化的措施,也就是泛型的類型擦除权薯,這個概念后面會介紹姑躲。

2.Java為什么要設計泛型

??在Java SE 1.5之前,沒有泛型的情況的下盟蚣,通過對類型Object的引用來實現(xiàn)參數(shù)的“任意化”黍析,“任意化”帶來的缺點是要做顯式的強制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對實際參數(shù)類型可以預知的情況下進行的屎开。對于強制類型轉(zhuǎn)換錯誤的情況 阐枣,編譯器可能不提示錯誤,在運行的時候才出現(xiàn)異常奄抽,這是一個安全隱患蔼两。
??泛型的好處:使用泛型,首先可以通過IDE進行代碼類型初步檢查逞度,然后在編譯階段進行編譯類型檢查额划,以保證類型轉(zhuǎn)換的安全性;并且所有的強制轉(zhuǎn)換都是自動和隱式的档泽,可以提高代碼的重用率俊戳。

3.泛型基礎

Java泛型有三種使用方式:泛型類、泛型方法馆匿、泛型接口抑胎。
泛型類
泛型類的語法如下:

class 類名稱 <泛型類型標識>{}

我們首先定義一個簡單的類:

public class Generic{
    private String object;
    public void set(String object) {
        this.object = object;
    }
    public String get() {
        return object;
    }
}

??這是一個常見的Java bean,這樣做的一個壞處是Box里面現(xiàn)在只能裝入String類型的元素渐北,今后如果我們需要裝入Integer等其他類型的元素阿逃,還必須要另外重寫一個類型是Integer的Box類,代碼得不到復用腔稀。
然而通過泛型類可以很好的解決復用的問題:

public class Generic<T> {
    private T t;
    public void set(T t) {
        this.t = t; 
    }
    public T get() { 
        return t;
    }
}

這樣的Box類就可以裝入任何我們想要的類型:

Generic<Integer> integerGeneric = new Generic<Integer>();
Generic<Double> doubleGeneric = new Generic<Double>();
Generic<String> stringGeneric = new Generic<String>();

泛型方法
泛型方法的語法如下:

[作用域修飾符] <泛型類型標識> [返回類型] 方法名稱(參數(shù)列表){}

按照這個語法聲明一個泛型方法很簡單盆昙,只要在返回類型前面加上一個類似<K, V,...>的形式就行了:

public class Util {
    public static <K, V> boolean compare(Generic<K, V> g1, Generic<K, V> g2) {
        return g1.getKey().equals(g2.getKey()) &&
               g1.getValue().equals(g2.getValue());
    }
}
public class Generic<K, V> {
    private K key;
    private V value;
    public Generic(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

Util.compare()就是一個泛型方法羽历,于是我們可以像下面這樣調(diào)用泛型:

Generic<Integer, String> g1 = new Generic<>(1, "apple");
Generic<Integer, String> g2 = new Generic<>(2, "pear");
boolean same = Util.<Integer, String>compare(g1, g2);

??在Java1.7之后焊虏,編譯器可以通過type inference(類型推導),根據(jù)實參的類型自動推導出相應參數(shù)的類型秕磷,可以縮寫成這樣:

Generic<Integer, String> p1 = new Generic<>(1, "apple");
Generic<Integer, String> p2 = new Generic<>(2, "pear");
boolean same = Util.compare(p1, p2);

泛型接口
??泛型接口的定義與泛型類的定義很相似诵闭。

public interface Generator<T> {
    public T next();
}

??當實現(xiàn)泛型接口的類,未給泛型傳入實參時:

class FruitGenerator<T> implements Generator<T>{
    public T next() {
        return null;
    }
}

??未傳入泛型實參時,與泛型類的定義相同疏尿,在聲明類的時候瘟芝,需將泛型的聲明也一起加到類中。即:class FruitGenerator<T> implements Generator<T>褥琐。我們可以為T傳入任意一種實參锌俱,形成無數(shù)種類型的Generator接口。
Generator<T>如果不聲明泛型敌呈,如:class FruitGenerator implements Generator<T>贸宏,編譯器會報錯:"Unknown class"。

當實現(xiàn)泛型接口的類磕洪,給泛型傳入了實參時:

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

??如果類已經(jīng)將泛型類型傳入實參類型吭练,則所有使用泛型的地方都要替換成傳入的實參類型。即:Generator<T>析显,public T next();中的的T都要替換成傳入的String類型鲫咽。

4.通配符

??介紹通配符之前,我們先思考一個場景谷异。
??我們知道Ingeter是Number的一個子類分尸,由于泛型擦除的存在,對于編譯器來說Generic<Ingeter>與Generic<Number>實際上是同一種基本類型歹嘹。那么問題來了寓落,在使用Generic<Number>作為形參的方法中,能否使用Generic<Ingeter>的實例傳入呢荞下?在邏輯上類似于Generic<Number>和Generic<Ingeter>是否可以看成是父子關(guān)系呢伶选?
為了弄清楚這個問題,我們定義一個方法:

public void showKeyValue(Generic<Number> obj){
    System.out.println("泛型測試,value is " + obj.get());
}

如果我們像下面這樣使用該方法:

Generic<Number> gNumber = new Generic<Number>(456);
Generic<Integer> gInteger = new Generic<Integer>(123);

gNumber.showKeyValue(gInteger);

當我們調(diào)用該方法時尖昏,編譯器會提示我們:

Generic<java.lang.Integer> cannot be applied to Generic<java.lang.Number>
showKeyValue(gInteger);

??通過提示信息我們可以看到Generic<Integer>不能被看作為Generic<Number>的子類仰税。由此可以看出:同一種泛型可以對應多個版本(因為參數(shù)類型是不確定的),而不同版本的泛型類實例之間是不兼容的抽诉。
??由此會產(chǎn)生一個問題陨簇,如果我們想對Generic<Integer>類型使用showKeyValue方法,我們就必須重新定義一個新的方法迹淌,這顯然與Java的多態(tài)理念相違背河绽。因此我們需要一個在邏輯上可以表示同時是Generic<Integer>和Generic<Number>父類的引用類型,通配符應運而生唉窃。
我們可以將上面的方法改一下:

public void showKeyValue(Generic<?> obj){
    System.out.println("泛型測試,value is " + obj.get());
}

此時耙饰,showKeyValue方法可以傳入任意類型的Generic參數(shù),這是一個無界的通配符纹份。

泛型上下邊界
在Java泛型定義時:
??用<T>等大寫字母標識泛型類型苟跪,用于表示未知類型廷痘。
??用<T extends ClassA & InterfaceB …>等標識有界泛型,用于表示有邊界的類型件已。
??在Java泛型實例化時:
??用<?>標識通配符笋额,用于表示實例化時的類型。
??用<? extends 父類型>標識上邊界通配符篷扩,用于表示實例化時可以確定父類型的類型兄猩。
??用<? super 子類型>標識下邊界通配符,用于表示實例化時可以確定子類型的類型鉴未。
對上面的Generic類增加一個新方法:

public void showKeyValue1(Generic<? extends Number> obj){
    System.out.println("泛型測試,value is " + obj.get());
}
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);

//這一行代碼編譯器會提示錯誤厦滤,因為String類型并不是Number類型的子類
showKeyValue1(generic1);

showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);

如果把泛型類的定義也改一下:

public class Generic<T extends Number>{
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}
//這一行代碼也會報錯,因為String不是Number的子類
Generic<String> generic1 = new Generic<String>("11111");

泛型的上下邊界添加歼狼,必須與泛型的聲明在一起 掏导。

PECS原則
首先我們先定義幾個簡單的類,下面我們將用到它:

class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}

然后定義一個主類:

public class Generics {
    public static void main(String[] args) {
        // 通過通配符申明一個List
        List<? extends Fruit> flist = new ArrayList<Apple>();
        // Compile Error: can't add any type of object:
        // flist.add(new Apple())
        // flist.add(new Orange())
        // flist.add(new Fruit())
        // flist.add(new Object())
        flist.add(null); // Legal but uninteresting
        // We Know that it returns at least Fruit:
        Fruit f = flist.get(0);
    }
}

對于上面的flist羽峰,Java編譯器不允許我們add任何對象趟咆,為什么呢?對于這個問題我們不妨從編譯器的角度去考慮梅屉。因為List<? extends Fruit> flist它自身可以有多種含義:

List<? extends Fruit> flist = new ArrayList<Fruit>();
List<? extends Fruit> flist = new ArrayList<Apple>();
List<? extends Fruit> flist = new ArrayList<Orange>();

當我們嘗試add一個Apple的時候值纱,flist可能指向new ArrayList<Orange>();

當我們嘗試add一個Orange的時候,flist可能指向new ArrayList<Apple>();

當我們嘗試add一個Fruit的時候坯汤,這個Fruit可以是任何類型的Fruit虐唠,而flist可能只想某種特定類型的Fruit,編譯器無法識別所以會報錯惰聂。

所以對于實現(xiàn)了<? extends T>的集合類只能將它視為Producer向外提供(get)元素疆偿,而不能作為Consumer來對外獲取(add)元素。
如果我們要add元素應該怎么做呢?可以使用<? super T>:

public class GenericWriting {
    static List<Apple> apples = new ArrayList<Apple>();
    static List<Fruit> fruit = new ArrayList<Fruit>();
    static <T> void writeExact(List<T> list, T item) {
        list.add(item);
    }
    static void f1() {
        writeExact(apples, new Apple());
        writeExact(fruit, new Apple());
    }
    static <T> void writeWithWildcard(List<? super T> list, T item) {
        list.add(item);
    }
    static void f2() {
        writeWithWildcard(apples, new Apple());
        writeWithWildcard(fruit, new Apple());
    }
    public static void main(String[] args) {
        f1(); f2();
    }
}

這樣我們可以往容器里面添加元素了,但是使用super后不能從容器里面get元素了舱沧,從編譯器的角度考慮這個問題,對于List<? super Apple> list处铛,它可以有下面幾種含義:

List<? super Apple> list = new ArrayList<Apple>();
List<? super Apple> list = new ArrayList<Fruit>();
List<? super Apple> list = new ArrayList<Object>();

當我們嘗試通過list來get一個Apple的時候,可能會get得到一個Fruit拐揭,這個Fruit可以是Orange等其他類型的Fruit撤蟆。

所以對于實現(xiàn)了<? super T>的集合類只能將它視為Consumer消費(add)元素,而不能作為Producer來對外獲取(get)元素堂污。
在Java的集合類中家肯,我們可以發(fā)現(xiàn)通常會將兩者結(jié)合起來一起用,比如像下面這樣:

public class Collections {
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i=0; i<src.size(); i++)
            dest.set(i, src.get(i));
    }
}

5.類型擦除

類型擦除就是說Java泛型只能用于在編譯期間的靜態(tài)類型檢查敷鸦,然后編譯器生成的代碼會擦除相應的類型信息息楔,這樣到了運行期間實際上JVM根本就知道泛型所代表的具體類型。這樣做的目的是因為Java泛型是1.5之后才被引入的扒披,為了保持向下的兼容性值依,所以只能做類型擦除來兼容以前的非泛型代碼。
泛型擦除到底是什么碟案,來看一個簡單的例子:

public class Node<T> {
    private T data;
    private Node<T> next;
    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }
    public T getData() { return data; }
    // ...
}
編譯器做完相應的類型檢查之后愿险,實際上到了運行期間上面這段代碼實際上將轉(zhuǎn)換成:

public class Node {
    private Object data;
    private Node next;
    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }
    public Object getData() { return data; }
    // ...
}

這意味著不管我們聲明Node<String>還是Node<Integer>,到了運行期間价说,JVM統(tǒng)統(tǒng)視為Node<Object>辆亏。有沒有什么辦法可以解決這個問題呢?這就需要我們自己重新設置bounds了鳖目,將上面的代碼修改成下面這樣:

public class Node<T extends Comparable<T>> {
    private T data;
    private Node<T> next;
    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }
    public T getData() { return data; }
    // ...
}

這樣編譯器就會將T出現(xiàn)的地方替換成Comparable而不再是默認的Object了:

public class Node {
    private Comparable data;
    private Node next;
    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }
    public Comparable getData() { return data; }
    // ...
}

6.泛型使用的幾個限制

Java泛型由于類型擦除的存在扮叨,會存在一些使用限制:

1.Java泛型不能使用基本類型

使用基本類型的泛型會編譯報錯,代碼如下:

List<int> list = new List<int>();// 編譯前類型檢查報錯

2.Java泛型不允許進行直接實例化

錯誤代碼如下:

<T> void test(T t){
    t = new T();//編譯前類型檢查報錯
}

通過類型擦除领迈,上面的泛型方法會轉(zhuǎn)換為如下的原始方法:

void test(Object t){
    t = new Object();
}

實例化的兩種方法:

  1. 通過集合來保存泛型對應的實例
public class DbHelper {
    private static final DbHelper instance;

    static {
        instance = new DbHelper();
    }

    private DbHelper() {
    }

    private Map<Class<?>, ChangedListener> changedListeners = new HashMap<>();

    public <Model extends BaseModel> ChangedListener getListener(Class<Model> modelClass) {
        if (changedListeners.containsKey(modelClass)) {
            return changedListeners.get(modelClass);
        }
        return null;
    }

    public <Model extends BaseModel> void addChangedListener(final Class<Model> tClass,
                                                             ChangedListener<Model> listener) {
        ChangedListener changedListener = getListener(tClass);
        // 添加到中的Map
        changedListeners.put(tClass, changedListener);
    }

    public interface ChangedListener<Data extends BaseModel> {
        void onDataSave(Data... list);

        void onDataDelete(Data... list);
    }
}
  1. 通過反射來實例化泛型類型
public class GenericInstance {
    public static <T> T createModelInstance(Class<T> tClass) {
        try {
            // 獲取直接父類的類型Type
            Type superClass = tClass.getGenericSuperclass();
            // 調(diào)用getActualTypeArguments()方法獲得實際綁定的類型
            Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
            Class<?> clazz = getRawType(type);
            return (T) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // type不能直接實例化對象彻磁,通過type獲取class的類型,然后實例化對象
    private static Class<?> getRawType(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class) rawType;
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        } else if (type instanceof TypeVariable) {
            return Object.class;
        } else if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <"
                    + type + "> is of type " + className);
        }
    }
}

為了防止此類類型轉(zhuǎn)換錯誤的發(fā)生狸捅,Java禁止進行泛型實例化衷蜓。

3.Java泛型不允許進行靜態(tài)化
參考下面的代碼:

class StaticGeneric<T>{
        private static T t;// 編譯前類型檢查報錯

        public static T getT() {// 編譯前類型檢查報錯
            return t;
        }
    }

靜態(tài)變量在類中共享,而泛型類型是不確定的尘喝,因此編譯器無法確定要使用的類型磁浇,所以不允許進行靜態(tài)化。

4.Java泛型不允許直接進行類型轉(zhuǎn)換(通配符可以)

List<Integer> integerList = new ArrayList<Integer>();
List<Double> doubleList = new ArrayList<Double>();
//不能直接進行類型轉(zhuǎn)換朽褪,類型檢查報錯
integerList = doubleList;

雖然在編譯期間integerList與doubleList都會經(jīng)過類型擦除置吓,但是這種類型轉(zhuǎn)換違反了Java泛型降低類型轉(zhuǎn)換安全隱患的設計初衷。當integerList中存儲Interge元素缔赠,而doubleList中存儲Double元素時交洗,如果不限制類型轉(zhuǎn)換,很容易產(chǎn)生ClassCastException異常橡淑。
但是通配符有可以實現(xiàn):

<!--List<Integer> integerList = new ArrayList<Integer>();-->
<!--List<Double> doubleList = new ArrayList<Double>();-->
<!--//通過通配符進行類型轉(zhuǎn)換-->
<!--doubleList = integerList;-->
static void cast(List<?> orgin, List<?> dest){
    dest = orgin;
}

5.Java泛型不允許直接使用instanceof運算符進行運行時類型檢查(通配符可以)
直接使用instanceof運算符進行運行時類型檢查:

List<String> stringList = new ArrayList<String >();
//不能直接使用instanceof构拳,類型檢查報錯
System.out.println(stringList instanceof ArrayList<Double>);

因為Java編譯器在生成代碼的時候會擦除所有相關(guān)泛型的類型信息,正如我們上面驗證過的JVM在運行時期無法識別出ArrayList<Integer>和ArrayList<String>的之間的區(qū)別梁棠。
而我們可以通過通配符的方式進行instanceof運行期檢查:

// 這個時候的類型檢查沒有意義
System.out.println(stringList instanceof ArrayList<?>);

6.Java泛型不允許創(chuàng)建確切類型的泛型數(shù)組(通配符可以)
創(chuàng)建整型泛型數(shù)組如下:

//類型檢查錯誤
List<Integer>[] list = new ArrayList<Integer>[2];

可以通過通配符創(chuàng)建:

Generic<?>[] generics = new Generic<?>[2];
generics[0] = new Generic<Integer>(123);
generics[1] = new Generic<String>("hello");
for (Generic<?> generic : generics) {
    System.out.println(generic.get());
}

結(jié)果會正常打印出123和"hello"置森。

7.Java泛型不允許作為參數(shù)進行重載

例如:

public class GenericTest<T>{
    void test(List<Integer> list){}
    //不允許作為參數(shù)列表進行重載
    void test(List<Double> list){}
}

原因是:類型擦除后兩個方法是一樣的參數(shù)列表,無法重載符糊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凫海,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子男娄,更是在濱河造成了極大的恐慌行贪,老刑警劉巖漾稀,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異建瘫,居然都是意外死亡崭捍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門啰脚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殷蛇,“玉大人,你說我怎么就攤上這事橄浓×C危” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵荸实,是天一觀的道長匀们。 經(jīng)常有香客問我,道長准给,這世上最難降的妖魔是什么昼蛀? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮圆存,結(jié)果婚禮上叼旋,老公的妹妹穿的比我還像新娘。我一直安慰自己沦辙,他們只是感情好夫植,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著油讯,像睡著了一般详民。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陌兑,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天沈跨,我揣著相機與錄音,去河邊找鬼兔综。 笑死饿凛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的软驰。 我是一名探鬼主播涧窒,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锭亏!你這毒婦竟也來了纠吴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤慧瘤,失蹤者是張志新(化名)和其女友劉穎戴已,沒想到半個月后固该,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡糖儡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年伐坏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片休玩。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡著淆,死狀恐怖劫狠,靈堂內(nèi)的尸體忽然破棺而出拴疤,到底是詐尸還是另有隱情,我是刑警寧澤独泞,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布呐矾,位于F島的核電站,受9級特大地震影響懦砂,放射性物質(zhì)發(fā)生泄漏蜒犯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一荞膘、第九天 我趴在偏房一處隱蔽的房頂上張望罚随。 院中可真熱鬧,春花似錦羽资、人聲如沸淘菩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潮改。三九已至,卻和暖如春腹暖,著一層夾襖步出監(jiān)牢的瞬間汇在,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工脏答, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留糕殉,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓殖告,卻偏偏與公主長得像糙麦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丛肮,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361