Java反射機(jī)制詳解

對于一般的開發(fā)者,很少需要直接使用Java反射機(jī)制來完成功能開發(fā),但是反射是很多框架譬如 Spring摔吏, Mybatis 實現(xiàn)的核心韧拒,反射雖小,能量卻很大。

本文主要介紹反射相關(guān)的概念以及API的使用,關(guān)于反射的應(yīng)用將在下一篇文章中介紹

反射的介紹

反射(Reflection) 是 Java 在運(yùn)行時(Run time)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力贝咙,它允許運(yùn)行中的 Java 程序獲取自身的信息,并且可以操作類或?qū)ο蟮膬?nèi)部屬性拂募。

Class 類介紹:Java虛擬機(jī)為每個類型管理一個Class對象庭猩,包含了與類有關(guān)的信息,當(dāng)通過 javac 編譯Java類文件時陈症,生成的同名 .class 文件保存著該類的 Class 對象蔼水,JVM 加載一個類即是加載該 .class 文件。

Classjava.lang.reflect 一起對反射提供了支持爬凑,java.lang.reflect 包中最常用的幾個類的關(guān)系如下:

reflect package

其中最主要的三個類 Field徙缴、MethodConstructor 分別用于描述類的域、方法和構(gòu)造器嘁信,它們有一個共同的父類 AccessibleObject于样,它提供了訪問控制檢查的功能。

  • Field :描述類的域(屬性)潘靖,可以使用 get() 和 set() 方法讀取和修改 Field 對象關(guān)聯(lián)的字段穿剖;
  • Method :描述類的方法,可以使用 invoke() 方法調(diào)用與 Method 對象關(guān)聯(lián)的方法卦溢;
  • Constructor :描述類的構(gòu)造器糊余,可以用 Constructor 創(chuàng)建新的對象秀又。

下面將通過幾個程序來學(xué)習(xí)Java反射機(jī)制。

準(zhǔn)備兩個類用于實驗

我們特別定義兩個類贬芥,Person和Employee吐辙,其中Employee繼承自Person,且各自都有一個private蘸劈,protected昏苏,public修飾的域(屬性),Employee還有private威沫,public修飾的方法

public class Person {
    public String name; // 姓名 公有
    protected String age;   // 年齡 保護(hù)
    private String hobby;   // 愛好   私有

    public Person(String name, String age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }
    public String getHobby() {
        return hobby;
    }
}

public class Employee extends Person {
    public static Integer totalNum = 0; // 員工數(shù)
    public int empNo;   // 員工編號 公有
    protected String position;  // 職位 保護(hù)
    private int salary; // 工資   私有

    public void sayHello() {
        System.out.println(String.format("Hello, 我是 %s, 今年 %s 歲, 愛好是%s, 我目前的工作是%s, 月入%s元\n", name, age, getHobby(), position, salary));
    }
    private void work() {
        System.out.println(String.format("My name is %s, 工作中勿擾.", name));
    }
    public Employee(String name, String age, String hobby, int empNo, String position, int salary) {
        super(name, age, hobby);
        this.empNo = empNo;
        this.position = position;
        this.salary = salary;
        Employee.totalNum++;
    }
}

獲取 Class 對象

獲取 Class 對象的方式有三種:使用 Class 類的 forName 靜態(tài)方法贤惯;直接獲取某一個對象的 class;調(diào)用某個對象的 getClass() 方法

public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class c1 = Class.forName("reflect.Employee");   // 第1種棒掠,forName 方式獲取Class對象
        Class c2 = Employee.class;      // 第2種孵构,直接通過類獲取Class對象
        Employee employee = new Employee("小明", "18", "寫代碼", 1, "Java攻城獅", 100000);
        Class c3 = employee.getClass();    // 第3種,通過調(diào)用對象的getClass()方法獲取Class對象

        if (c1 == c2 && c1 == c3) {     // 可以通過 == 比較Class對象是否為同一個對象
            System.out.println("c1烟很、c2颈墅、c3 為同一個對象");
            System.out.println(c1);     // class reflect.Employee
        }
    }
}

通過反射來創(chuàng)建實例

通過反射來生成對象主要有兩種方式

  • 使用Class對象的newInstance()方法來創(chuàng)建Class對象對應(yīng)類的實例
  • 先通過Class對象獲取指定的Constructor對象,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建實例
public class NewInstanceTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class c = Date.class;
        Date date1 = (Date) c.newInstance();    // 第1種方式:使用Class對象的newInstance()方法來創(chuàng)建Class對象對應(yīng)類的實例
        System.out.println(date1);      // Wed Dec 19 22:57:16 CST 2018

        long timestamp =date1.getTime();
        Constructor constructor = c.getConstructor(long.class); 
        Date date2 = (Date)constructor.newInstance(timestamp);  // 第2種方式:先通過Class對象獲取指定的Constructor對象溯职,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建實例
        System.out.println(date2);  // Wed Dec 19 22:57:16 CST 2018
    }
}

獲取類的全部信息

上面我們定義了兩個類精盅,現(xiàn)在有個需求:獲取Employee的類名帽哑,構(gòu)造器簽名谜酒,所有的方法,所有的域(屬性)和值妻枕,然后打印出來僻族。該通過什么方式來實現(xiàn)呢?

沒錯屡谐,猜對了述么,就是通過反射來獲取這些類的信息,在上面介紹中我們知道JVM虛擬機(jī)為每個類型管理一個Class對象愕掏,

為了完成我們的需求度秘,我們需要知道一些API如下:

獲取類信息的部分API

String getName() 獲取這個Class的類名

Constructor[] getDeclaredConstructors() 返回這個類的所有構(gòu)造器的對象數(shù)組,包含保護(hù)和私有的構(gòu)造器饵撑;相近的方法 getConstructors() 則返回這個類的所有公有構(gòu)造器的對象數(shù)組剑梳,不包含保護(hù)和私有的構(gòu)造器

Method[] getDeclaredMethods() 返回這個類或接口的所有方法,包括保護(hù)和私有的方法滑潘,不包括超類的方法垢乙;相近的方法 getMethods() 則返回這個類及其超類的公有方法的對象數(shù)組,不含保護(hù)和私有的方法

Field[] getDeclaredFields() 返回這個類的所有域的對象數(shù)組语卤,包括保護(hù)域和私有域追逮,不包括超類的域酪刀;還有一個相近的API getFields(),返回這個類及其超類的公有域的對象數(shù)組钮孵,不含保護(hù)域和私有域

int getModifiers() 返回一個用于描述Field骂倘、Method和Constructor的修飾符的整形數(shù)值,該數(shù)值代表的含義可通過Modifier這個類分析

Modifier 類 它提供了有關(guān)Field巴席、Method和Constructor等的訪問修飾符的信息稠茂,主要的方法有:toString(int modifiers)返回整形數(shù)值modifiers代表的修飾符的字符串;isAbstract是否被abstract修飾情妖;isVolatile是否被volatile修飾睬关;isPrivate是否為private;isProtected是否為protected毡证;isPublic是否為public电爹;isStatic是否為static修飾;等等料睛,見名知義

打印類信息程序

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        String name;
        if (args.length > 0) {
            name = args[0];
        } else {
            Scanner in = new Scanner(System.in);
            System.out.println("輸入一個類名(e.g. java.util.Date):"); // reflect.Employee
            name = in.next();
        }
        try {
            Class cl = Class.forName(name);
            Class superCl = cl.getSuperclass();
            String modifiers = Modifier.toString(cl.getModifiers());
            if (modifiers.length() > 0) {
                System.out.print(modifiers + " ");
            }
            System.out.print("class " + name);
            if (superCl != null && superCl != Object.class) {
                System.out.print(" extends " + superCl.getName());
            }
            System.out.println("\n{");

            printConstructors(cl); // 打印構(gòu)造方法
            System.out.println();
            printMethods(cl);   // 打印方法
            System.out.println();
            printFields(cl);    // 打印屬性
            System.out.println("}");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    /**
     * 打印Class對象的所有構(gòu)造方法
     */
    public static void printConstructors(Class cl) {
        Constructor[] constructors = cl.getDeclaredConstructors();

        for (Constructor c : constructors) {
            String name = c.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(c.getModifiers());
            if (modifiers.length() > 0) {
                System.out.print(modifiers + " ");
            }
            System.out.print(name + "(");
            // 打印構(gòu)造參數(shù)
            Class[] paramTypes = c.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                if (i > 0) {
                    System.out.print(", ");
                }
                System.out.print(paramTypes[i].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * 打印Class的所有方法
     */
    public static void printMethods(Class cl) {
        Method[] methods = cl.getDeclaredMethods();
        //Method[] methods = cl.getMethods();
        for (Method m : methods) {
            Class retType = m.getReturnType();  // 返回類型
            System.out.print("  ");
            String modifiers = Modifier.toString(m.getModifiers());
            if (modifiers.length() > 0) {
                System.out.print(modifiers + " ");
            }
            System.out.print(retType.getName() + " " + m.getName() + "(");
            Class[] paramTypes = m.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                if (i > 0) {
                    System.out.print(", ");
                }
                System.out.print(paramTypes[i].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * 打印Class的所有屬性
     */
    public static void printFields(Class cl) {
        Field[] fields = cl.getDeclaredFields();
        for (Field f: fields) {
            Class type = f.getType();
            System.out.print("  ");
            String modifiers = Modifier.toString(f.getModifiers());
            if (modifiers.length()> 0) {
                System.out.print(modifiers + " ");
            }
            System.out.println(type.getName() + " " + f.getName() + ";");
        }
    }
}

運(yùn)行程序丐箩,然后在控制臺輸入一個我們想分析的類的全名,譬如 reflect.Employee恤煞,可得到下面的輸出

輸入一個類名(e.g. java.util.Date):
reflect.Employee
public class reflect.Employee extends reflect.Person
{
  private reflect.Employee(java.lang.String, java.lang.String, java.lang.String);
  public reflect.Employee(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, int);

  public static void main([Ljava.lang.String;);
  public void sayHello();
  private void work();

  public static java.lang.Integer totalNum;
  public int empNo;
  protected java.lang.String position;
  private int salary;
}

上面的輸出中我們得到的類的構(gòu)造器屎勘,所有方法和所有的域(屬性),包括修飾符居扒,名稱和參數(shù)類型都是準(zhǔn)確的概漱,看來反射機(jī)制能完成我們的需求。

小結(jié)一下喜喂,我們通過 getDeclaredConstructors() 獲取構(gòu)造器信息瓤摧,通過 getDeclaredMethods() 獲得方法信息,通過 getDeclaredFields() 獲得域信息玉吁,再通過 getModifiers() 和 Modifier類 獲得修飾符信息照弥,匯總起來就得到了整個類的類信息。

運(yùn)行時查看對象數(shù)據(jù)域的實際內(nèi)容

上面我們已經(jīng)獲取到了類的信息进副,現(xiàn)在又有一個需求:在運(yùn)行時查看對象的數(shù)據(jù)域的實際值这揣。這個場景就像我們通過IDEA調(diào)試程序,設(shè)置斷點攔截到程序后影斑,查看某個對象的屬性的值给赞。

我們知道java反射機(jī)制提供了查看類信息的API,那么它應(yīng)該也提供了查看Field域?qū)嶋H值和設(shè)置Field域?qū)嶋H值的API鸥昏,沒錯塞俱,猜對了,確實有相關(guān)的API吏垮,但是有個疑問障涯,有一些屬性是private修飾的私有域罐旗,這種是否也能直接查看和設(shè)置呢?看完下面的API即可知道答案

運(yùn)行時查看對象數(shù)據(jù)域?qū)嶋H內(nèi)容的相關(guān)API

Class<?> getComponentType() 返回數(shù)組類里組件類型的 Class唯蝶,如果不是數(shù)組類則返回null

boolean isArray() 返回這個類是否為數(shù)組九秀,同類型的API還有 isAnnotation、isAsciiDigit粘我、isEnum鼓蜒、isInstance、isInterface征字、isLocalClass都弹、isPrimitive 等

int Array.getLength(obj) 返回數(shù)組對象obj的長度

Object Array.get(obj, i) 獲取數(shù)組對象下標(biāo)為i的元素

boolean isPrimitive() 返回這個類是否為8種基本類型之一,即是否為boolean, byte, char, short, int, long, float, 和double 等原始類型

Field getField(String name) 獲取指定名稱的域?qū)ο?/p>

AccessibleObject.setAccessible(fields, true) 當(dāng)訪問 Field匙姜、Method 和 Constructor 的時候Java會執(zhí)行訪問檢查畅厢,如果訪問者沒有權(quán)限將拋出SecurityException,譬如訪問者是無法訪問private修飾的域的氮昧。通過設(shè)置 setAccessible(true) 可以取消Java的執(zhí)行訪問檢查框杜,這樣訪問者就獲得了指定 Field、Method 或 Constructor 訪問權(quán)限

Class<?> Field.getType() 返回一個Class 對象袖肥,它標(biāo)識了此 Field 對象所表示字段的聲明類型

Object Field.get(Object obj) 獲取obj對象上當(dāng)前域?qū)ο蟊硎镜膶傩缘膶嶋H值咪辱,獲取到的是一個Object對象,實際使用中還需要轉(zhuǎn)換成實際的類型椎组,或者可以通過 getByte()油狂、getChar、getInt() 等直接獲取具體類型的值

void Field.set(Object obj, Object value) 設(shè)置obj對象上當(dāng)前域表示的屬性的實際值

查看對象數(shù)據(jù)域?qū)嶋H內(nèi)容程序

了解完上述相關(guān)API之后庐杨,我們敲出下面的程序來驗證

public class ObjectAnalyzer {
    private ArrayList<Object> visited = new ArrayList<>();

    public String toString(Object obj) {
        if (obj == null) {
            return "null";
        }
        if (visited.contains(obj)) {    // 如果該對象已經(jīng)處理過选调,則不再處理
            return "...";
        }
        visited.add(obj);

        Class cl = obj.getClass(); // 獲取Class對象
        if (cl == String.class) {   // 如果是String類型則直接轉(zhuǎn)為String
            return (String) obj;
        }
        if (cl.isArray()) {        // 如果是數(shù)組
            String r = cl.getComponentType() + "[]{\n";     // 數(shù)組的元素的類型
            for (int i = 0; i < Array.getLength(obj); i++) {
                if (i > 0) {   // 不是數(shù)組的第一個元素加逗號和換行夹供,顯示更加美觀
                    r += ",\n";
                }
                r += "\t";
                Object val = Array.get(obj, i);
                if (cl.getComponentType().isPrimitive()) { // Class為8種基本類型的時候為 true灵份,直接輸出
                    r += val;
                } else {
                    r += toString(val); // 不是8中基本類型時,說明是類哮洽,遞歸調(diào)用toString
                }
            }
            return r + "\n}";
        }
        // 既不是String填渠,也不是數(shù)組時,輸出該對象的類型和屬性值
        String r = cl.getName();
        do {
            r += "[";
            Field[] fields = cl.getDeclaredFields();    // 獲取該類自己定義的所有域鸟辅,包括私有的氛什,不包括父類的
            AccessibleObject.setAccessible(fields, true); // 訪問私有的屬性,需要打開這個設(shè)置匪凉,否則會報非法訪問異常
            for (Field f : fields) {
                if (!Modifier.isStatic(f.getModifiers())) { // 通過 Modifier 可獲取該域的修飾符枪眉,這里判斷是否為 static
                    if (!r.endsWith("[")) {
                        r += ",";
                    }
                    r += f.getName() + "=";     // 域名稱
                    try {
                        Class t = f.getType();  // 域(屬性)的類型
                        Object val = f.get(obj);   // 獲取obj對象上該域的實際值
                        if (t.isPrimitive()) {     // 如果類型為8種基本類型,則直接輸出
                            r += val;
                        } else {
                            r += toString(val);     // 不是8種基本類型再层,遞歸調(diào)用toString
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            r += "]";
            cl = cl.getSuperclass(); // 繼續(xù)打印超類的類信息
        } while (cl != null);
        return r;
    }
}

測試驗證結(jié)果

接下來驗證一下獲取數(shù)據(jù)域?qū)嶋H值是否正確贸铜,分別打印數(shù)組堡纬、自定義類的對象的實際值

public class ObjectAnalyzerTest {
    public static void main(String[] args) {
        int size = 4;
        ArrayList<Integer> squares = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            squares.add(i * i);
        }
        ObjectAnalyzer objectAnalyzer = new ObjectAnalyzer(); // 創(chuàng)建一個上面定義的分析類ObjectAnalyzer的對象
        System.out.println(objectAnalyzer.toString(squares)); // 分析ArrayList<Integer>對象的實際值

        Employee employee = new Employee("小明", "18", "愛好寫代碼", 1, "Java攻城獅", 100); // 分析自定義類Employee的對象的實際值
        System.out.println(objectAnalyzer.toString(employee));
    }
}

輸出如下

java.util.ArrayList[elementData=class java.lang.Object[]{
    java.lang.Integer[value=0][][],
    java.lang.Integer[value=1][][],
    java.lang.Integer[value=4][][],
    java.lang.Integer[value=9][][]
},size=4][modCount=4][][]
reflect.Employee[empNo=1,position=Java攻城獅,salary=100][name=小明,age=18,hobby=愛好寫代碼][]

其中ArrayList<Integer>打印了類名和5個元素的類型和值,Employee 打印了類名蒿秦,自己定義的3個基本類型的屬性的實際值烤镐,和父類Person的3個基本類型的屬性的實際值

需要注意的是,position棍鳖,age 是 protected 保護(hù)域炮叶,salary,hobby 是 private 私有域渡处,Java的安全機(jī)制只允許查看任意對象有哪些域镜悉,但是不允許讀取它們的值

程序中是通過 AccessibleObject.setAccessible(fields, true) 將域設(shè)置為了可訪問,取消了Java的執(zhí)行訪問檢查医瘫,因此可以訪問积瞒,如果不加會報異常 IllegalAccessException

小結(jié)一下,我們通過 setAccessible(true) 繞過了Java執(zhí)行訪問檢查登下,因此能夠訪問私有域茫孔,通過 Field.getType() 獲得了屬性的聲明類型,通過了 Field.get(Object obj) 獲得了該域?qū)傩缘膶嶋H值被芳,還有一個沒用上的 Field.set(Object obj, Object value) 設(shè)置域?qū)傩缘膶嶋H值

調(diào)用任意方法

上面我們已經(jīng)獲取了類的構(gòu)造器缰贝,方法,域畔濒,查看和設(shè)置了域的實際值剩晴,那么是不是還可以在調(diào)用對象的方法呢?嘿嘿侵状,又猜對了赞弥,機(jī)智,類的方法信息趣兄,獲取都獲取了绽左,當(dāng)然就要調(diào)用一下,來都來了

上面查看Field的實際值是通過 Field 類的 get() 方法艇潭,與之類似拼窥,Method 調(diào)用方法是通過 Method 類的 invoke 方法

調(diào)用任意方法相關(guān)的API

Method getMethod(String name, Class<?>... parameterTypes) 獲取指定的 Method,參數(shù) name 為要獲取的方法名蹋凝,parameterTypes 為指定方法的參數(shù)的 Class鲁纠,由于可能存在多個同名的重載方法,所以只有提供正確的 parameterTypes 才能準(zhǔn)確的獲取到指定的 Method

Object invoke(Object obj, Object... args) 執(zhí)行方法鳍寂,第一個參數(shù)執(zhí)行該方法的對象改含,如果是static修飾的類方法,則傳null即可迄汛;后面是傳給該方法執(zhí)行的具體的參數(shù)值

調(diào)用任意方法程序

public class MethodTableTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Employee employee = new Employee("小明", "18", "寫代碼", 1, "Java攻城獅", 100000);
        Method sayHello = employee.getClass().getMethod("sayHello");
        System.out.println(sayHello);   // 打印 sayHello 的方法信息
        sayHello.invoke(employee);      // 讓 employee 執(zhí)行 sayHello 方法

        double x = 3.0;
        Method square = MethodTableTest.class.getMethod("square", double.class);  // 獲取 MethodTableTest 的square方法
        double y1 = (double) square.invoke(null, x);    // 調(diào)用類方法 square 求平方捍壤,方法參數(shù) x 
        System.out.printf("square    %-10.4f -> %10.4f%n", x, y1);

        Method sqrt = Math.class.getMethod("sqrt", double.class);   // 獲取 Math 的 sqrt 方法
        double y2 = (double) sqrt.invoke(null, x);  // 調(diào)用類方法 sqrt 求根刃唤,方法參數(shù) x 
        System.out.printf("sqrt      %-10.4f -> %10.4f%n", x, y2);
    }

    // static靜態(tài)方法 計算乘方
    public static double square(double x) {
        return x * x;
    }
}

執(zhí)行結(jié)果

public void reflect.Employee.sayHello()
Hello, 我是 小明, 今年 18 歲, 愛好是寫代碼, 我目前的工作是Java攻城獅, 月入100000元

square    3.0000     ->     9.0000
sqrt      3.0000     ->     1.7321

相信大家都看懂啦,通過 getMethod() 獲取指定的 Method白群,再調(diào)用 Method.invoke() 執(zhí)行該方法

反射的優(yōu)缺點

此段引用自 CyC2018/CS-Notes

反射的優(yōu)點:

  • 可擴(kuò)展性 :應(yīng)用程序可以利用全限定名創(chuàng)建可擴(kuò)展對象的實例尚胞,來使用來自外部的用戶自定義類。

  • 類瀏覽器和可視化開發(fā)環(huán)境 :一個類瀏覽器需要可以枚舉類的成員帜慢×眩可視化開發(fā)環(huán)境(如 IDE)可以從利用反射中可用的類型信息中受益,以幫助程序員編寫正確的代碼粱玲。

  • 調(diào)試器和測試工具 : 調(diào)試器需要能夠檢查一個類里的私有成員躬柬。測試工具可以利用反射來自動地調(diào)用類里定義的可被發(fā)現(xiàn)的 API 定義,以確保一組測試中有較高的代碼覆蓋率抽减。

反射的缺點:

盡管反射非常強(qiáng)大允青,但也不能濫用。如果一個功能可以不用反射完成卵沉,那么最好就不用颠锉。在我們使用反射技術(shù)時,下面幾條內(nèi)容應(yīng)該牢記于心史汗。

  • 性能開銷 :反射涉及了動態(tài)類型的解析琼掠,所以 JVM 無法對這些代碼進(jìn)行優(yōu)化。因此停撞,反射操作的效率要比那些非反射操作低得多瓷蛙。我們應(yīng)該避免在經(jīng)常被執(zhí)行的代碼或?qū)π阅芤蠛芨叩某绦蛑惺褂梅瓷洹?/p>

  • 安全限制 :使用反射技術(shù)要求程序必須在一個沒有安全限制的環(huán)境中運(yùn)行。如果一個程序必須在有安全限制的環(huán)境中運(yùn)行戈毒,如 Applet艰猬,那么這就是個問題了。

  • 內(nèi)部暴露 :由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法)埋市,所以使用反射可能會導(dǎo)致意料之外的副作用冠桃,這可能導(dǎo)致代碼功能失調(diào)并破壞可移植性。反射代碼破壞了抽象性恐疲,因此當(dāng)平臺發(fā)生改變的時候腊满,代碼的行為就有可能也隨著變化。

參考:
《Java核心技術(shù)》卷一
https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20%E5%9F%BA%E7%A1%80.md#%E4%B8%83%E5%8F%8D%E5%B0%84

后記

歡迎評論培己、轉(zhuǎn)發(fā)、分享胚泌,您的支持是我最大的動力

更多內(nèi)容可訪問我的個人博客:http://laijianfeng.org

關(guān)注【小旋鋒】微信公眾號省咨,及時接收博文推送

關(guān)注_小旋鋒_微信公眾號
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市玷室,隨后出現(xiàn)的幾起案子零蓉,更是在濱河造成了極大的恐慌笤受,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敌蜂,死亡現(xiàn)場離奇詭異箩兽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)章喉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門汗贫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秸脱,你說我怎么就攤上這事落包。” “怎么了摊唇?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵咐蝇,是天一觀的道長。 經(jīng)常有香客問我巷查,道長有序,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任岛请,我火速辦了婚禮笔呀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘髓需。我一直安慰自己许师,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布僚匆。 她就那樣靜靜地躺著微渠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咧擂。 梳的紋絲不亂的頭發(fā)上逞盆,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音松申,去河邊找鬼云芦。 笑死,一個胖子當(dāng)著我的面吹牛贸桶,可吹牛的內(nèi)容都是我干的舅逸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皇筛,長吁一口氣:“原來是場噩夢啊……” “哼琉历!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤旗笔,失蹤者是張志新(化名)和其女友劉穎彪置,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝇恶,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拳魁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撮弧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潘懊。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖想虎,靈堂內(nèi)的尸體忽然破棺而出卦尊,到底是詐尸還是另有隱情,我是刑警寧澤舌厨,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布岂却,位于F島的核電站,受9級特大地震影響裙椭,放射性物質(zhì)發(fā)生泄漏躏哩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一揉燃、第九天 我趴在偏房一處隱蔽的房頂上張望扫尺。 院中可真熱鬧,春花似錦炊汤、人聲如沸正驻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑曙。三九已至,卻和暖如春迈倍,著一層夾襖步出監(jiān)牢的瞬間伤靠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工啼染, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留宴合,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓迹鹅,卻偏偏與公主長得像卦洽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子徒欣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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