java 反射

java反射主要從以下幾個(gè)方面理解

  • 理解 Class 類
  • 理解 Java 的類加載機(jī)制
  • 學(xué)會(huì)使用 ClassLoader 進(jìn)行類加載
  • 理解反射的機(jī)制
  • 掌握 Constructor魂莫、Method、Field 類的用法
  • 理解并掌握動(dòng)態(tài)代理

1迷郑、理解Class類

Java程序在運(yùn)行時(shí)恒傻,Java運(yùn)行時(shí)系統(tǒng)一直對(duì)所有的對(duì)象進(jìn)行所謂的運(yùn)行時(shí)類型標(biāo)識(shí),即所謂的RTTI。這項(xiàng)信息紀(jì)錄了每個(gè)對(duì)象所屬的類塞椎。虛擬機(jī)通常使用運(yùn)行時(shí)類型信息選準(zhǔn)正確方法去執(zhí)行唱凯,用來保存這些類型信息的類是Class類羡忘。Class類封裝一個(gè)對(duì)象和接口運(yùn)行時(shí)的狀態(tài),當(dāng)裝載類時(shí)磕昼,Class類型的對(duì)象自動(dòng)創(chuàng)建。
簡單解釋上面一段話
1节猿、Class類也是類的一種票从,只是名字和class關(guān)鍵字高度相似。Java是大小寫敏感的語言滨嘱。
2峰鄙、Class類的對(duì)象內(nèi)容是你創(chuàng)建的類的類型信息,比如你創(chuàng)建一個(gè)shapes類太雨,那么吟榴,Java會(huì)生成一個(gè)內(nèi)容是shapes的Class類的對(duì)象。
3囊扳、Class類的對(duì)象不能像普通類一樣吩翻,以 new shapes() 的方式創(chuàng)建,它的對(duì)象只能由JVM創(chuàng)建锥咸,因?yàn)檫@個(gè)類沒有public構(gòu)造函數(shù)狭瞎。
4、Class類的作用是運(yùn)行時(shí)提供或獲得某個(gè)對(duì)象的類型信息搏予。

Class原理

所有java類都是繼承了object這個(gè)類熊锭,在object這個(gè)類中有一個(gè)方法:getclass().這個(gè)方法是用來取得該類已經(jīng)被實(shí)例化了的對(duì)象的該類的引用,這個(gè)引用指向的是Class類的對(duì)象雪侥。

怎么獲取Class對(duì)象

1碗殷、Class類的forName方法

 Class<?> aClass = Class.forName("com.sl.reflect.Student");

2、對(duì)象的getClass()方法

 Student student = new Student();
 Class<? extends Student> aClass1 = student.getClass();

3速缨、使用類名加.class

 Class classes = Student.class.getClass();

4锌妻、通過ClassLoader對(duì)象的loadClass()方法

Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.sl.reflect.Student");
Class類的常用方法
image.png

敲黑板調(diào)用newInstance()方法時(shí),調(diào)用的是無參構(gòu)造器鸟廓,所以每個(gè)類中一定要聲明一個(gè)無參構(gòu)造器

ClassLoader加載

類裝載器是用來把類(class)裝載進(jìn) JVM 的从祝。JVM 規(guī)范定義了兩種類型的類裝載器:啟動(dòng)類裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)。 JVM在運(yùn)行時(shí)會(huì)產(chǎn)生3個(gè)類加載器組成的初始化加載器層次結(jié)構(gòu) 引谜,如下圖所示:


image.png
        //1牍陌、獲取系統(tǒng)類的加載器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);

        //2. 獲取系統(tǒng)類加載器的父類加載器(擴(kuò)展類加載器,可以獲仍毖省).
        classLoader = classLoader.getParent();
        System.out.println(classLoader);

        //3. 獲取擴(kuò)展類加載器的父類加載器(引導(dǎo)類加載器毒涧,不可獲取).
        classLoader = classLoader.getParent();
        System.out.println(classLoader);

注意系統(tǒng)類加載器可以加載當(dāng)前項(xiàng)目src目錄下面的所有類贝室,如果文件也放在src下面契讲,也可以用類加載器來加載調(diào)用 getResourceAsStream 獲取類路徑下的文件對(duì)應(yīng)的輸入流仿吞。

       //文件夾在src下
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("text1.txt");
        //文件夾在包名下
        InputStream resourceAsStream1 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/sl/reflect/text2.txt");

反射

Java反射機(jī)制主要提供了以下功能

  • 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
  • 在運(yùn)行時(shí)獲取任意一個(gè)類所具有的成員變量和方法
  • 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法(屬性)
  • 生成動(dòng)態(tài)代理

Student測(cè)試類

/**
 * @author shuliangzhao
 * @Title: Student
 * @ProjectName design-parent
 * @Description: TODO
 * @date 2019/6/15 23:08
 */
public class Student {

    private String name;

    private Integer age;

    public Student() {

    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
Method類
  public static void testMethod() throws Exception {
        Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
        //1.獲取方法
        // 獲取取clazz對(duì)應(yīng)類中的所有方法--方法數(shù)組(一)
        // 不能獲取private方法,并且獲取從父類繼承來的所有方法
        Method[] methods = aClass.getMethods();
        for (Method method:methods) {
            System.out.println(method);
        }
        System.out.println("================================");
        //2.獲取方法
        // 獲取取clazz對(duì)應(yīng)類中的所有方法--方法數(shù)組(一)
        // 不能獲取private方法,不獲取從父類繼承來的所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method method:declaredMethods) {
            System.out.println(method);
        }

        System.out.println("=================================");
        //  1.3.獲取指定的方法
        //  需要參數(shù)名稱和參數(shù)列表,無參則不需要寫
        //  對(duì)于方法public void setName(String name) {  }
        Method method = aClass.getDeclaredMethod("setName", String.class);
        System.out.println(method);
        //  而對(duì)于方法public void setAge(int age) {  }
        method = aClass.getDeclaredMethod("setAge", Integer.class);
        System.out.println(method);
        //  這樣寫是獲取不到的捡偏,如果方法的參數(shù)類型是int型
        //  如果方法用于反射唤冈,那么要么int類型寫成Integer: public void setAge(Integer age) {  }
        //  要么獲取方法的參數(shù)寫成int.class

        //2.執(zhí)行方法
        //  invoke第一個(gè)參數(shù)表示執(zhí)行哪個(gè)對(duì)象的方法,剩下的參數(shù)是執(zhí)行方法時(shí)需要傳入的參數(shù)
        Object obje = aClass.newInstance();
        method.invoke(obje,2);

        //如果一個(gè)方法是私有方法银伟,第三步是可以獲取到的你虹,但是這一步卻不能執(zhí)行
        //私有方法的執(zhí)行,必須在調(diào)用invoke之前加上一句method.setAccessible(true);
    }

    /**
      *  把類對(duì)象和類方法名作為參數(shù)彤避,執(zhí)行方法
     *
     *  把全類名和方法名作為參數(shù)傅物,執(zhí)行方法
     *  可變參數(shù)可以放數(shù)組
     * @param obj: 方法執(zhí)行的那個(gè)對(duì)象.
     * @param methodName: 類的一個(gè)方法的方法名. 該方法也可能是私有方法.
     * @param args: 調(diào)用該方法需要傳入的參數(shù)
     * @return: 調(diào)用方法后的返回值
     *
     */
    public Object invoke(Object obj, String methodName, Object ... args) throws Exception{
        //1. 獲取 Method 對(duì)象
        //   因?yàn)間etMethod的參數(shù)為Class列表類型,所以要把參數(shù)args轉(zhuǎn)化為對(duì)應(yīng)的Class類型琉预。

        Class [] parameterTypes = new Class[args.length];
        for(int i = 0; i < args.length; i++){
            parameterTypes[i] = args[i].getClass();
            System.out.println(parameterTypes[i]);
        }

        Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes);
        //如果使用getDeclaredMethod董饰,就不能獲取父類方法,如果使用getMethod圆米,就不能獲取私有方法

        //
        //2. 執(zhí)行 Method 方法
        //3. 返回方法的返回值
        return method.invoke(obj, args);
    }
Field類
public static void testField() throws Exception {
        Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
        //1.獲取字段
        //  1.1 獲取所有字段 -- 字段數(shù)組
        //     可以獲取公用和私有的所有字段卒暂,但不能獲取父類字段
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field:declaredFields) {
            System.out.println(field);
        }
        System.out.println("=============================");
        //  1.2獲取指定字段
        Field field = aClass.getDeclaredField("name");
        System.out.println(field.getName());

        System.out.println("==============================");

        Student student = new Student();
        //如果字段是私有的,不管是讀值還是寫值榨咐,都必須先調(diào)用setAccessible(true)方法
        field.setAccessible(true);
        student.setAge(1);
        student.setName("張三");
        //2.使用字段
        //  2.1獲取指定對(duì)象的指定字段的值
        Object o = field.get(student);
        System.out.println(o);
        System.out.println("==========================");
        //  2.2設(shè)置指定對(duì)象的指定對(duì)象Field值
        field.set(student, "DEF");
        System.out.println(student.getName());
    }
Constructor類
 public static void testConstructor() throws Exception{
        Class<Student> aClass = (Class<Student>) Class.forName("com.sl.reflect.Student");
        //1. 獲取 Constructor 對(duì)象
        //   1.1 獲取全部
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor constructor:constructors) {
            System.out.println(constructor);
        }
        System.out.println("============================");
        //  1.2獲取某一個(gè)介却,需要參數(shù)列表
        Constructor<Student> constructor = aClass.getConstructor(String.class, Integer.class);
        System.out.println(constructor);
        System.out.println("============================");
        //2. 調(diào)用構(gòu)造器的 newInstance() 方法創(chuàng)建對(duì)象
        Object obj = constructor.newInstance("zhagn", 1);
    }
Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface AgeValidator {
    public int min();
    public int max();
}

public static void testAnnotation() throws Exception{
        Class<?> aClass = Class.forName("com.sl.reflect.Student");
        Object o = aClass.newInstance();
        Method method = aClass.getDeclaredMethod("setAge", Integer.class);
        int val = 6;
        AgeValidator annotation = method.getAnnotation(AgeValidator.class);
        if (annotation != null) {
            if (annotation instanceof AgeValidator) {
               AgeValidator ageValidator =  annotation;
               if (val < ageValidator.min() || val > ageValidator.max()) {
                   throw new RuntimeException("年齡非法");
               }
            }
        }
        method.invoke(o,20);
        System.out.println(o);
    }

小結(jié)

  1. Class: 是一個(gè)類; 一個(gè)描述類的類.
      封裝了描述方法的 Method,
    描述字段的 Filed,
    描述構(gòu)造器的 Constructor 等屬性.
  2. 如何得到 Class 對(duì)象:
      2.1 Person.class
      2.2 person.getClass()
      2.3 Class.forName("com.atguigu.javase.Person")
  3. 關(guān)于 Method:
      3.1 如何獲取 Method:
        1). getDeclaredMethods: 得到 Method 的數(shù)組.
        2). getDeclaredMethod(String methondName, Class ... parameterTypes)
      3.2 如何調(diào)用 Method
        1). 如果方法時(shí) private 修飾的, 需要先調(diào)用 Method 的 setAccessible(true), 使其變?yōu)榭稍L問
        2). method.invoke(obj, Object ... args);
  4. 關(guān)于 Field:
      4.1 如何獲取 Field: getField(String fieldName)
      4.2 如何獲取 Field 的值:
        1). setAccessible(true)
        2). field.get(Object obj)
      4.3 如何設(shè)置 Field 的值:
        field.set(Obejct obj, Object val)
  5. 了解 Constructor 和 Annotation
  6. 反射和泛型.
      6.1 getGenericSuperClass: 獲取帶泛型參數(shù)的父類
      6.2 Type 的子接口: ParameterizedType
      6.3 可以調(diào)用 ParameterizedType 的 Type[] getActualTypeArguments() 獲取泛型參數(shù)的數(shù)組.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市块茁,隨后出現(xiàn)的幾起案子齿坷,更是在濱河造成了極大的恐慌,老刑警劉巖数焊,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件永淌,死亡現(xiàn)場離奇詭異,居然都是意外死亡佩耳,警方通過查閱死者的電腦和手機(jī)遂蛀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來干厚,“玉大人李滴,你說我怎么就攤上這事÷椋” “怎么了所坯?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挂捅。 經(jīng)常有香客問我芹助,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任状土,我火速辦了婚禮无蜂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒙谓。我一直安慰自己斥季,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布彼乌。 她就那樣靜靜地躺著泻肯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慰照。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天琉朽,我揣著相機(jī)與錄音毒租,去河邊找鬼。 笑死箱叁,一個(gè)胖子當(dāng)著我的面吹牛墅垮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耕漱,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼算色,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了螟够?” 一聲冷哼從身側(cè)響起灾梦,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妓笙,沒想到半個(gè)月后若河,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寞宫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年萧福,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辈赋。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲫忍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钥屈,到底是詐尸還是另有隱情悟民,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布焕蹄,位于F島的核電站逾雄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸦泳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一银锻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧做鹰,春花似錦击纬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饭尝,卻和暖如春肯腕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钥平。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工实撒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涉瘾。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓知态,卻偏偏與公主長得像,于是被迫代替她去往敵國和親立叛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子负敏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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