秒懂 Java注解類型(@Annotation)

版權(quán)申明】非商業(yè)目的附文章鏈接可自由轉(zhuǎn)載
博文地址:http://www.reibang.com/p/9e72ca22fc50
出自:shusheng007

概述

照例先啰嗦幾句挖腰,剛開始接觸Java的時(shí)候,某一天發(fā)現(xiàn)調(diào)用的一個(gè)方法被劃了一個(gè)刪除橫線,查看這個(gè)方法的源代碼的時(shí)候發(fā)現(xiàn)除了上面有一句@Deprecated代碼外,和其他方法沒有區(qū)別,所以我斷定就是這貨起的作用克懊,當(dāng)時(shí)覺得好神奇,于是乎我開始了對Java注解的了解,這個(gè)過程是不連續(xù)的吊圾,最近比較閑,所以總結(jié)一下翰蠢。

理解Java注解

注解就相當(dāng)于對源代碼打的標(biāo)簽项乒,給代碼打上標(biāo)簽和刪除標(biāo)簽對源代碼沒有任何影響。有的人要說了梁沧,你盡幾把瞎扯檀何,沒有影響,打這些標(biāo)簽干毛線呢廷支?其實(shí)不是這些標(biāo)簽自己起了什么作用频鉴,而且外部工具通過訪問這些標(biāo)簽,然后根據(jù)不同的標(biāo)簽做出了相應(yīng)的處理恋拍。這是注解的精髓垛孔,理解了這一點(diǎn)一切就變得不再那么神秘。
例如我們寫代碼用的IDE(例如 IntelliJ Idea),它檢查發(fā)現(xiàn)某一個(gè)方法上面有@Deprecated這個(gè)注解芝囤,它就會(huì)在所有調(diào)用這個(gè)方法的地方將這個(gè)方法標(biāo)記為刪除似炎。

訪問和處理Annotation的工具統(tǒng)稱為APT(Annotation Processing Tool)

基本語法

注解可以分為以下3類

基本注解

Java內(nèi)置的注解共有5個(gè)

@Override:讓編譯器檢查被標(biāo)記的方法,保證其重寫了父類的某一個(gè)方法悯姊。此注解只能標(biāo)記方法羡藐。源碼如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated:標(biāo)記某些程序元素已經(jīng)過時(shí),程序員請不要再使用了悯许。源碼如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnings :告訴編譯器不要給老子顯示警告仆嗦,老子不想看,老子清楚的知道自己在干什么先壕。源碼如下:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}           

其內(nèi)部有一個(gè)String數(shù)組瘩扼,根據(jù)傳入的值來取消相應(yīng)的警告:
deprecation:使用了不贊成使用的類或方法時(shí)的警告谆甜;
unchecked:執(zhí)行了未檢查的轉(zhuǎn)換時(shí)的警告,例如當(dāng)使用集合時(shí)沒有用泛型 (Generics) 來指定集合保存的類型;
fallthrough:當(dāng) Switch 程序塊直接通往下一種情況而沒有 Break 時(shí)的警告;
path:在類路徑集绰、源文件路徑等中有不存在的路徑時(shí)的警告;
serial:當(dāng)在可序列化的類上缺少 serialVersionUID 定義時(shí)的警告;
finally:任何 finally 子句不能正常完成時(shí)的警告;
all:關(guān)于以上所有情況的警告规辱。

@SafeVarargs(Java7 新增)@SuppressWarnings可以用在各種需要取消警告的地方,而 @SafeVarargs主要用在取消參數(shù)的警告栽燕。就是說編譯器如果檢查到你對方法參數(shù)的操作罕袋,有可能發(fā)生問題時(shí)會(huì)給出警告,但是你很自(任)性碍岔,老子不要警告浴讯,于是你就加上了這個(gè)標(biāo)簽。源碼如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

其實(shí)這個(gè)注解是專為取消堆污染警告設(shè)置的蔼啦,因?yàn)镴ava7會(huì)對可能產(chǎn)生堆污染的代碼提出警告榆纽,什么是堆污染?且看下面代碼

 @SafeVarargs
 private static void method(List<String>... strLists) {
     List[] array = strLists;
     List<Integer> tmpList = Arrays.asList(42);
     array[0] = tmpList; //非法操作捏肢,但是沒有警告
     String s = strLists[0].get(0); //ClassCastException at runtime!
 }

如果不使用 @SafeVarargs奈籽,這個(gè)方法在編譯時(shí)候是會(huì)產(chǎn)生警告的 : “...使用了未經(jīng)檢查或不安全的操作⊥液眨”,用了就不會(huì)有警告唠摹,但是在運(yùn)行時(shí)會(huì)拋異常。

@FunctionalInterface(Java8 新增): 標(biāo)記型注解奉瘤,告訴編譯器檢查被標(biāo)注的接口是否是一個(gè)函數(shù)接口勾拉,即檢查這個(gè)接口是否只包含一個(gè)抽象方法,只有函數(shù)接口才可以使用Lambda表達(dá)式創(chuàng)建實(shí)例盗温。源碼如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

元注解

用來給其他注解打標(biāo)簽的注解藕赞,即用來注解其他注解的注解。元注解共有6個(gè)卖局。從上面的基本注解的源代碼中就會(huì)看到使用了元注解來注解自己斧蜕。

@Retention:用于指定被此元注解標(biāo)注的注解的保留時(shí)長,源代碼如下:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        RetentionPolicy value();
    }

從源代碼中可以看出砚偶,其有一個(gè)屬性value,返回一個(gè)枚舉RetentionPolicy 類型批销,有3種類型:

  • RetentionPolicy.SOURCE: :注解信息只保留在源代碼中,編譯器編譯源碼時(shí)會(huì)將其直接丟棄染坯。
  • RetentionPolicy.CLASS::注解信息保留在class文件中均芽,但是虛擬機(jī)VM不會(huì)維護(hù)默認(rèn)值。
  • RetentionPolicy.RUNTIME::注解信息保留在class文件中单鹿,而且VM也會(huì)持有此注解信息掀宋,所以可以通過反射的方式獲得注解信息。

@Target:用于指定被此元注解標(biāo)注的注解可以標(biāo)注的程序元素,源碼如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

從源碼中可以看出劲妙,其有一個(gè)屬性value,返回一個(gè)枚舉ElementType類型的數(shù)組湃鹊,這個(gè)數(shù)組的值就代表了可以使用的程序元素。

public enum ElementType {
   /**標(biāo)明該注解可以用于類镣奋、接口(包括注解類型)或enum聲明*/
   TYPE,

   /** 標(biāo)明該注解可以用于字段(域)聲明币呵,包括enum實(shí)例 */
   FIELD,

   /** 標(biāo)明該注解可以用于方法聲明 */
   METHOD,

   /** 標(biāo)明該注解可以用于參數(shù)聲明 */
   PARAMETER,

   /** 標(biāo)明注解可以用于構(gòu)造函數(shù)聲明 */
   CONSTRUCTOR,

   /** 標(biāo)明注解可以用于局部變量聲明 */
   LOCAL_VARIABLE,

   /** 標(biāo)明注解可以用于注解聲明(應(yīng)用于另一個(gè)注解上)*/
   ANNOTATION_TYPE,

   /** 標(biāo)明注解可以用于包聲明 */
   PACKAGE,

   /**
    * 標(biāo)明注解可以用于類型參數(shù)聲明(1.8新加入)
    */
   TYPE_PARAMETER,

   /**
    * 類型使用聲明(1.8新加入)
    */
   TYPE_USE
}

例如@Override注解使用了 @Target(ElementType.METHOD),那么就意味著侨颈,它只能注解方法富雅,不能注解其他程序元素。

當(dāng)注解未指定Target值時(shí)肛搬,則此注解可以用于任何元素之上,多個(gè)值使用{}包含并用逗號隔開毕贼,下面代碼表示温赔,此Annotation既可以注解構(gòu)造函數(shù)、字段和方法:

@Target(value={CONSTRUCTOR, FIELD, METHOD})

值得注意的是鬼癣,TYPE_PARAMETER陶贼,TYPE_USE是Java8 加入的新類型,在Java8之前待秃,只能在聲明各種程序元素時(shí)使用注解拜秧,而TYPE_PARAMETER允許使用注解修飾參數(shù)類型,TYPE_USE允許使用注解修飾任意類型章郁。

//TYPE_PARAMETER 修飾類型參數(shù)
class A<@Parameter T> { }

//TYPE_USE則可以用于標(biāo)注任意類型(不包括class)

//用于父類或者接口
class Image implements @Rectangular Shape { }

//用于構(gòu)造函數(shù)
new @Path String("/usr/bin")

//用于強(qiáng)制轉(zhuǎn)換和instanceof檢查,注意這些注解中用于外部工具枉氮,它們不會(huì)對類型轉(zhuǎn)換或者instanceof的檢查行為帶來任何影響。
String path=(@Path String)input;
if(input instanceof @Path String)

//用于指定異常
public Person read() throws @Localized IOException.

//用于通配符綁定
List<@ReadOnly ? extends Person>
List<? extends @ReadOnly Person>

@NotNull String.class //非法暖庄,不能標(biāo)注class
import java.lang.@NotNull String //非法聊替,不能標(biāo)注import

雖然Java8 提供了類型注解,但是沒有提供APT,所以需要框架自己實(shí)現(xiàn)培廓。

@Documented:將被標(biāo)注的注解生成到javadoc中惹悄。

@Inherited:其讓被修飾的注解擁有被繼承的能力。如下肩钠,我們有一個(gè)用@Inherited修飾的注解@InAnnotation泣港,那么這個(gè)注解就擁有了被繼承的能力。

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InAnnotation{
}

@InAnnotation
class Base{}

class Son extends Base{}

當(dāng)使用此注解修飾一個(gè)基類Base, 其子類Son 并沒有使用任何注解修飾价匠,但是其已經(jīng)擁有了@InAnnotation這個(gè)注解当纱,相當(dāng)于Son 已經(jīng)被@InAnnotation修飾了

@Repeatable :使被修飾的注解可以重復(fù)的注解某一個(gè)程序元素。例如下面的代碼中@ShuSheng這個(gè)自定義注解使用了@Repeatable修飾踩窖,所以其可以按照下面的語法重復(fù)的注解一個(gè)類惫东。

@ShuSheng(name="frank",age=18)
@ShuSheng(age = 20)
public class AnnotationDemo{}

如何定義一個(gè)重復(fù)注解呢,如下所示,我們需要先定義一個(gè)容器,例如ShuShengs 廉沮,然后將其作為參數(shù)傳入@Repeatable中颓遏。

@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuSheng {
    String name() default "ben";
    int age();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShengs {
    ShuSheng[] value();
}

自定義注解

通過前面的講解,很容易得出如何自定義一個(gè)注解滞时。注解是以關(guān)鍵字@interface 來定義的叁幢,下面我們自定義一個(gè)注解。
注解按照有無成員變量可以分為:

  • 標(biāo)記Annotation:無成員變量坪稽,只利用自身是否存在來提供信息曼玩。

    @Target(ElementType.METHOD)//只能應(yīng)用于方法上。
    @Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
    public @interface Test {
    }
    
  • 元數(shù)據(jù)Annotation:有一個(gè)或者多個(gè)成員變量窒百,可以接收外界信息黍判。

```
@Target(ElementType.TYPE)//只能應(yīng)用于類型上,包括類篙梢,接口顷帖。
@Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
public @interface Table {
    String name() default "";
}
```

以上就是我們定義的兩種注解,那么如何使用呢

//在類上使用該注解
@Table (name = "MEMBER")
public class Member {
    @Test 
    public void method()
    {...}
}

如何使用注解

就像我們文章開頭說的渤滞,當(dāng)我們使用注解修飾了程序元素后贬墩,這種Annotation不會(huì)自己起作用,的需要APT的幫助妄呕,那么這些APT就需要讀取代碼中的屬性信息陶舞,那么如何讀取呢?答案是通過反射绪励!

Annotation接口是所有注解的父接口(需要通過反編譯查看)肿孵,在java.lang.reflect 反射包下存在一個(gè)叫AnnotatedElement接口,其表示程序中可以接受注解的程序元素疏魏,例如 類颁井,方法,字段蠢护,構(gòu)造函數(shù)雅宾,包等等。而Java為使用反射的主要類實(shí)現(xiàn)了此接口葵硕,如反射包內(nèi)的Constructor類眉抬、Field類、Method類懈凹、Package類和Class類蜀变。

當(dāng)我們通過反射技術(shù)獲取到反射包內(nèi)的那些類型的實(shí)例后,就可以使用AnnotatedElement接口的中的API方法來獲取注解的信息了介评。

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass); : 返回該元素上存在的指定類型的注解库北,如果不存在則返回 null爬舰。
  • default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass){} :返回該元素上存在的直接修飾該元素的指定類型的注解,如果不存在則返回null.
  • Annotation[] getAnnotations();:返回該元素上存在的所有注解寒瓦。
  • Annotation[] getDeclaredAnnotations();:返回該元素上存在的直接修飾該元素的所有注解情屹。
  • default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass){}:該方法功能與前面getAnnotation方法類似,但是由于Java8 加入了重復(fù)注解功能杂腰,因此需要此方法獲取修飾該程序元素的指定類型的多個(gè)Annotation

獲取注解簡單示例

首先我們定義了兩個(gè)注解@Master@ShuSheng垃你,@ShuSheng是一個(gè)可重復(fù)注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Master {
}


@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuSheng {
    String name() default "ben";
    int age();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShengs {
    ShuSheng[] value();
}

然后我們定義了兩個(gè)類,使用定義好的注解來修飾喂很,如下

@Master
public class AnoBase {
}

@ShuSheng(name="frank",age=18)
@ShuSheng(age = 20)
public class AnnotationDemo extends AnoBase{
}

最后我們來調(diào)用相關(guān)函數(shù)獲取相應(yīng)的結(jié)果
private static void getAnnotation()

   {
       Class<?> cInstance=AnnotationDemo.class;

       //獲取AnnotationDemo上的重復(fù)注解
       ShuSheng[] ssAons= cInstance.getAnnotationsByType(ShuSheng.class);
       System.out.println("重復(fù)注解:"+Arrays.asList(ssAons).toString());

       //獲取AnnotationDemo上的所有注解惜颇,包括從父類繼承的
       Annotation[] allAno=cInstance.getAnnotations();
       System.out.println("所有注解:"+Arrays.asList(allAno).toString());

       //判斷AnnotationDemo上是否存在Master注解
       boolean isP=cInstance.isAnnotationPresent(Master.class);
       System.out.println("是否存在Master: "+isP);
   }

執(zhí)行結(jié)果如下:

重復(fù)注解:[@top.ss007.ShuSheng(name=frank, age=18), @top.ss007.ShuSheng(name=ben, age=20)]
所有注解:[@top.ss007.ShuShengs(value=[@top.ss007.ShuSheng(name=frank, age=18), @top.ss007.ShuSheng(name=ben, age=20)])]
是否存在Master: false

自定義注解處理器(APT)

了解完注解與反射的相關(guān)API后,就可以更進(jìn)一步少辣。下面的實(shí)例自定義了一個(gè)APT,完成通過注解構(gòu)建SQL語句的功能凌摄。此處代碼來自此處。下面代碼要求對數(shù)據(jù)庫有初步認(rèn)識漓帅。

先定義相關(guān)的注解

/**
 * 用來注解表
 */
@Target(ElementType.TYPE)//只能應(yīng)用于類上
@Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
public @interface DBTable {
    String name() default "";
}

/**
 * 注解Integer類型的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    //該字段對應(yīng)數(shù)據(jù)庫表列名
    String name() default "";
    //嵌套注解
    Constraints constraint() default @Constraints;
}

/**
 * 注解String類型的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    //對應(yīng)數(shù)據(jù)庫表的列名
    String name() default "";
    //列類型分配的長度锨亏,如varchar(30)的30
    int value() default 0;
    Constraints constraint() default @Constraints;
}

/**
 * 約束注解
 */
@Target(ElementType.FIELD)//只能應(yīng)用在字段上
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    //判斷是否作為主鍵約束
    boolean primaryKey() default false;
    //判斷是否允許為null
    boolean allowNull() default false;
    //判斷是否唯一
    boolean unique() default false;
}

/**
 * 數(shù)據(jù)庫表Member對應(yīng)實(shí)例類bean
 */
@DBTable(name = "MEMBER")
public class Member {
    //主鍵ID
    @SQLString(name = "ID",value = 50, constraint = @Constraints(primaryKey = true))
    private String id;
    
    @SQLString(name = "NAME" , value = 30)
    private String name;
    
    @SQLInteger(name = "AGE")
    private int age;
    
    @SQLString(name = "DESCRIPTION" ,value = 150 , constraint = @Constraints(allowNull = true))
    private String description;//個(gè)人描述

   //省略set get.....
}

上述定義4個(gè)注解,分別是@DBTable(用于類上)煎殷、@Constraints(用于字段上)、 @SQLString(用于字段上)腿箩、@SQLString(用于字段上)并在Member類中使用這些注解豪直,這些注解的作用的是用于幫助注解處理器生成創(chuàng)建數(shù)據(jù)庫表MEMBER的構(gòu)建語句,在這里有點(diǎn)需要注意的是珠移,我們使用了嵌套注解@Constraints弓乙,該注解主要用于判斷字段是否為null或者字段是否唯一。接下來就需要編寫我們自己的注解處理器了钧惧。

public class TableCreator {

  public static String createTableSql(String className) throws ClassNotFoundException {
    Class<?> cl = Class.forName(className);
    DBTable dbTable = cl.getAnnotation(DBTable.class);
    //如果沒有表注解暇韧,直接返回
    if(dbTable == null) {
      System.out.println(
              "No DBTable annotations in class " + className);
      return null;
    }
    String tableName = dbTable.name();
    // If the name is empty, use the Class name:
    if(tableName.length() < 1)
      tableName = cl.getName().toUpperCase();
    List<String> columnDefs = new ArrayList<String>();
    //通過Class類API獲取到所有成員字段
    for(Field field : cl.getDeclaredFields()) {
      String columnName = null;
      //獲取字段上的注解
      Annotation[] anns = field.getDeclaredAnnotations();
      if(anns.length < 1)
        continue; // Not a db table column

      //判斷注解類型
      if(anns[0] instanceof SQLInteger) {
        SQLInteger sInt = (SQLInteger) anns[0];
        //獲取字段對應(yīng)列名稱,如果沒有就是使用字段名稱替代
        if(sInt.name().length() < 1)
          columnName = field.getName().toUpperCase();
        else
          columnName = sInt.name();
        //構(gòu)建語句
        columnDefs.add(columnName + " INT" +
                getConstraints(sInt.constraint()));
      }
      //判斷String類型
      if(anns[0] instanceof SQLString) {
        SQLString sString = (SQLString) anns[0];
        // Use field name if name not specified.
        if(sString.name().length() < 1)
          columnName = field.getName().toUpperCase();
        else
          columnName = sString.name();
        columnDefs.add(columnName + " VARCHAR(" +
                sString.value() + ")" +
                getConstraints(sString.constraint()));
      }
    }
    //數(shù)據(jù)庫表構(gòu)建語句
    StringBuilder createCommand = new StringBuilder(
            "CREATE TABLE " + tableName + "(");
    for(String columnDef : columnDefs)
      createCommand.append("\n    " + columnDef + ",");

    // Remove trailing comma
    String tableCreate = createCommand.substring(
            0, createCommand.length() - 1) + ");";
    return tableCreate;
  }

  /**
   * 判斷該字段是否有其他約束
   * @param con
   * @return
   */
  private static String getConstraints(Constraints con) {
    String constraints = "";
    if(!con.allowNull())
      constraints += " NOT NULL";
    if(con.primaryKey())
      constraints += " PRIMARY KEY";
    if(con.unique())
      constraints += " UNIQUE";
    return constraints;
  }

  public static void main(String[] args) throws Exception {
    String[] arg={"com.zejian.annotationdemo.Member"};
    for(String className : arg) {
      System.out.println("Table Creation SQL for " +
              className + " is :\n" + createTableSql(className));
    }
  }
}

輸出結(jié)果為:

Table Creation SQL for com.zejian.annotationdemo.Member is :
CREATE TABLE MEMBER(
        ID VARCHAR(50) NOT NULL PRIMARY KEY,
        NAME VARCHAR(30) NOT NULL,
        AGE INT NOT NULL,
        DESCRIPTION VARCHAR(150)
        );

常用場景

Annotation浓瞪,特別是自定義注解懈玻,一般是在構(gòu)建框架或者通用庫時(shí)候使用的較多。下面列出了些我知道的乾颁,其他的歡迎補(bǔ)充涂乌。

Spring框架:解耦神器。
JUnit :測試框架
ButterKnife :在Android中使用的視圖注解框架英岭,Android的小伙伴們都知道湾盒。
Dagger2 :依賴注入框架,在Android中用的也比較多诅妹。
Retrofit :Http網(wǎng)絡(luò)訪問框架罚勾,Android網(wǎng)絡(luò)請求標(biāo)配毅人。
Room :Google 發(fā)布的用于Android開發(fā)的本地?cái)?shù)據(jù)庫解決方案庫。

參考文章:深入理解Java注解類型(@Annotation)
《瘋狂Java講義》
《Think in java》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尖殃,一起剝皮案震驚了整個(gè)濱河市丈莺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌分衫,老刑警劉巖场刑,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蚪战,居然都是意外死亡牵现,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門邀桑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞎疼,“玉大人,你說我怎么就攤上這事壁畸≡艏保” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵捏萍,是天一觀的道長太抓。 經(jīng)常有香客問我,道長令杈,這世上最難降的妖魔是什么走敌? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮逗噩,結(jié)果婚禮上掉丽,老公的妹妹穿的比我還像新娘。我一直安慰自己异雁,他們只是感情好捶障,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纲刀,像睡著了一般项炼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上示绊,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天芥挣,我揣著相機(jī)與錄音,去河邊找鬼耻台。 笑死空免,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盆耽。 我是一名探鬼主播蹋砚,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼扼菠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坝咐?” 一聲冷哼從身側(cè)響起循榆,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎墨坚,沒想到半個(gè)月后秧饮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泽篮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年盗尸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帽撑。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泼各,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亏拉,到底是詐尸還是另有隱情扣蜻,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布及塘,位于F島的核電站莽使,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笙僚。R本人自食惡果不足惜芳肌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望味咳。 院中可真熱鬧庇勃,春花似錦檬嘀、人聲如沸槽驶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掂铐。三九已至,卻和暖如春揍异,著一層夾襖步出監(jiān)牢的瞬間全陨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工衷掷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辱姨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓戚嗅,卻偏偏與公主長得像雨涛,于是被迫代替她去往敵國和親枢舶。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359