java基礎(chǔ)_反射

什么是反射

反射是一個很牛的功能纫骑,能夠在程序運行時修改程序的行為蝎亚。但是反射是非常規(guī)手段,反射有風(fēng)險先馆,應(yīng)用需謹(jǐn)慎发框。

把程序代碼比作一輛車,因為Java是面向?qū)ο蟮恼Z言,車子有自己的型號梅惯、車牌號宪拥、發(fā)動機,有倒車铣减、泊車等功能她君。

正常情況下,我們需要為車子配備一個司機葫哗,然后按照行為準(zhǔn)則規(guī)范駕駛缔刹。

反射是非常規(guī)手段,正常行駛的時候劣针,車子需要司機的駕駛校镐。但是反射不需要,因為它就是車子的自動駕駛捺典。(非常規(guī)嘛)

  • 反射技術(shù)通常被用來檢測和改變應(yīng)用程序在 Java 虛擬機中的行為表現(xiàn)鸟廓。它是一個相對而言比較高級的技術(shù),通常它應(yīng)用的前提是開發(fā)者本身對于 Java 語言特性有很強的理解的基礎(chǔ)上襟己。值得說明的是引谜,反射是一種強有力的技術(shù)特性,因此可以使得應(yīng)用程序突破一些藩籬擎浴,執(zhí)行一些常規(guī)手段無法企及的目的员咽。

Class

Java是面向?qū)ο蟮恼Z言,基本上是以類為基礎(chǔ)構(gòu)造了整個程序退客。反射中要求提供的規(guī)格說明書就是類的規(guī)格說明書骏融。

  • 其中小寫的class是定義類的關(guān)鍵字。

  • Class本質(zhì)是一個類萌狂。

    public final class Class<T> implements java.io.Serializable,
                                  GenericDeclaration,
                                  Type,
                                  AnnotatedElement,
                                  TypeDescriptor.OfField<Class<?>>,
                                  Constable {}
    
    
  • Class就是一個對象档玻,運行在Java虛擬機中的類和接口。

Class的獲取

反射的入口是Class茫藏,但是反射中Class是沒有公開的構(gòu)造函數(shù)误趴,不能通過關(guān)鍵字new來獲取Class的對象,所以就有如下3種獲取Class的方式

  • Object.getClass()

    • 對于一個對象而言务傲,如果能訪問凉当,那么調(diào)用getClass()方法就可以獲取到它對應(yīng)的Class對象。
    • 如下這種方法不適合基本類型 :int float
    public class Car {
    }
    
    public class CarTest {
        public static void main(String[] args) {
            Car car = new Car();
            Class aClass = car.getClass();
    
        }
    }
    
  • 通過 .class標(biāo)識

    • 對于上述方法售葡,Car是一個類看杭,car是一個對象,通過car.getClass()就獲取到Car這個類的Class對象挟伙。
    • 如果不想通過類的對象的getClass()方式去獲取楼雹,就可以通過 ‘.class的標(biāo)識’來獲取。
    public class CarTest {
        public static void main(String[] args) {
           
            Class<Car> carClass = Car.class;
            Class<Integer> integerClass = int.class;
            Class<String> stringClass = String.class;
    
        }
    }
    
  • 通過 Class.forName()方法

    • 在Android中,Google的Android工程師把一些類加上了@hide注解贮缅,這些被@hide注解修飾的類就沒有出現(xiàn)在SDK中榨咐。
    • 我們要獲取到這個在當(dāng)前開發(fā)環(huán)境中不存在的類的Class對象,就可以通過此方法來獲取到谴供。
    • 如下代碼中块茁,"com.dashingqi.reflect.Car"就是Car類的全限定類名稱,包括 包名+類名桂肌。
    • 如果找不到該類的時候数焊,會拋出ClassNotFoundException,因為如果這個沒有加載到JVM中的時候崎场,需要告訴我們昌跌。
    public class CarTest {
        public static void main(String[] args) {
           
            try {
                Class.forName("com.dashingqi.reflect.Car");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    

Class的內(nèi)容清單

Class的名字獲取

Class對象也有名字,涉及到API如下

  • Class.getName()
  • Class.getSimpleName()
  • Class.getCanonicalName()

因為Class是一個入口,它代表引用孕暇、基本數(shù)據(jù)類型以及數(shù)組對象购撼,所以獲取它們的方式也是不同的。

getName

當(dāng)Class代表一個引用時

  • getName放回的是一個二進(jìn)制形式的字符串 “com.dashingqi.reflect.Car”

當(dāng)Class代表是一個基本數(shù)據(jù)類型的時候

  • getName返回的就是它們的關(guān)鍵字 比如 int.class的名字就是int

當(dāng)Class代表是基本數(shù)據(jù)類型的數(shù)組時(int [] [] [] 這樣的3維數(shù)組)

  • getName返回的是[[[I 這樣的字符串

  • Java在這個地方有相應(yīng)的規(guī)則亭姥,在元素類型的前面添加相應(yīng)數(shù)量的[符號,用[符號的數(shù)量來表示數(shù)組的維度,對于基本數(shù)據(jù)類型都有相應(yīng)的編碼污呼,都是大寫的字母;規(guī)則如下

元素類型 編碼
boolean Z
byte B
char C
Double D
Float F
int I
Long L
short S
類或者接口 L類名;(是有;的喲)
  • 代碼操作一下

    public class CarTest {
        public static void main(String[] args) {
    
            try {
    
                Class<?> aClass = Class.forName("getclassinstance.Car");
                Class<Integer> integerClass = int.class;
                Class<Float> floatClass = float.class;
                Class<? extends int[]> aClass1 = new int[]{}.getClass();
                Class<? extends Car[]> aClass2 = new Car[]{}.getClass();
    
                  //代表一個引用
                System.out.println("aClass = " + aClass.getName());
                //基本數(shù)據(jù)類型
                System.out.println("integerClass = " + integerClass.getName());
                System.out.println("floatClass=" + floatClass.getName());
                  //基本數(shù)據(jù)類型的數(shù)組
                System.out.println("aClass1=" + aClass1.getName());
               //類或者接的數(shù)組
                System.out.println("aClass2=" + aClass2.getName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    //運行結(jié)果如下
    
    aClass = getclassinstance.Car
    integerClass = int
    floatClass=float
    aClass1=[I
    aClass2=[Lgetclassinstance.Car;
    
    
getSimpleName()
  • 先看一個嵌套類

    public class ClassA {
    
        public static class ClassB{
    
        }
    }
    
    public class ClassTest {
    
        public static void main(String[] args) {
            ClassA.ClassB classB = new ClassA.ClassB();
            String name = classB.getClass().getName();
            String simpleName = classB.getClass().getSimpleName();
            System.out.println("name = " + name);
            System.out.println("simpleName = " + simpleName);
        }
    }
    
    //運行結(jié)果
    name = getSimpleName.ClassA$ClassB
    simpleName = ClassB
    
    • 因為是內(nèi)部類包竹,通過getName得到是的二進(jìn)制形式的全限定類名燕酷,并且類名前面還有一個$符號。
    • getSimpleName()則返回一個ClassB,去掉了包名限定周瞎。
  • 當(dāng)數(shù)組的Class獲取simpleName的時候苗缩,不同于getName,simpleName不在前面加[,而是在后面加[]

    public class ClassTest {
    
        public static void main(String[] args) {
    
            Class<? extends ClassA[]> aClass = new ClassA[]{}.getClass();
            String name1 = aClass.getName();
            System.out.println("name1 = " + name1);
            String simpleName1 = aClass.getSimpleName();
            System.out.println("simpleName1=" + simpleName1);
        }
    }
    
    //運行結(jié)果如下
    name1 = [LgetSimpleName.ClassA;
    simpleName1=ClassA[]
    
  • 對于匿名內(nèi)部類來說声诸,通過getSimpleName()獲取的是一個空的字符串

    public class ClassTest {
    
        public static void main(String[] args) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
    
                }
            };
    
            System.out.println("name = " + runnable.getClass().getName());
            System.out.println("simple name = " + runnable.getClass().getSimpleName());
        }
    }
    
    // 運行結(jié)果如下
    name = getSimpleName.ClassTest$1
    simple name = 
    
getCanonicalName

Canonial 是官方酱讶、標(biāo)準(zhǔn)的意思,那么該方法就是獲取Class對象的官方名字彼乌。

這個CanonicalName是官方規(guī)定的泻肯,如果Class對象沒有canonicalName的話就返回一個null

getCanonicalName()是getName()與getSimpleName()的結(jié)合

  • getCanonicalName()返回的也是全限定類名,但是對于內(nèi)部類來說慰照,不用$開頭灶挟,而是用.。

  • getCanonicalName()對于數(shù)組的Class毒租,同getSimpleName()也是在后面加[]稚铣。

  • getCanonicalName()不同于getSimpleName()的是,如果不存在canonicalName就返回null而不是空字符串。

  • 局部類和匿名內(nèi)部類不存在canonicalName榛泛。

  • 代碼

    public class ClassTest {
    
        public static void main(String[] args) {
            ClassA.ClassB classB = new ClassA.ClassB();
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
    
                }
            };
            String canonicalName = classB.getClass().getCanonicalName();
            String canonicalName1 = new ClassA.ClassB[][][]{}.getClass().getCanonicalName();
    
            System.out.println("canonicalName = " + canonicalName);
            System.out.println("canonicalName1 = " + canonicalName1);
            System.out.println("run name = " + runnable.getClass().getCanonicalName());
    
    
            /**
             * local是局部類
             */
            class Local {
            }
            ;
            String name = Local.class.getName();
            String simpleName = Local.class.getSimpleName();
            String canonicalName2 = Local.class.getCanonicalName();
    
            System.out.println("local name = " + name);
            System.out.println("local simple name = " + simpleName);
            System.out.println("local canonical name = " + canonicalName2);
    
        }
    }
    
    //運行結(jié)果
    
    canonicalName = getSimpleName.ClassA.ClassB
    canonicalName1 = getSimpleName.ClassA.ClassB[][][]
    run name = null
    local name = getSimpleName.ClassTest$1Local
    local simple name = Local
    local canonical name = null
    

Class獲取修飾符

Java開發(fā)中定義一個類蝌蹂,往往要通過很多修飾符來配合使用的。大致分為如下四類

  • 用來限制作用域:publish曹锨、protected孤个、private
  • 用來標(biāo)記為靜態(tài):static
  • 注解
  • 用來提示子類來復(fù)寫:abstract

Java反射提供了API去獲取這些修飾符

  • 定義一個被 abstract 和 publish 修飾的類

    public  abstract class DemoA {
    }
    
  • 我們現(xiàn)在要提取這些修飾符,只需要調(diào)用 Class.getModifiers()

    public class Demo {
        public static void main(String[] args) {
              /**
              * public native int getModifiers();
              * 是一個native層的方法
              */
            int modifiers = DemoA.class.getModifiers();
            System.out.println("modifiers = " + modifiers);
            System.out.println("modifiers_str = "+ Modifier.toString(modifiers));
        }
    }
    
    
  • 運行結(jié)果如下

    modifiers = 1025
    modifiers_str = public abstract
    
    
  • Java工程師考慮到了位運算沛简,用一個int數(shù)值來記錄所有的修飾符齐鲤,然后不同的位對應(yīng)不同的修飾符,這些修飾符都定義在Modifier類中椒楣。

    public static String toString(int mod) {
            StringJoiner sj = new StringJoiner(" ");
    
            if ((mod & PUBLIC) != 0)        sj.add("public");
            if ((mod & PROTECTED) != 0)     sj.add("protected");
            if ((mod & PRIVATE) != 0)       sj.add("private");
    
            /* Canonical order */
            if ((mod & ABSTRACT) != 0)      sj.add("abstract");
            if ((mod & STATIC) != 0)        sj.add("static");
            if ((mod & FINAL) != 0)         sj.add("final");
            if ((mod & TRANSIENT) != 0)     sj.add("transient");
            if ((mod & VOLATILE) != 0)      sj.add("volatile");
            if ((mod & SYNCHRONIZED) != 0)  sj.add("synchronized");
            if ((mod & NATIVE) != 0)        sj.add("native");
            if ((mod & STRICT) != 0)        sj.add("strictfp");
            if ((mod & INTERFACE) != 0)     sj.add("interface");
    
            return sj.toString();
        }
    
        /*
         * Access modifier flag constants from tables 4.1, 4.4, 4.5, and 4.7 of
         * <cite>The Java&trade; Virtual Machine Specification</cite>
         */
    
        /**
         * The {@code int} value representing the {@code public}
         * modifier.
         */
        public static final int PUBLIC           = 0x00000001;
    
        /**
         * The {@code int} value representing the {@code private}
         * modifier.
         */
        public static final int PRIVATE          = 0x00000002;
    
        /**
         * The {@code int} value representing the {@code protected}
         * modifier.
         */
        public static final int PROTECTED        = 0x00000004;
    
        /**
         * The {@code int} value representing the {@code static}
         * modifier.
         */
        public static final int STATIC           = 0x00000008;
    
        /**
         * The {@code int} value representing the {@code final}
         * modifier.
         */
        public static final int FINAL            = 0x00000010;
    
        /**
         * The {@code int} value representing the {@code synchronized}
         * modifier.
         */
        public static final int SYNCHRONIZED     = 0x00000020;
    
        /**
         * The {@code int} value representing the {@code volatile}
         * modifier.
         */
        public static final int VOLATILE         = 0x00000040;
    
        /**
         * The {@code int} value representing the {@code transient}
         * modifier.
         */
        public static final int TRANSIENT        = 0x00000080;
    
        /**
         * The {@code int} value representing the {@code native}
         * modifier.
         */
        public static final int NATIVE           = 0x00000100;
    
        /**
         * The {@code int} value representing the {@code interface}
         * modifier.
         */
        public static final int INTERFACE        = 0x00000200;
    
        /**
         * The {@code int} value representing the {@code abstract}
         * modifier.
         */
        public static final int ABSTRACT         = 0x00000400;
    
        /**
         * The {@code int} value representing the {@code strictfp}
         * modifier.
         */
        public static final int STRICT           = 0x00000800;
    
        // Bits not (yet) exposed in the public API either because they
        // have different meanings for fields and methods and there is no
        // way to distinguish between the two in this class, or because
        // they are not Java programming language keywords
        static final int BRIDGE    = 0x00000040;
        static final int VARARGS   = 0x00000080;
        static final int SYNTHETIC = 0x00001000;
        static final int ANNOTATION  = 0x00002000;
        static final int ENUM      = 0x00004000;
        static final int MANDATED  = 0x00008000;
    

獲取Class的成員

獲取Filed

獲取到指定名字的屬性API

  • getDeclaredField()方法獲取的是Class中任何修飾符修飾的屬性名字给郊,name

    public Field getDeclaredField(String name)
            throws NoSuchFieldException, SecurityException {
        }
    
  • getField(String name):當(dāng)前 name是被publish修飾 就能獲取到,否則拋出異常

    @CallerSensitive
        public Field getField(String name)
           
        }
    

獲取到所有屬性

  • getDeclaredFieleds()獲取到所有屬性捧灰,但不包括從父類繼承下來的屬性

     public Field[] getDeclaredFields() throws SecurityException {
           
        }
    
  • getFields() 獲取到自身的所有public屬性淆九,包括從父類繼承下來的

    public Field[] getFields() throws SecurityException {
            
        }
    
  • 代碼實操

    public class MainTest {
    
        public static void main(String[] args) {
            Class<Son> sonClass = Son.class;
    
            try {
                //獲取到屬性名字,
                Field a = sonClass.getDeclaredField("d");
                System.out.println("getDeclaredField name = " + a.getName());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
                System.out.println("getDeclaredField = " + e.getMessage());
            }
    
            try {
                //獲取 publish修飾的屬性名字毛俏,如果自己找不到炭庙,會去查找父類中的
                Field c = sonClass.getField("c");
                System.out.println("getField name = " + c.getName());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
    
            //獲取到所有屬性的名字,不包括父類的
            Field[] declaredFields = sonClass.getDeclaredFields();
            for (Field f : declaredFields){
                System.out.println("getDeclaredFields name =  "+f.getName());
            }
    
            //獲取到自身所有的publish修飾的屬性名字煌寇,包括從父類繼承下來的
            Field[] fields = sonClass.getFields();
            for (Field field : fields){
                System.out.println("getFields name = "+field.getName());
            }
        }
    }
    
    //運行結(jié)果如下 
    
    getDeclaredField name = d
    java.lang.NoSuchFieldException: c
      at java.base/java.lang.Class.getField(Class.java:2004)
      at get_class_filed.MainTest.main(MainTest.java:21)
    getDeclaredFields name =  c
    getDeclaredFields name =  d
    getDeclaredFields name =  f
    getFields name = b
    
獲取Method

類獲取接口中的方法對應(yīng)到Class就是Method

  • getDeclaredMethod():獲取到任何修飾符修飾的方法焕蹄,不包括從父類繼承到的

     public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
           
        }
    
  • getMethod():獲取到任何publish修飾符修飾的方法,包括從父類中繼承得到的

    public Method getMethod(String name, Class<?>... parameterTypes)
           
        }
    
  • getDeclaredMethods():獲取所有方法的名字阀溶,不包括從父類繼承得到的

    public Method[] getDeclaredMethods() throws SecurityException {
           
        }
    
  • getMethods():獲取所有publish修飾方法的名字腻脏,包括從父類繼承的來的(包括Object)

    public Method[] getMethods() throws SecurityException {
        }
    
  • 代碼演示

    public class MainTest {
    
        public static void main(String[] args) {
            Class<Son> sonClass = Son.class;
    
            try {
                Method methodC = sonClass.getDeclaredMethod("methodF", null);
                System.out.println("methodC = " + methodC.getName());
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
    
            try {
                Method methodA = sonClass.getMethod("methodF", null);
                System.out.println("methodA name = " + methodA.getName());
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
    
            Method[] declaredMethods = sonClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println("getDeclaredMethods name =  " + method.getName());
            }
    
            Method[] methods = sonClass.getMethods();
            for (Method method : methods) {
                System.out.println(" getMethods name = " + method.getName());
            }
        }
    }
    
    //運行結(jié)果如下 
    
    methodC = methodF
    java.lang.NoSuchMethodException: get_class_filed.Son.methodF()
      at java.base/java.lang.Class.getMethod(Class.java:2113)
      at get_class_filed.MainTest.main(MainTest.java:20)
    get_class_filed.Son.methodF()
    getDeclaredMethods name =  methodC
    getDeclaredMethods name =  methodF
    getDeclaredMethods name =  methodD
    getDeclaredMethods name =  methodG
     getMethods name = methodC
     getMethods name = methodD
     getMethods name = methodA
     getMethods name = wait
     getMethods name = wait
     getMethods name = wait
     getMethods name = equals
     getMethods name = toString
     getMethods name = hashCode
     getMethods name = getClass
     getMethods name = notify
     getMethods name = notifyAll
     
    
Constructor

Java把構(gòu)造器從Method中拎出來,用Constructor來表示

Constructor不能從父類中繼承银锻,所以就沒有辦法通過getConstructor()來獲取到父類的Constructor

  • getDeclaredConstructor():獲取任何修飾符修飾的構(gòu)造方法名字

    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
            throws NoSuchMethodException, SecurityException
        }
    
  • getConstructor():獲取當(dāng)前Class對象中 publish修飾的構(gòu)造方法的名字

    public Constructor<T> getConstructor(Class<?>... parameterTypes)
            throws NoSuchMethodException, SecurityException{
        }
    
  • getDeclaredConstructors():獲取所有構(gòu)造方法名字

    public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
    }
    
  • getConstructors():獲取所有publish修飾的構(gòu)造方法名字

    public Constructor<?>[] getConstructors() throws SecurityException {
          
    }
    

Field的操作

我們在類中定義字段時永品,通常是這樣的

其中 c、d击纬、f腐碱、g、demo這些變量都是屬性掉弛,在反射機制中映射到Class對象中都是Field

它們要么是8中基本數(shù)據(jù)類型症见,或者是引用,所有的引用都是Object的子類殃饿。

public class Son extends Father {
    int c;

    private String d;

    protected float f;

    public int g;

    Demo demo;
}
Field類型的獲取
  • 獲取Field類型有2種方法(首先獲取到Class對象種的Field谋作,通過Field的一下兩種方法獲取Field類型)

    public Type getGenericType() {
        }
    public Class<?> getType() {
        }
    //兩種方法返回參數(shù)不一樣,getGenericType()可以獲取到泛型類型
    
  • 代碼實操

    public class Son extends Father {
        int c;
    
        private String d;
    
        protected float f;
    
        public int g;
    
        Demo demo;
    
       public List<Demo> mDemoList;
    
        public Map<Integer, Integer> maps;
    }
    
    public static void main(String[] args) {
            Class<Son> sonClass = Son.class;
    
            Field[] fields = sonClass.getFields();
            for (Field field : fields) {
                System.out.println("getName == " + field.getName());
                System.out.println("getType = " + field.getType());
                System.out.println("getGenericType = " + field.getGenericType());
                System.out.println("======================");
            }
    }
    //運行結(jié)果如下
    getName == g
    getType = int
    getGenericType = int
    ======================
    getName == mDemoList
    getType = interface java.util.List
    getGenericType = java.util.List<get_class_filed.Demo>
    ======================
    getName == maps
    getType = interface java.util.Map
    getGenericType = java.util.Map<java.lang.Integer, java.lang.Integer>
    ======================
    getName == b
    getType = int
    getGenericType = int
    ======================
      
    // 我們看到 把自己Class對象種的publish屬性和父類中的publish修飾的屬性都給找到了
    // getGenericType()把泛型的類型都給打印出來了  乎芳,相比較與getType 更詳細(xì)
    
Field修飾符的獲取
  • 通過getModifiers()方法就可以獲取

    public int getModifiers() {
            return modifiers;
        }
    
  • 代碼實操

    public static void main(String[] args) {
            Class<Son> sonClass = Son.class;
    
            Field[] fields = sonClass.getFields();
            for (Field field : fields) {
                System.out.println("getModifiers = "+ Modifier.toString(field.getModifiers()));
                System.out.println("======================");
            }
      //運行結(jié)果如下
    getModifiers = public
    ======================
    getModifiers = public
    ======================
    getModifiers = public
    ======================
    getModifiers = public
    ======================
    
Field內(nèi)容的讀取與賦值

這也是反射機制中對Filed主要的目的

Filed定義了一些列set和get方法來設(shè)置和獲取不同類型的值

  • get

    public Object get(Object obj);
    
    public int getInt(Object obj);
    
    public long getLong(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public float getFloat(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public short getShort(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public double getDouble(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public char getChar(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public byte getByte(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public boolean getBoolean(Object obj)
            throws IllegalArgumentException, IllegalAccessException
    
  • set

    public void set(Object obj, Object value);
    
    public void getInt(Object obj,int value);
    
    public void getLong(Object obj,long value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getFloat(Object obj,float value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getShort(Object obj,short value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getDouble(Object obj,double value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getChar(Object obj,char value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getByte(Object obj,byte b)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void getBoolean(Object obj,boolean b)
            throws IllegalArgumentException, IllegalAccessException
    
  • 其中Object就是類的實例引用遵蚜,Class本身不對成員進(jìn)行存儲帖池,它只提供檢索,所以需要Field吭净、Method睡汹、Constructor對象來承載這些成員,所以針對成員的操作時寂殉,一般需要為成員指定類的實例引用囚巴,表示這個成員屬于哪個Object的,是為了精準(zhǔn)定位友扰。

  • 代碼實操

    public class MainTest {
    
        public static void main(String[] args) {
    
            Son son = new Son();
            son.g = 10;
    
            System.out.println("son.g = " + son.g);
            //Class本身不對成員做存儲
            Class<Son> sonClass = Son.class;
    
            try {
                Field g = sonClass.getField("g");
                int anInt = g.getInt(son);
                System.out.println(" reflection son.g = " + anInt);
                g.setInt(son, 16);
                System.out.println("son.g = " + son.g);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
      
      //運行結(jié)果如下
    son.g = 10
     reflection son.g = 10
    son.g = 16
    
  • 上述代碼是獲取publish修飾的屬性彤叉,這回找個private修飾的屬性 h

    Son son = new Son();
            son.setH(10);
    
            System.out.println("son.g = " + son.getH());
            //Class本身不對成員做存儲
            Class<Son> sonClass = Son.class;
    
            try {
                Field h = sonClass.getDeclaredField("h");
                int anInt = h.getInt(son);
                System.out.println(" reflection son.g = " + anInt);
                h.setInt(son, 16);
                System.out.println("son.g = " + son.getH());
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
    //運行結(jié)果如下
    son.g = 10
    java.lang.IllegalAccessException: class get_class_filed.MainTest cannot access a member of class get_class_filed.Son with modifiers "private"
      at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:376)
      at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639)
      at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
      at java.base/java.lang.reflect.Field.getInt(Field.java:592)
      at get_class_filed.MainTest.main(MainTest.java:21)
     //報錯了
    
  • 這是因為反射過程操作了private屬性,如果要消除異常加如下代碼

    h.setAccessible(true)
    
    • 運行結(jié)果如下

      son.g = 10
       reflection son.g = 10
      son.g = 16
      

Method操控

Method對應(yīng)普通類的方法村怪;

一般普通類的構(gòu)成

public int add(int a, int b){

? return a+b;

}

主要包括如下幾個方面

  1. 方法名
  2. 方法的返回值類型
  3. 方法的參數(shù)
  4. 方法的修飾符
  5. 方法拋出的異常

Java的反射中針對Method 有相關(guān)操作API的來提取相關(guān)元素

獲取Method方法名
  • 獲取方法名字的通過getName()方法

  • 代碼實操

    public class MainClass {
    
        public static void main(String[] args) {
    
            Class<Car> carClass = Car.class;
            Method[] declaredMethods = carClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println("method name = " + method.getName());
            }
        }
    }
    //運行結(jié)果如下
    method name = getCarName
    method name = setCarName
    method name = getCarId
    method name = setCarId
    
Method獲取方法參數(shù)
  • 通過 getParameters()來獲取

    public Parameter[] getParameters() {}
    
  • 返回是一個Parameter的數(shù)組秽浇,在反射中Parameter對象就是用來映射方法中的參數(shù),經(jīng)常使用的方法有如下

    // 獲取參數(shù)名字
    public String getName() {}
    
    // 獲取參數(shù)類型
    public Class<?> getType() {}
    
    // 獲取參數(shù)的修飾符
    public int getModifiers() {}
    
  • 當(dāng)然有時我們不需要參數(shù)的名字甚负,只需要參數(shù)的類型就可以柬焕,通過Method下面的方法獲取

    //獲取所有參數(shù)的類型
    public Class<?>[] getParameterTypes() {
        }
    //獲取所有參數(shù)的類型,包括泛型    
    public Type[] getGenericParameterTypes() {
            return super.getGenericParameterTypes();
        }
    
  • 代碼實操

    public class MainClass {
    
        public static void main(String[] args) {
    
            Class<Car> carClass = Car.class;
            Method[] declaredMethods = carClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println("method name = " + method.getName());
    
                //獲取到方法中的參數(shù)
                Parameter[] parameters = method.getParameters();
    
                for (Parameter parameter : parameters) {
                    //獲取到參數(shù)的名字和參數(shù)和參數(shù)的修飾符
                    System.out.println("parameter = " + parameter.getName() + " " + parameter.getType().getName() + " Modifiers name = " + Modifier.toString(parameter.getModifiers()));
                }
    
                //獲取到所有參數(shù)的類型
                Class<?>[] pTypes = method.getParameterTypes();
                System.out.println("method para types");
                for (Class type : pTypes) {
                    System.out.println(" type name = " + type.getName());
                }
    
                //獲取到所有參數(shù)的類型梭域,包括泛型
                Type[] genericParameterTypes = method.getGenericParameterTypes();
                for (Type type : genericParameterTypes) {
                    System.out.println(" generic type name = " + type.getTypeName());
                }
    
            }
        }
    }
    
    //運行結(jié)果如下
    method name = toString
    method para types
    method name = test
    parameter = arg0 [Ljava.lang.String; Modifiers name = 
    parameter = arg1 java.util.List Modifiers name = 
    parameter = arg2 java.util.HashMap Modifiers name = 
    method para types
     type name = [Ljava.lang.String;
     type name = java.util.List
     type name = java.util.HashMap
     generic type name = java.lang.String[]
     generic type name = java.util.List<java.lang.String>
     generic type name = java.util.HashMap<java.lang.String, java.lang.Integer>
    method name = drive
    method para types
    
獲取Method的返回值類型
  • 與如下兩種方法

    // 獲取返回值類型
    public Class<?> getReturnType() {}
    
    
    // 獲取返回值類型包括泛型
    public Type getGenericReturnType() {}
    
    
  • 代碼實操

    • getReturnType()

      public class MainClass {
      
          public static void main(String[] args) {
      
              Class<Car> carClass = Car.class;
              Method[] declaredMethods = carClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("method name = " + method.getName());
      
                  Class<?> returnTypes = method.getReturnType();
                  String name = returnTypes.getName();
                  System.out.println("return type name = " + name);
      
              }
          }
      }
      
      //運行結(jié)果如下
      method name = toString
      return type name = java.lang.String
      method name = test
      return type name = void
      method name = drive
      return type name = java.lang.String
      method name = getNames
      return type name = java.util.List
      
    • getGenericReturnType();

      public class MainClass {
      
          public static void main(String[] args) {
      
              Class<Car> carClass = Car.class;
              Method[] declaredMethods = carClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("method name = " + method.getName());
      
                  Type genericReturnType = method.getGenericReturnType();
                  System.out.println("genericReturnType name = " + genericReturnType.getTypeName());
      
              }
          }
      }
      //運行結(jié)果如下
      method name = toString
      genericReturnType name = java.lang.String
      method name = test
      genericReturnType name = void
      method name = drive
      genericReturnType name = java.lang.String
      method name = getNames
      genericReturnType name = java.util.List<java.lang.String>
      
Method獲取修飾符
  • 通過如下方法獲取

    public int getModifiers() {}
    
  • 代碼實操

    public class MainClass {
    
        public static void main(String[] args) {
    
            Class<Car> carClass = Car.class;
            Method[] declaredMethods = carClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println("method name = " + method.getName());
    
                int modifiers = method.getModifiers();
                System.out.println("modifiers name = " + Modifier.toString(modifiers));
            }
        }
    }
    //運行結(jié)果如下
    method name = toString
    modifiers name = public
    method name = test
    modifiers name = public
    method name = drive
    modifiers name = public
    method name = getNames
    modifiers name = private
    
Method獲取異常類型
  • 通過如下方法獲取

    public Class<?>[] getExceptionTypes() {}
    
    public Type[] getGenericExceptionTypes() {}
    
Method方法的執(zhí)行

這應(yīng)該是整個反射機制的核心內(nèi)容击喂,我們很多時候運用反射目的就是為了以非常規(guī)手段執(zhí)行Method

  • Method類型,是如下這個方法

    public Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException,
               InvocationTargetException
        {}
    
  • Method在調(diào)用invoke()方法的時候碰辅,存在許多細(xì)節(jié)

    • invoke()方法中的第一個參數(shù)obj是Method依附的Class對應(yīng)的類的實例,如果這個方法是一個靜態(tài)方法介时,那么obj為null没宾,第二個參數(shù)args對應(yīng)的是方法的參數(shù)
    • invoke()方法返回的是Object,所以實際上執(zhí)行的時候要進(jìn)行強制轉(zhuǎn)換沸柔。
    • 在對Method的invoke()方法調(diào)用的時候循衰,如果方法本身會拋出異常,那么這個異常就會進(jìn)過包裝褐澎,由Method統(tǒng)一拋出InvocationTargetException会钝。而通過InvocationTargetException.getCause()可以獲取真正的異常。
  • 代碼實操

    public class TestMethod {
    
        public static void testStatic() {
            System.out.println("test static");
        }
    
        private int add(int a, int b) {
            return a + b;
        }
    
        public void testException() throws IllegalAccessException {
            throw new IllegalAccessException("You have some problem");
        }
    }
    
    public class MainTest {
        public static void main(String[] args) {
            Class<TestMethod> testMethodClass = TestMethod.class;
    
            try {
                Method testStatic = testMethodClass.getMethod("testStatic", null);
                //測試靜態(tài)方法
                testStatic.invoke(null, null);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            TestMethod testMethod = new TestMethod();
    
            try {
                Method add = testMethodClass.getDeclaredMethod("add", int.class, int.class);
                //通過這行代碼才能訪問private修飾的method
                add.setAccessible(true);
                int addValue = (int) add.invoke(testMethod, 3, 4);
                System.out.println("addValue = " + addValue);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            try {
                Method testException = testMethodClass.getMethod("testException", null);
                testException.invoke(testMethod, null);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                System.out.println("testException occur some error,Error type is :" + e.getCause().getClass().getName());
                System.out.println("Error message is : " + e.getCause().getMessage());
            }
        }
    }
    
    // 運行結(jié)果如下
    test static
    addValue = 7
    java.lang.reflect.InvocationTargetException
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base/java.lang.reflect.Method.invoke(Method.java:567)
      at method_invoke.MainTest.main(MainTest.java:40)
    Caused by: java.lang.IllegalAccessException: You have some problem
      at method_invoke.TestMethod.testException(TestMethod.java:14)
      ... 5 more
    
Constructor的操控

構(gòu)造器也叫做構(gòu)造方法工三,在Java的反射機制中把Constructor與Method分離開來迁酸,單獨用Constructor這個類來表示,Constructor同Method差不多俭正,但是它的特別的地方在于它能夠創(chuàng)建一個對象奸鬓。

Java的反射機制中有兩種方法可以用來創(chuàng)建類的對象實例:Class.newInstance()和Constructor.newInstance() 官方文檔建議開發(fā)者使用第二種方法 原因如下

  • Class.newInstance()只能調(diào)用無參的構(gòu)造方法,而Constructor.newInstance()可以調(diào)用任意的構(gòu)造方法掸读。

  • Class.newInstance()通過構(gòu)造方法直接拋出異常串远,而Constructor.newInstance()可以把拋出來的異常包裝到InvocationtargetException里面去宏多,這個是和Method一致的。

  • Class.newInstance()要求構(gòu)造方法能夠被訪問澡罚,而Constructor.newInstance()卻能夠訪問private修飾的構(gòu)造器伸但。

  • 代碼實操

    public class TestClass {
    
        private String name;
    
        public TestClass(String name) {
            this.name = name;
        }
    
        public TestClass() {
            name = "zhangqi";
        }
    
        @Override
        public String toString() {
            return "TestClass{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    public class TestMain {
    
        public static void main(String[] args) {
            Class<TestClass> testClassClass = TestClass.class;
    
            //Class.newInstance() 測試
            try {
                //在Java 9 中已經(jīng)標(biāo)注為過時的API
                TestClass testClass = testClassClass.newInstance();
                System.out.println(testClass.toString());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
            // Constructor.newInstance() 測試
    
            try {
                Constructor<TestClass> constructor = testClassClass.getConstructor(String.class);
                TestClass dashingqi = constructor.newInstance("dashingqi");
                System.out.println("name = "+dashingqi);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    //運行結(jié)果如下
    TestClass{name='zhangqi'}
    name = TestClass{name='dashingqi'}
    

反射中的數(shù)組

數(shù)組本質(zhì)上是一個Class,而在Class中存在一個方法用來識別它是否為一個數(shù)組

  • public native boolean isArray();
    
  • 代碼實操

    public class Arrays {
    
        private int[] array;
        private Car[] cars;
    }
    //我們定義了一個類 Arrays 內(nèi)部有兩個數(shù)組 array和cars 留搔。當(dāng)然array和cars是類Arrays的Field更胖,從Field角度來說,它們是數(shù)組類型催式。
    //我們可以通過一些列的API來獲取它們的具體信息
    
    //獲取數(shù)組的元素對應(yīng)的編碼 同F(xiàn)ield中的
    getName()
     //該方法是獲取數(shù)組里面元素的類型
    getComponentType() array 就是 int cars就是 class operate_array.Car
    
public class MainTest {

    public static void main(String[] args) {
        Class<Arrays> arraysClass = Arrays.class;

        Field[] declaredFields = arraysClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Class<?> type = field.getType();
            if (type.isArray()) {
                System.out.println("Type is " + type.getName());
                System.out.println("ComponentType type is " + type.getComponentType());
            }
        }
    }
}
//運行結(jié)果如下
Type is [I
ComponentType type is int
Type is [Loperate_array.Car;
ComponentType type is class operate_array.Car
反射中動態(tài)創(chuàng)建數(shù)組
  • 是通過Array.newInstance()方法創(chuàng)建的函喉。

    // Array.java
    public static Object newInstance(Class<?> componentType, int... dimensions)
            throws IllegalArgumentException, NegativeArraySizeException {
            return multiNewArray(componentType, dimensions);
        }
    
    • 第一個參數(shù)表示數(shù)組內(nèi)元素類型,第二個參數(shù)表示相應(yīng)維度數(shù)組中的長度限制

      //比如我們呢要創(chuàng)建 int [][] ints = new int[2][3];
      Array.newInstance(int.class,2,3);
      
Array的讀取與賦值

對于Array整體的讀取賦值荣月,把它作為一個普通的Field管呵,調(diào)用Field中對應(yīng)的方法即可。

也就是 Field中提供的 set() 和get

ublic void set(Object obj,
                Object value)
         throws IllegalArgumentException,
                IllegalAccessException;


public Object get(Object obj)
           throws IllegalArgumentException,
                  IllegalAccessException;

當(dāng)數(shù)組中指定位置的元素進(jìn)行讀取與賦值哺窄,這要涉及到Array提供的一些列 setXXX()和getXXX()方法

public static native boolean getBoolean(Object array, int index)
        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;

    public static native byte getByte(Object array, int index)
        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
  • 代碼操作

    public class ArrayTest {
    
        public static void main(String[] args) {
            Class<Arrays> arraysClass = Arrays.class;
    
            try {
                //Constructor 能夠創(chuàng)建一個對象
                //類的實例對象的創(chuàng)建
                Arrays arrays = arraysClass.newInstance();
    
                Field fieldArray = arraysClass.getDeclaredField("array");
                fieldArray.setAccessible(true);
    
                //創(chuàng)建一個數(shù)組
                Object o = Array.newInstance(int.class, 3);
                Array.set(o, 0, 1);
                Array.set(o, 1, 2);
                Array.set(o, 2, 3);
                //操作Field中的set
                fieldArray.set(arrays, o);
    
                int[] array = arrays.getArray();
                for (int i = 0; i < array.length; i++) {
                    System.out.println("array index "+i+"value "+array[i]);
                }
    
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
    
    //運行結(jié)果如下
    array index 0value 1
    array index 1value 2
    array index 2value 3
    

反射中的枚舉類Enum

同數(shù)組一樣捐下,枚舉本質(zhì)上也是一個Class而已,但是反射中還是把它單獨提出來了萌业。

  • 定義一個枚舉類

    public enum EnumTestState {
        IDLE,
        DRIVING,
        STOPPING,
    
        test();
    
        int test1() {
            return 1;
        }
    }
    
  • 枚舉跟類很相似坷襟,有自己的修飾符,方法生年,屬性字段婴程,還可以有構(gòu)造方法。

  • 在Java的反射機制中抱婉,提供了3個特別的API用于操控枚舉档叔。

    // 用來判定 Class 對象是不是枚舉類型
    Class.isEnum()
    
    // 獲取所有的枚舉常量
    Class.getEnumConstants()
    
    
    // 判斷一個 Field 是不是枚舉常量
    java.lang.reflect.Field.isEnumConstant()
    
    
枚舉的獲取與設(shè)定

應(yīng)為等同于Class,所以枚舉的獲取與設(shè)定都可以通過Field中的get()和set()方法蒸绩。

如果枚舉要獲取里面的Field衙四、Method、Constructor可以調(diào)用Class通用的API

  • 代碼實操

    public enum EnumTestState {
        IDLE,
        DRIVING,
        STOPPING,
    
        test();
    
        int test1() {
            return 1;
        }
    }
    
    
    public class EnumTestClass {
    
        private EnumTestState state = EnumTestState.DRIVING;
    
        public EnumTestState getState() {
            return state;
        }
    
        public void setState(EnumTestState state) {
            this.state = state;
        }
    }
    
    public class EnumTestMain {
    
        public static void main(String[] args) {
            //獲取到枚舉對應(yīng)的Class對象
            Class<EnumTestState> enumTestStateClass = EnumTestState.class;
            //Class中有判斷當(dāng)前獲取的Class對象是否是枚舉
            if (enumTestStateClass.isEnum()) {
    
                System.out.println(Arrays.asList(enumTestStateClass.getEnumConstants()));
    
                //活到枚舉中所有的Field
                Field[] declaredFields = enumTestStateClass.getDeclaredFields();
                for (Field field : declaredFields) {
                    //判斷一個Field是不是一個枚舉常量
                    if (field.isEnumConstant()) {
                        System.out.println("is Enum");
                    } else {
                        System.out.println("is not Enum");
                    }
                }
    
                Class<EnumTestClass> enumTestClassClass = EnumTestClass.class;
                EnumTestClass enumTestClass = new EnumTestClass();
                try {
                    //獲取到名字叫做"state"的Field
                    Field state = enumTestClassClass.getDeclaredField("state");
                    state.setAccessible(true);
    
                    EnumTestState enumTestState = (EnumTestState) state.get(enumTestClass);
                    System.out.println("enumTestState = " + enumTestState);
                    state.set(enumTestClass, EnumTestState.STOPPING);
    
                    System.out.println("State current is " + enumTestClass.getState());
    
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
    
    
        }
    }
    
    //運行結(jié)果如下
    [IDLE, DRIVING, STOPPING, test]
    is Enum
    is Enum
    is Enum
    is Enum
    is not Enum
    enumTestState = DRIVING
    State current is STOPPING
    

總結(jié)

  • Java中的反射是非常規(guī)編碼方式患亿。
  • Java中的反射入口是Class文件传蹈,可通過 getClass()、.class步藕、Class.forName()三種方式獲取它惦界。
  • 獲取到Class對象之后,按照需要還能獲取到Field咙冗、Constructor表锻、Method。
  • Field的操作主要涉及到類別的獲取以及數(shù)值的讀取與賦值乞娄。
  • Constractor:可通過Class.newInstance()和Constructor.newInstance()創(chuàng)建類的對象實例瞬逊,推薦后者显歧。
  • Method算是反射的最核心的,發(fā)射都是為了調(diào)用某個Method的invoke方法确镊。
  • 數(shù)組和枚舉可以看成Class對待士骤。

反射是非常規(guī)手段,它會拋棄Java虛擬機的很多優(yōu)化蕾域,所以同樣功能代碼拷肌,反射要比正常方式要慢,所以考慮到采用反射時旨巷,要考慮它的時間成本巨缘。
參考文章:細(xì)說反射,Java 和 Android 開發(fā)者必須跨越的坎

知識點總結(jié)
反射機制.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末采呐,一起剝皮案震驚了整個濱河市若锁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斧吐,老刑警劉巖又固,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異煤率,居然都是意外死亡仰冠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蝶糯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洋只,“玉大人,你說我怎么就攤上這事昼捍∈缎椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵端三,是天一觀的道長。 經(jīng)常有香客問我鹃彻,道長郊闯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任蛛株,我火速辦了婚禮团赁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谨履。我一直安慰自己欢摄,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布笋粟。 她就那樣靜靜地躺著怀挠,像睡著了一般析蝴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绿淋,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天闷畸,我揣著相機與錄音,去河邊找鬼吞滞。 笑死佑菩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的裁赠。 我是一名探鬼主播殿漠,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼佩捞!你這毒婦竟也來了绞幌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤失尖,失蹤者是張志新(化名)和其女友劉穎啊奄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掀潮,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡菇夸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仪吧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庄新。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖薯鼠,靈堂內(nèi)的尸體忽然破棺而出择诈,到底是詐尸還是另有隱情,我是刑警寧澤出皇,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布羞芍,位于F島的核電站,受9級特大地震影響郊艘,放射性物質(zhì)發(fā)生泄漏荷科。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一纱注、第九天 我趴在偏房一處隱蔽的房頂上張望畏浆。 院中可真熱鬧,春花似錦狞贱、人聲如沸刻获。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝎毡。三九已至厚柳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間顶掉,已是汗流浹背草娜。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痒筒,地道東北人宰闰。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像簿透,于是被迫代替她去往敵國和親移袍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353