Java泛型

一、基础

1.1 什么是泛型

  • 泛型是 JDK5 的一个新特性,是将类型明确的事情延后到创建对象或调用方法时,再去明确的特殊的类型;

  • 泛型的本质是参数化类型,它提供了编译时类型安全监测机制,通过这个机制,我们可以在编译时检查类型安全,泛型所有的强制转换都是自动、隐式的,只要编译时没有问题,运行时就不会出现 ClassCastException(类型转换异常),极大地提高代码的可读性、复用性及安全性;

  • 泛型可以用在类、接口和方法的创建中,被称为泛型类、泛型接口、泛型方法。

1.2 泛型的作用
  • 没有泛型前,我们通过对类型 Object 的引用,来实现参数的“任意化”。缺点是:要做显式的强制类型转换,开发者需要对实际参数类型在可以预知的情况下进行。

  • 引入泛型后,我们可以在使用时,再确定具体的类型。在调用方法时,也不用强转对象、写很多重复方法了。

1.3 泛型的优点
  • 代码可复用:一套代码可支持不同的类型;
  • 可读性更高:不用强制转换,代码更加简洁;
  • 程序更安全:在编译时就检查类型安全,如在编译时没有警告,运行时就不会出现 ClassCastException (类型转换异常),降低 crash 率;
  • 稳定性更强:在创建集合时,就限定了集合元素的类型。因此,在取出元素时,不需要强制类型转换了。
1.4 泛型的使用场景
  • 使用在不想写多个重载函数的场景;
  • 使用在用户希望返回他自定义类型的返回值场景。例如:Json 返回 Java bean ;
  • 在使用反射的应用中,也经常使用泛型。例如:Class<T> ;
  • 使用在约束对象类型的场景,用来定义边界(T extends ...)。例如:JDK 集合 List,Set ;
  • 使用在网页、资源分析或返回的场景。
1.5 泛型的设计原则

编译时没有出现警告,运行时就不会出现 ClassCastException 异常。

1.6 泛型的实现原理

Java 中的泛型,基本上都是在编译器这个层次来实现的。在生成的 Java 字节码里面,没有包含泛型中的类型信息。我们在使用泛型的时候添加的类型参数,将在编译时被擦除掉。这也是 Java 的泛型也被称作为“伪泛型”的原因。

1.6.1 类型擦除(Type Erasure)

测试代码:

public class GenericType {
    public static void main(String[] args) {  
      ArrayList<String> arrayString=new ArrayList<String>();
      ArrayList<Integer> arrayInteger=new ArrayList<Integer>();

      System.out.println(arrayString.getClass());
      System.out.println(arrayString.getClass()==arrayInteger.getClass());
    }  
}

输出:

class java.util.ArrayList

true

注意:是类型相同,而不是对象相同。

在这个示例中,我们定义了两个 ArrayList 数组:

数组一:ArrayList<String> 泛型类型,只可以存储字符串;
数组二:ArrayList<Integer> 泛型类型,只可以存储整型。
最后,我们通过 ArrayString 对象、和 ArrayInteger 对象的 GetClass 方法,获取它们的类信息并比较,发现结果为 true

这是为什么呢,明明我们定义了两种不同的类型呀?这是因为:
在编译期,所有的泛型信息都会被擦除,List<Integer> 和 List<String> 类型在编译后,都会变成 List 类型(原始类型)。

1.7 泛型的特性

Java 中的泛型,只在编译的时候有效。

  • 我们在编译时,先检验泛型结果,再将泛型信息擦除掉,最后在对象进入和离开方法的边界处,添加上类型检查和类型转换的方法。
  • 在编译后,程序会采取去泛型化的措施,即泛型信息不会进入到运行时阶段。
1.8 泛型的规则限制
  • 不可以使用泛型地形参创建对象;
  • 在泛型类中,不可以给静态成员变量定义泛型;
  • 泛型类不可以继承 java.lang.Throwable 类;
  • Java 泛型不可以使用基本类型;
  • 泛型类不可以初始化一个数组;
  • Java 泛型不可以进行实例化;
  • Java 泛型不可以直接进行类型转换;
  • Java 泛型不可以直接使用 instanceof 运算符,进行运行时类型检查;
  • Java 泛型不可以创建确切类型的泛型数组;
  • Java 泛型不可以定义泛型异常类、或 catch 异常;
  • Java 泛型不可以作为参数进行重载。
1.9 泛型常用术语
1
1.10 泛型的类型表示
2

二、为什么要使用泛型

1、在 JDK5 之前,集合中没有泛型,我们是通过继承来实现泛型的程序设计的,使用继承实现有两个弊端

  • 取值时,需要强制类型转换,反之,得到的都是 Object。
  • 编译时,不会检查错误。

2、在 JDK5 之后,集合中有了泛型,我们就可以使用泛型来实现了,泛型提供了编译时类型安全检测机制,该机制:

  • 允许我们在编译时检查类型安全,且所有的强制转换都是自动和隐式的,只要编译时不出现问题,运行时就不会出现ClassCastException(类型转换异常)。
  • 可以极大地提高代码的可读性、复用性及安全性。
2.1 实例

电商系统中,有普通用户、商户用户两种类型。当用户获取信息详情时,系统要先将其中的敏感信息设置为“空”,再返回给用户。此时,我们需要写一个通用方法,将敏感字段设置为“空”。具体怎么实现呢?

我们可能想到的三种实现方式:继承、方法重载机制、泛型机制

2.1.1 第一种实现方式:继承

在 Java 中,所有的类都继承了 Object ,我们可以将 Object 作为传入参数,再使用反射去操作字段,设置 password 为空。

public class Client {
    public static void main(String[] args) {
        // 初始化
        ShopUser shopUser = new ShopUser(0L, "shopUser", "123456");
        ClientUser clientUser = new ClientUser(0L, "clientUser", "123456");

        // 输出原始信息
        System.out.println("过滤前:");
        System.out.println("          " + shopUser);
        System.out.println("          " + clientUser);

        // 执行过滤
        shopUser = (ShopUser) removeField(shopUser);
        clientUser = (ClientUser) removeField(clientUser);

        // 输出过滤后信息
        System.out.println("过滤后:");
        System.out.println("          " + shopUser);
        System.out.println("          " + clientUser);
    }

    public static Object removeField(Object obj) throws Exception {
        Set<String> fieldSet = new HashSet();
        fieldSet.add("password");

        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (fieldSet.contains(field.getName())) {
                field.setAccessible(true);
                field.set(obj, null);
                return obj;
            }
        }
        return obj;
    }
}

运行结果

过滤前:
      ShopUser{id=0, username='shopUser', password='123456'}
      ClientUser{id=0, username='clientUser', password='123456'}
过滤后:
      ShopUser{id=null, username='shopUser', password='null'}
      ClientUser{id=null, username='clientUser', password='null'}

通过继承实现,运行结果没问题,但要强制转换对象,代码不够简洁。

2.1.2 第二种实现方式:方法重载机制

Java 不是有方法重载机制吗,我们再来试试这个方法。

业务方法:

public static ShopUser removeField(ShopUser user) throws Exception {

     // 强转,并返回对象
     return (ShopUser) remove(user);
 }
 public static ClientUser removeField(ClientUser user) throws Exception {

     // 强转,并返回对象
     return (ClientUser) remove(user);
 }

核心方法:

// 把敏感字段设置为空
    public static Object remove(Object obj) throws Exception {
        // 需要过滤的敏感字段
        Set<String> fieldSet = new HashSet<String>();
        fieldSet.add("password");

        // 获取所有字段:然后获取这个类所有字段
        Field[] fields = obj.getClass().getDeclaredFields();

        // 敏感字段设置为空
        for (Field field : fields) {
            if (fieldSet.contains(field.getName())) {
                // 开放字段操作权限
                field.setAccessible(true);
                // 设置空
                field.set(obj, null);
            }
        }

        // 返回对象
        return obj;
    }
}

通过方法重载机制实现,重复方法会很多,我们每添加一个供应商用户,就要再写一个方法、修改源码,简直太繁琐了,还极容易出错。

2.1.3 第三种实现方式:泛型
public static <T> T removeField(T obj) throws Exception {
    Set<String> fieldSet = new HashSet();
    fieldSet.add("password");

    Field[] fields = obj.getClass().getDeclaredFields();
    for (Field field : fields) {
        if (fieldSet.contains(field.getName())) {
            field.setAccessible(true);
            field.set(obj, null);
            return obj;
        }
    }
    return obj;
}

通过泛型实现,我们就不用把参数的类型代码写死,并且可以在使用的时候,再去确定具体的类型;在调用方法的时候,也不用强转对象、写很多重复方法,代码更加简洁、安全。

三、哪些情况下需要使用泛型

当接口、类及方法中操作的引用数据类型不确定时,就可以用泛型来表示,这样能避免强转,将运行问题转移到编译期。

需要注意的是:

  • 泛型实际代表什么类型,取决于调用者传入的类型,如果没传,默认是 Object 类型。
  • 使用带泛型的类创建对象时,等式两边指定的泛型类型必须一致。原因是:编译器检查对象调用方法时只看变量;而程序在运行期间调用方法时,就要考虑对象具体类型了。
  • 等式两边,可以在任意一边使用泛型,在另一边不使用(考虑向后兼容)。

四、泛型类、泛型接口、泛型方法

4.1 泛型类
4.1.1 泛型类的定义

泛型类,是在实例化类时指明泛型的具体类型,是拥有泛型特性的类,本质还是一个 Java 类,可以被继承。泛型类是最常见的泛型使用方式,最常见的运用就是各种集合类和接口,例如List、ArrayList 等。

4.1.2 泛型类的格式

格式:public class 类名<数据类型,…> { }
示例:public class Generic<T>{ }

关于数据类型:

  • 可以用任意字母来代表,例如 T ,或者 T,E,K,V等形式的参数。
  • 也可以用多个英文字母,如果用多个英文字母,需要用逗号隔开,例如 public class Generic<T,K,V>{ }
4.1.3 泛型类的使用
  • 定义一个类,在该类名后面添加类型参数声明部分,由尖括号分隔。
  • 每一个类型参数声明部分,可以包括一个或多个类型参数,参数间用逗号隔开。
  • 使用 <T> 来声明一个类型持有者名称,将 T 当作一个类型来声明成员、参数、返回值类型(此处T可以写为任意标识)。
4.1.4 泛型类的示例代码
public class GenericsBox<T> {
    private T t;

    public void add(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}
4.1.5 泛型类的使用注意事项
  • 泛型的类型参数(包括自定义类),只能是类类型(String、Integer),不能是简单类型(int,double)。
  • 不能对确切的泛型类型使用 instanceof 操作,会导致编译时出错。
  • 使用泛型时,如果传入泛型实参,会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用;如果不传入泛型类型实参,在泛型类中,使用泛型的方法或成员变量定义的类型,可以是任何的类型。
  • 若泛型类型已确定,则只能是其本身,其子类不能使用。
4.2 泛型接口
4.2.1 泛型接口的定义

将泛型用在接口 interface 上,被称为泛型接口,泛型接口经常被用在各种类的生产器中。

4.2.2 泛型接口的格式

格式:修饰符 interface 接口名 <类型>{ }
示例:public interface ShangHaiJieKou<T> { }

泛型接口格式,类似于泛型类的格式;接口中的方法的格式,类似于泛型方法的格式。

以下有两个类,和一个接口,我们以接口类为例:

1. Genericimpl(接口实现类)

2. Generic_泛型接口<T>(interface接口类)

3. GenericDemo_泛型接口(main方法实现类)
4.2.3 泛型接口的使用
  • 定义一个接口,在该接口名后面,添加类型参数声明部分,用尖括号分隔。
  • 每一个类型参数声明部分,可以包括一个或多个类型参数,参数间用逗号隔开。
4.2.4 泛型接口的示例代码
public interface GenericsInterface<T> {

    public abstract void genericsInterface1(T element);

    public abstract <T> void genericsInterface2();
}
4.2.5 泛型接口的使用注意事项

实现泛型的类,必须传入泛型实参,然后在实现类中用该实参来替换 T 。

4.3 泛型方法
4.3.1 泛型方法的定义

泛型方法,是在调用方法时指明泛型的具体类型,既可用在类和接口上,还可用在方法上。

4.3.2 泛型方法的格式
  • 格式:修饰符 <类型> 返回值类型方法名(类型变量名){}
  • 示例:public <T> void show(T t){}
4.3.3 泛型方法的使用
  • 所有泛型方法声明,都有一个类型参数声明部分,由尖括号分隔。该类型参数的声明部分,在方法返回值类型之前。
  • 每一个类型参数声明部分,都包括一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数可被用来声明返回值类型,作为泛型方法得到的实际参数类型的占位符。
4.3.4 泛型方法的示例代码

打印各种类型的数组中的元素

public static <T> void printLog(T[] inputArray) {
    for (T element : inputArray) {
         LogUtil.e("打印数组中的元素", element + "");
    }
}
    /**
     * 精确匹配
     * 类比SQL:select * from indexName where field = 'keyword'
     *
     * @param clazz
     * @param indexName
     * @param field
     * @param keyword
     * @return
     */
    public <T> List<T> termSearch(Class<T> clazz, String indexName, String field, String keyword) {
        List<T> resultList = Lists.newArrayList();
        SearchRequest request = new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(field, keyword);
        searchSourceBuilder.query(termQueryBuilder);
        request.source(searchSourceBuilder);
        log.info("DSL={}", searchSourceBuilder.toString());

        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (SearchHit hit : response.getHits()) {
            String resultString = hit.getSourceAsString();
            resultList.add(JSONObject.parseObject(resultString, clazz));
        }
        return resultList;
    }
4.3.5 编程原则

泛型方法是为了调高代码的重用性和程序安全性,尽量设计泛型方法去解决问题,如果设计泛型方法,可以取代泛型整个类,就选择泛型方法。另外,泛型方法可以搞定的,就不建议使用泛型类了。

五、泛型类的继承

泛型类被继承的情况有两种:子类明确泛型类的类型参数变量、子类不明确泛型类的类型参数变量。

5.1 子类明确泛型类的类型参数变量

把泛型定义在接口上

public interface Inter<T> {
    public abstract void show(T t);
}

定义一个子类来实现接口

public class InterImpl implements Inter<String> {//子类明确泛型类的类型参数变量为String:
    @Override
    public void show(String s) {
        System.out.println(s);

    }
}

测试代码

public class mingquefanxingcanshu {
    public static void main(String[] args) {
        Inter<String> i = new InterImpl();
        i.show("hello java!");
    }
}
5.2 子类不明确泛型类的类型参数变量

把泛型定义在接口上

public interface Inter<T> {
    public abstract void show(T t);
}

定义一个子类来实现接口

public class InterImpl<T> implements Inter<T> {//实现类不明确泛型类的类型参数变量,实现类也要定义出<T>类型的
    @Override
    public void show(T s) {//子类方法也要继承类的数据类型T
        System.out.println(s);

    }
}

测试代码

public class mingquefanxingcanshu {
    public static void main(String[] args) {
        Inter<String> i = new InterImpl();
        i.show("hello java!");
    }
}
5.3 泛型类的继承注意事项
  • 实现类要的是重写父类的方法,返回值的类型需要同父类一样。
  • 类上声明的泛形,只对非静态成员有效。

六 、泛型通配符

6.1 泛型通配符的基本概述

泛型通配符用 ? 表示,代表不确定的类型,是泛型的一个重要组成。

相关术语及范例:

  • ArrayList<E>,称为泛型类型,此处的 E ,称为类型参数变量。
  • ArrayList<Integer>,称为参数化的类型 ParameterizedType ,此处的Integer,称为实际类型参数。
6.2 为什么要用泛型通配符

泛型通配符的引入,是为了解决类型被限制死之后,不能动态根据实例来确定的缺点。先来看代码示例,我们现在有这样一个函数

public void test(List<Number> data) {

}

根据泛型规则,这个函数只能传进来 List<Number> 这一种类型, List<Object> 和 List<Integer> 是传不进来的。如果我们既要泛型,又想把这两个类型的子类、或者父类的泛型传进去,就需要使用到通配符泛型。

6.3 泛型通配符的优点

可以在保证运行时类型安全的基础上,提高参数类型的灵活性。

6.4 泛型通配符的作用

泛型本身不支持协变和逆变,通配符能解决协变和逆变的问题。

在 Java 中:

  • 数组可以协变。例如:Dog extends Animal , Animal[] 与 Dog[] 是可以兼容的。
  • 集合不可以协变。List<Animal> 不是 List<Dog> 的父类,为了建立两个集合之间的联系,此时需要引用通配符来解决。

泛型 T 是确定的类型,而通配符更加灵活(不确定),更多用于扩充参数的范围。泛型 T就像是变量,将传来的一个具体的类型拿来使用,而通配符则是一种规定,规定你能传哪些参数,就像是一个特殊的实际类型参数。

6.5 泛型通配符的使用场景
  • 用于类型参数中。
  • 用于实例变量,或者局部变量中。
  • 有时也可作为返回类型,例如Object的getClass方法。

注意:泛型通配符只能用于泛型类的使用(声明变量、方法的参数),不能用于泛型定义、New 泛型实例。

6.6 泛型通配符的三种形式
  • <?> :无边界通配符(基本使用)
  • <? extends T> :固定上边界通配符(又称有上限的通配符)
  • <? super T> :固定下边界通配符(又称有下限的通配符)
6.6.1 无边界通配符 <?>

无边界通配符的概述:

  • 英文全称 Unbounded Wildcards ,代表任意类型,是万能通配符 ;
  • 采用 <?> 的语法形式,来声明使用该类通配符。例如: List<?> ,表示元素类型未知的 List ,它的元素可以匹配任何的类型。这种带通配符的 List ,仅表示它是各种泛型 List 的父类,并不能把元素添加到其中。

无边界通配符的作用:可以让泛型能够接受未知类型的数据。

6.6.2 固定上边界通配符 <? extends T>

固定上边界通配符的概述:

  • 英文全称 Upper Bounded Wildcards 。
  • 采用 <? extends 类型> 的语法形式,来声明使用该类通配符。例如:List<?extends Number> ,表示 Number 或者其子类型。需要注意的是,此处虽然使用了 extends 关键字,但却不仅限于继承了父类 类型 的子类,也可以代指实现了接口 类型 的类。
  • 代表类型变量的范围有限,只能传入某种类型、或者它的子类,适合频繁往外读取数据的场景。

固定上边界通配符的作用:
当我们不希望 List<?> 是任何泛型 List 的父类,只希望它代表某一类泛型 List 的父类时,我们就使用固定上边界通配符的泛型,来接受指定类、以及其子类类型的数据。

6.6.3 固定下边界通配符 <? super T>

固定下边界通配符的概述:

  • 英文全称 Lower Bounded Wildcards。
  • 采用 <? super 类型> 的语法形式 ,来声明使用该类通配符。例如: List<?super Number> ,它表示的类型是 Number 或者其父类型。
  • 代表类型变量的范围有限,只能传入某种类型、或者其父类,适合频繁插入数据的场景。

固定下边界通配符的作用:
我们可以使用固定下边界通配符的泛型,来接受指定类及其父类类型的数据。需要重点注意的是,一个泛型可以单独指定类型通配符的上边界、或下边界,但不能同时指定上边界、下边界。

6.7 泛型通配符的使用

我们确定类型变量时,如果不能明确类型变量,就可以使用泛型通配符(泛型通配符代表不确定的类型)。反之,如果我们能明确地知道类型变量,就不需要使用泛型通配符。

6.7.1 代码示例1 :不需要使用泛型通配符

我们明确地知道 count() 方法用在哪儿,在编码时,可以直接指明这是一个 Integer 集合。

public static Integer count(List<Integer> list) {
    int total = 0;
    for (Integer number : list) {
        total += number;
    }
    list.add(total);
    return total;
}

在调用方法时,如果不传指定的数据进来,编译会报错:

public static void main(String[] args) {
    // 不传指定数据,编译报错
    List<String> strList = Arrays.asList("0", "1", "2");
    int totalNum = count(strList);
}

即便我们绕过了编译,程序也很可能无法正常运行:

public static void main(String[] args) {
    // 绕过了编译,运行报错
    List strList1 = Arrays.asList("0", "1", "2");
    int totalNum = count(strList1);
}

所以,如果你已经明确自己要做什么,清楚地知道类型变量,就没必要使用泛型通配符。

6.7.2 代码示例2 :使用泛型通配符,实现一些通用算法

但是,在一些通用方法中,什么类型的数据都能传进来,不能确认类型变量,这时候该怎么办呢?此时,我们就可以使用泛型通配符,在不用确认类型变量的情况下,来实现一些通用算法。例如:我们需要写一个通用方法,把传入的 List 集合输出到控制台。

public static void print(List<?> list) {
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

Integer 集合,可以运行:

public static void main(String[] args) {
    //Integer 集合,可以运行
    List<Integer> intList = Arrays.asList(0, 1, 2);
    print(intList);
}

String 集合,可以运行:

public static void main(String[] args) {
    //String 集合,可以运行
    List<String> strList = Arrays.asList("0", "1", "2");
    print(strList);
}

List<?> list 指不确定 List 集合装的是什么类型,有可能是 Integer、或 String、或其他。但这不用理会,我们只需要传一个 List 集合进来,这个方法就能正常运行了。这就是泛型通配符的作用所在。

我们还可以使用泛型通配符,来实现一些特殊算法。泛型通配符也适用于一些特殊算法,但可使用的范围不大。例如:用户分为普通用户、商家用户,但用户有一些特殊功能,其它角色都没有。这时候,又该怎么办呢?我们可以通过给泛型通配符设定边界,来限定类型变量的范围。

6.8 PECS原则

泛型通配符的用法有些复杂,为了更好的帮助我们理解、及正确使用 Java 泛型通配符。Joshua Bloch 在《Effective Java 》第 3 版中提出了 PECS 原则。PECS 的英文全称是 Producer Extends , Consumer Super ,字面意思是“读取时使用 extends ,写入时使用 super ”。

也就是说:

  • 参数化类型表示一个生产者,我们就使用 <? extends T> ;
  • 参数化类型表示一个消费者,我们就使用 <? super T> 。
6.9 通配符与类型参数的区别

一般而言,通配符能干的事情都可以用类型参数替换。例如:

public void testWildCards(Collection<?> collection){}

可以用以下来取代:

public <T> void test(Collection<T> collection){}

值得注意的是,如果用泛型方法来取代通配符,上面代码中的 collection 是能进行写操作的,但要进行强制转换。

public <T> void test(Collection<T> collection){
    collection.add((T)new Integer(12));
    collection.add((T)"123");
}

需要特别注意的是,类型参数适用于参数之间的类别依赖关系,举例说明。

public class Test2 <T,E extends T>{
    T value1;
    E value2;
}

如果一个方法的返回类型依赖于参数的类型,那么通配符也无能为力。

public T test1(T t){
    return value1;
}
6.10 泛型通配符和泛型方法的使用选型

在实际应用场景中,有很多时候,我们可以使用泛型方法、来替代泛型通配符。使用通配符:

public static void test(List<?> list) {

}

使用泛型方法:

public <T> void test2(List<T> t) {

}

既然通配符和泛型方法都可用,具体该怎样选型呢?
通配符和泛型方法的选型参考:

  • 参数之间的类型有依赖关系的,又或者返回值与参数之间有依赖关系的,使用泛型方法。
  • 参数之间的类型没有依赖关系的,使用通配符。

七、Java 泛型中的 T,E,K,V

本质上都是通配符,只是一种编码约定俗成。我们可以将 T 换成 A-Z 之间的任意字母,不会影响程序的正常运行。但是,如果换成其他的字母代替 T ,可读性可能会差一些。通常情况下,T,E,K,V,?是这样约定的:

3

资料来源:
Java泛型基础最全详解,超级详细

为什么要使用泛型,什么时候使用泛型?

泛型的3种使用方式:泛型类、泛型接口、泛型方法

泛型通配符超详解,一文彻底搞懂

一线互联网面试真题,最全最新整理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容