Java 反射,這篇寫的很透徹!

一、反射機制是什么?

二、反射的具體使用

    • 2.1 獲取對象的包名以及類名

    • 2.2 獲取Class對象

    • 2.3 getInstance()獲取指定類型的實例化對象

    • 2.4 通過構造函數對象實例化對象

    • 2.5 獲取類繼承的接口

    • 2.6 獲取父類相關信息

    • 2.7 獲取當前類的公有屬性和私有屬性以及更新

    • 2.8 獲取以及調用類的公有/私有方法

三、反射的優缺點

    • 3.1 優點

    • 3.2 缺點

很多時候我們會遇到別人問一個問題:你給我講一下反射,到底是什么東西?怎么實現的?我們能用反射來做什么?它有什么優缺點?下面我們會圍繞著這幾個問題展開:
圖片
image.gif

?

一、反射機制是什么?

反射是什么?什么是反?什么是正射?有反就有正,我們知道正常情況, 如果我們希望創建一個對象,會使用以下的語句:

Person person = new Person();

image.gif

其實我們第一次執行上面的語句的時候,JVM會先加載Person.class,加載到內存完之后,在方法區/堆中會創建了一個Class對象,對應這個Person類。這里有爭議,有人說是在方法區,有些人說是在堆。個人感覺應該JVM規范說是在方法區,但是不是強制要求,而且不同版本的JVM實現也不一樣。具體參考以下鏈接,這里不做解釋:https://www.cnblogs.com/xy-nb/p/6773051.html 而上面正常的初始化對象的方法,也可以說是“正射”,就是使用Class對象創建出一個Person對象。

而反射則相反,是根據Person對象,獲取到Class對象,然后可以獲取到Person類的相關信息,進行初始化或者調用等一系列操作。

運行狀態時,可以構造任何一個類的對象,獲取到任意一個對象所屬的類信息,以及這個類的成員變量或者方法,可以調用任意一個對象的屬性或者方法。可以理解為具備了動態加載對象以及對對象的基本信息進行剖析和使用的能力。

提供的功能包括:

  • 1.在運行時判斷一個對象所屬的類

  • 2.在運行時構造任意一個類的對象

  • 3.在運行時獲取一個類定義的成員變量以及方法

  • 4.在運行時調用任意一個對象的方法

  • 5.生成動態代理

靈活,強大,可以在運行時裝配,無需在組件之間進行源代碼鏈接,但是使用不當效率會有影響。所有類的對象都是Class的實例。既然我們可以對類的全限定名,方法以及參數等進行配置,完成對象的初始化,那就是相當于增加了java的可配置性。

這里特別需要明確的一點:類本身也是一個對象,方法也是一個對象,在Java里面萬物皆可對象,除了基礎數據類型...

二、反射的具體使用

2.1 獲取對象的包名以及類名

package invocation;
public class MyInvocation {
    public static void main(String[] args) {
        getClassNameTest();
    }

    public static void getClassNameTest(){
        MyInvocation myInvocation = new MyInvocation();
        System.out.println("class: " + myInvocation.getClass());
        System.out.println("simpleName: " + myInvocation.getClass().getSimpleName());
        System.out.println("name: " + myInvocation.getClass().getName());
        System.out.println("package: " +
                "" + myInvocation.getClass().getPackage());
    }
}

image.gif

運行結果:

class: class invocation.MyInvocation
simpleName: MyInvocation
name: invocation.MyInvocation
package: package invocation

image.gif

由上面結果我們可以看到:1.getClass():打印會帶著class+全類名 2.getClass().getSimpleName():只會打印出類名 3.getName():會打印全類名 4.getClass().getPackage():打印出package+包名

getClass()獲取到的是一個對象,getPackage()也是。

2.2 獲取Class對象

在java中,一切皆對象。java中可以分為兩種對象,實例對象和Class對象。這里我們說的獲取Class對象,其實就是第二種,Class對象代表的是每個類在運行時的類型信息,指和類相關的信息。比如有一個Student類,我們用Student student = new Student()new一個對象出來,這個時候Student這個類的信息其實就是存放在一個對象中,這個對象就是Class類的對象,而student這個實例對象也會和Class對象關聯起來。我們有三種方式可以獲取一個類在運行時的Class對象,分別是

  • Class.forName("com.Student")

  • student.getClass()

  • Student.class

實例代碼如下:

package invocation;

public class MyInvocation {
    public static void main(String[] args) {
        getClassTest();
    }
    public static void getClassTest(){
        Class<?> invocation1 = null;
        Class<?> invocation2 = null;
        Class<?> invocation3 = null;
        try {
            // 最常用的方法
            invocation1 = Class.forName("invocation.MyInvocation");
        }catch (Exception ex){
            ex.printStackTrace();
        }
        invocation2 = new MyInvocation().getClass();
        invocation3 = MyInvocation.class;
        System.out.println(invocation1);
        System.out.println(invocation2);
        System.out.println(invocation3);
    }
}

image.gif

執行的結果如下,三個結果一樣:

class invocation.MyInvocation
class invocation.MyInvocation
class invocation.MyInvocation

image.gif

2.3 getInstance()獲取指定類型的實例化對象

首先我們有一個Student類,后面都會沿用這個類,將不再重復。

class Student{
    private int age;

    private String name;

    public Student() {
    }
    public Student(int age) {
        this.age = age;
    }

    public Student(String name) {
        this.name = name;
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

image.gif

我們可以使用getInstance()方法構造出一個Student的對象:

    public static void getInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Student student = (Student) stduentInvocation.newInstance();
            student.setAge(9);
            student.setName("Hahs");
            System.out.println(student);

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

輸出結果如下:
Student{age=9, name='Hahs'}

image.gif

但是如果我們取消不寫Student的無參構造方法呢?就會出現下面的報錯:

java.lang.InstantiationException: invocation.Student
 at java.lang.Class.newInstance(Class.java:427)
 at invocation.MyInvocation.getInstanceTest(MyInvocation.java:40)
 at invocation.MyInvocation.main(MyInvocation.java:8)
Caused by: java.lang.NoSuchMethodException: invocation.Student.<init>()
 at java.lang.Class.getConstructor0(Class.java:3082)
 at java.lang.Class.newInstance(Class.java:412)
 ... 2 more

image.gif

這是因為我們重寫了構造方法,而且是有參構造方法,如果不寫構造方法,那么每個類都會默認有無參構造方法,重寫了就不會有無參構造方法了,所以我們調用newInstance()的時候,會報沒有這個方法的錯誤。值得注意的是,newInstance()是一個無參構造方法。

2.4 通過構造函數對象實例化對象

除了newInstance()方法之外,其實我們還可以通過構造函數對象獲取實例化對象,怎么理解?這里只構造函數對象,而不是構造函數,也就是構造函數其實就是一個對象,我們先獲取構造函數對象,當然也可以使用來實例化對象。

可以先獲取一個類的所有的構造方法,然后遍歷輸出:

    public static void testConstruct(){
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            for(int i=0;i<cons.length;i++){
                System.out.println(cons[i]);
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

image.gif

輸出結果:

public invocation.Student(int,java.lang.String)
public invocation.Student(java.lang.String)
public invocation.Student(int)
public invocation.Student()

image.gif

取出一個構造函數我們可以獲取到它的各種信息,包括參數,參數個數,類型等等:

    public static void constructGetInstance() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            Constructor constructors = cons[0];
            System.out.println("name: " + constructors.getName());
            System.out.println("modifier: " + constructors.getModifiers());
            System.out.println("parameterCount: " + constructors.getParameterCount());
            System.out.println("構造參數類型如下:");
            for (int i = 0; i < constructors.getParameterTypes().length; i++) {
                System.out.println(constructors.getParameterTypes()[i].getName());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

image.gif

輸出結果,modifier是權限修飾符,1表示為public,我們可以知道獲取到的構造函數是兩個參數的,第一個是int,第二個是String類型,看來獲取出來的順序并不一定是我們書寫代碼的順序。

name: invocation.Student
modifier: 1
parameterCount: 2
構造參數類型如下:
int
java.lang.String

image.gif

既然我們可以獲取到構造方法這個對象了,那么我們可不可以通過它去構造一個對象呢?答案肯定是可以!!!下面我們用不同的構造函數來創建對象:

    public static void constructGetInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            // 一共定義了4個構造器
            Student student1 = (Student) cons[0].newInstance(9,"Sam");
            Student student2 = (Student) cons[1].newInstance("Sam");
            Student student3 = (Student) cons[2].newInstance(9);
            Student student4 = (Student) cons[3].newInstance();
            System.out.println(student1);
            System.out.println(student2);
            System.out.println(student3);
            System.out.println(student4);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

image.gif

輸出如下:

Student{age=9, name='Sam'}
Student{age=0, name='Sam'}
Student{age=9, name='null'}
Student{age=0, name='null'}

image.gif

構造器的順序我們是必須一一針對的,要不會報一下的參數不匹配的錯誤:

java.lang.IllegalArgumentException: argument type mismatch
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
 at invocation.MyInvocation.constructGetInstanceTest(MyInvocation.java:85)
 at invocation.MyInvocation.main(MyInvocation.java:8)

image.gif

2.5 獲取類繼承的接口

通過反射我們可以獲取接口的方法,如果我們知道某個類實現了接口的方法,同樣可以做到通過類名創建對象調用到接口的方法。

首先我們定義兩個接口,一個InSchool:

public interface InSchool {
    public void attendClasses();
}

image.gif

一個AtHome:

public interface AtHome {
    public void doHomeWork();
}

image.gif

創建一個實現兩個接口的類Student.java

public class Student implements AtHome, InSchool {
    public void doHomeWork() {
        System.out.println("I am a student,I am doing homework at home");
    }

    public void attendClasses() {
        System.out.println("I am a student,I am attend class in school");
    }
}

image.gif

測試代碼如下:

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> studentClass = Class.forName("invocation.Student");
        Class<?>[] interfaces = studentClass.getInterfaces();
        for (Class c : interfaces) {
            // 獲取接口
            System.out.println(c);
            // 獲取接口里面的方法
            Method[] methods = c.getMethods();
            // 遍歷接口的方法
            for (Method method : methods) {
                // 通過反射創建對象
                Student student = (Student) studentClass.newInstance();
                // 通過反射調用方法
                method.invoke(student, null);
            }
        }
    }
}

image.gif

結果如下:
圖片
image.gif

?

可以看出其實我們可以獲取到接口的數組,并且里面的順序是我們繼承的順序,通過接口的Class對象,我們可以獲取到接口的方法,然后通過方法反射調用實現類的方法,因為這是一個無參數的方法,所以只需要傳null即可。

2.6 獲取父類相關信息

主要是使用getSuperclass()方法獲取父類,當然也可以獲取父類的方法,執行父類的方法,首先創建一個Animal.java:

public class Animal {
    public void doSomething(){
        System.out.println("animal do something");
    }
}

image.gif

Dog.java繼承于Animal.java

public class Dog extends Animal{
    public void doSomething(){
        System.out.println("Dog do something");
    }
}

image.gif

我們可以通過反射創建Dog對象,獲取其父類Animal以及創建對象,當然也可以獲取Animal的默認父類Object

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> dogClass = Class.forName("invocation02.Dog");
        System.out.println(dogClass);
        invoke(dogClass);

        Class<?> animalClass = dogClass.getSuperclass();
        System.out.println(animalClass);
        invoke(animalClass);

        Class<?> objectClass = animalClass.getSuperclass();
        System.out.println(objectClass);
        invoke(objectClass);
    }

    public static void invoke(Class<?> myClass) throws Exception {
        Method[] methods = myClass.getMethods();
        // 遍歷接口的方法
        for (Method method : methods) {
            if (method.getName().equalsIgnoreCase("doSomething")) {
                // 通過反射調用方法
                method.invoke(myClass.newInstance(), null);
            }
        }
    }
}

image.gif

輸入如下:
圖片
image.gif

?

2.7 獲取當前類的公有屬性和私有屬性以及更新

創建一個Person.java,里面有靜態變量,非靜態變量,以及publicprotected,private不同修飾的屬性。

public class Person {

    public static String type ;

    private static String subType ;

    // 名字(公開)
    public String name;

    protected String gender;

    private String address;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

image.gif

使用getFields()可以獲取到public的屬性,包括static屬性,使用getDeclaredFields()可以獲取所有聲明的屬性,不管是publicprotected,private不同修飾的屬性。

修改public屬性,只需要field.set(object,value)即可,但是private屬性不能直接set,否則會報以下的錯誤。

Exception in thread "main" java.lang.IllegalAccessException: Class invocation03.Tests can not access a member of class invocation03.Person with modifiers "private"
 at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
 at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
 at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
 at java.lang.reflect.Field.set(Field.java:761)
 at invocation03.Tests.main(Tests.java:21)

image.gif

那么需要怎么做呢?private默認是不允許外界操作其值的,這里我們可以使用field.setAccessible(true);,相當于打開了操作的權限。

那static的屬性修改和非static的一樣,但是我們怎么獲取呢?如果是public修飾的,可以直接用類名獲取到,如果是private修飾的,那么需要使用filed.get(object),這個方法其實對上面說的所有的屬性都可以的。測試代碼如下

public class Tests {
    public static void main(String[] args) throws Exception{
        Class<?> personClass = Class.forName("invocation03.Person");
        Field[] fields = personClass.getFields();
        // 獲取公開的屬性
        for(Field field:fields){
            System.out.println(field);
        }
        System.out.println("=================");
        // 獲取所有聲明的屬性
        Field[] declaredFields = personClass.getDeclaredFields();
        for(Field field:declaredFields){
            System.out.println(field);
        }
        System.out.println("=================");
        Person person = (Person) personClass.newInstance();
        person.name = "Sam";
        System.out.println(person);

        // 修改public屬性
        Field fieldName = personClass.getDeclaredField("name");
        fieldName.set(person,"Jone");

        // 修改private屬性
        Field addressName = personClass.getDeclaredField("address");
        // 需要修改權限
        addressName.setAccessible(true);
        addressName.set(person,"東風路47號");
        System.out.println(person);

        // 修改static 靜態public屬性
        Field typeName = personClass.getDeclaredField("type");
        typeName.set(person,"人類");
        System.out.println(Person.type);

        // 修改靜態 private屬性
        Field subType = personClass.getDeclaredField("subType");
        subType.setAccessible(true);
        subType.set(person,"黃種人");
        System.out.println(subType.get(person));
    }
}

image.gif

結果:
圖片
image.gif

?

從結果可以看出,不管是public,還是protectedprivate修飾的,我們都可以通過反射對其進行查詢和修改,不管是靜態變量還是非靜態變量。getDeclaredField()可以獲取到所有聲明的屬性,而getFields()則只能獲取到public的屬性。對于非public的屬性,我們需要修改其權限才能訪問和修改:field.setAccessible(true)

獲取屬性值需要使用field.get(object),值得注意的是:每個屬性,其本身就是對象

2.8 獲取以及調用類的公有/私有方法

既然可以獲取到公有屬性和私有屬性,那么我想,執行公有方法和私有方法應該都不是什么問題?
圖片
image.gif

?

那下面我們一起來學習一下...

先定義一個類,包含各種修飾符,以及是否包含參數,是否為靜態方法,Person.java:

public class Person {
    // 非靜態公有無參數
    public void read(){
        System.out.println("reading...");
    }

    // 非靜態公有無參數有返回
    public String getName(){
        return "Sam";
    }

    // 非靜態公有帶參數   
    public int readABookPercent(String name){
        System.out.println("read "+name);
        return 80;
    }

    // 私有有返回值
    private String getAddress(){
        return "東方路";
    }

    // 公有靜態無參數無返回值
    public static void staticMethod(){
        System.out.println("static public method");
    }

    // 公有靜態有參數
    public static void staticMethodWithArgs(String args){
        System.out.println("static public method:"+args);
    }

    // 私有靜態方法
    private static void staticPrivateMethod(){
        System.out.println("static private method");
    }
}

image.gif

首先我們來看看獲取里面所有的方法:

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("=============================================");
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
    }
}

image.gif

結果如下:
圖片

image.gif
?咦,我們發現getMethods()確實可以獲取所有的公有的方法,但是有一個問題,就是他會把父類的也獲取到,也就是上面圖片綠色框里面的,我們知道所有的類默認都繼承了Object類,所以它把Object的那些方法都獲取到了。而getDeclaredMethods確實可以獲取到公有和私有的方法,不管是靜態還是非靜態,但是它是獲取不到父類的方法的。

那如果我們想調用方法呢?先試試調用非靜態方法:

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.getName().equalsIgnoreCase("read")){
                method.invoke(person,null);
                System.out.println("===================");
            }else if(method.getName().equalsIgnoreCase("getName")){
                System.out.println(method.invoke(person,null));
                System.out.println("===================");
            }else if(method.getName().equalsIgnoreCase("readABookPercent")){
                System.out.println(method.invoke(person,"Sam"));
                System.out.println("===================");
            }
        }

    }
}

image.gif

結果如下,可以看出method.invoke(person,null);是調用無參數的方法,而method.invoke(person,"Sam")則是調用有參數的方法,要是有更多參數,也只需要在里面多加一個參數即可,返回值也同樣可以獲取到。

圖片

image.gif

?

那么private方法呢?我們照著來試試,試試就試試,who 怕 who?

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.getName().equalsIgnoreCase("getAddress")){
                method.invoke(person,null);
            }
        }

    }
}

image.gif

結果報錯了:

Exception in thread "main" java.lang.IllegalAccessException: Class invocation03.Tests can not access a member of class invocation03.Person with modifiers "private"
 at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
 at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
 at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
 at java.lang.reflect.Method.invoke(Method.java:491)
 at invocation03.Tests.main(Tests.java:13)

image.gif
圖片
image.gif

?

一看就是沒有權限,小場面,不要慌,我來操作一波,只要加上

method.setAccessible(true);

image.gif

哦豁,完美解決了...
圖片
image.gif

?

那么問題來了,上面說的都是非靜態的,我就想要調用靜態的方法。當然用上面的方法,對象也可以直接調用到類的方法的:
圖片
image.gif

?

一點問題都沒有,為什么輸出結果有幾個null,那是因為這函數是無返回值的呀,笨蛋...

如果我不想用遍歷方法的方式,再去判斷怎么辦?能不能直接獲取到我想要的方法啊?那答案肯定是可以啊。

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method method = personClass.getMethod("readABookPercent", String.class);
        method.invoke(person, "唐詩三百首");
    }
}

image.gif

結果和上面調用的完全一樣,圖我就不放了,就一行字。要是這個方法沒有參數呢?那就給一個null就可以啦。或者不給也可以。

public class Tests {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("invocation03.Person");
        Person person = (Person) personClass.newInstance();
        Method method = personClass.getMethod("getName",null);
        System.out.println(method.invoke(person));
    }
}

image.gif

三、反射的優缺點

3.1 優點

反射可以在不知道會運行哪一個類的情況下,獲取到類的信息,創建對象以及操作對象。這其實很方便于拓展,所以反射會是框架設計的靈魂,因為框架在設計的時候,為了降低耦合度,肯定是需要考慮拓展等功能的,不能將類型寫死,硬編碼。

降低耦合度,變得很靈活,在運行時去確定類型,綁定對象,體現了多態功能。

3.2 缺點

這么好用,沒有缺點?怎么可能!!!有利就有弊,事物都是有雙面性的。即使功能很強大,但是反射是需要動態類型的,JVM沒有辦法優化這部分代碼,執行效率相對直接初始化對象較低。一般業務代碼不建議使用。

反射可以修改權限,比如上面訪問到private這些方法和屬性,這是會破壞封裝性的,有安全隱患,有時候,還會破壞單例的設計。

反射會使代碼變得復雜,不容易維護,畢竟代碼還是要先寫給人看的嘛,逃~


第二張_01.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373