2郑藏、反射技術(shù)

注解

一、前期概要

1瘩欺、 什么是反射

Java 反射機(jī)制在程序運(yùn)行時(shí)必盖,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法俱饿;對(duì)于任意一個(gè)對(duì)象歌粥,都能夠調(diào)用它的任意一個(gè)方法和屬性。這種 動(dòng)態(tài)的獲取信息 以及 動(dòng)態(tài)調(diào)用對(duì)象的方法 的功能稱為 java 的反射機(jī)制拍埠。

反射中的反的理解:在使用的之前失驶,提前不知道需要使用什么類型的對(duì)象。只是在調(diào)用的時(shí)候枣购,才知道要調(diào)用的對(duì)象類型嬉探。這種反其道而行的就是反射中反的理解。

程序執(zhí)行分為編譯器和運(yùn)行期棉圈,編譯時(shí)刻加載一個(gè)類就稱為靜態(tài)加載類涩堤,運(yùn)行時(shí)刻加載類稱為動(dòng)態(tài)加載類,

核心思想 讓你在寫代碼的時(shí)候可以更加靈活分瘾,降低耦合定躏,提高代碼的自適應(yīng)能力。

反射框架提供如下常用的核心功能:

1.在運(yùn)行時(shí)判斷任意對(duì)象所屬的類芹敌;

2.在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象痊远;

3.在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法(通過(guò)反射甚至可以調(diào)用private方法);

4.在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;

2氏捞、反射的主要用途

通用框架碧聪,很多框架都是配置化的(比如Spring通過(guò)xml配置Bean), 為了保證框架的通用性液茎,可能需要根據(jù)不同的配置文件加載不同的對(duì)象或者類逞姿,調(diào)用不同的方法辞嗡,這個(gè)時(shí)候就需要反射,運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象滞造。

3续室、缺點(diǎn)

  • 性能不佳 - 由于java反射動(dòng)態(tài)解析類型,因此涉及掃描類路徑以查找要加載的類的處理谒养,從而導(dǎo)致性能降低挺狰。
  • 安全限制 - Reflection需要運(yùn)行時(shí)權(quán)限,這些權(quán)限可能不適用于在安全管理器下運(yùn)行的系統(tǒng)买窟。由于安全管理器丰泊,這可能導(dǎo)致應(yīng)用程序在運(yùn)行時(shí)失敗。
  • 安全問(wèn)題 - 使用反射始绍,我們可以訪問(wèn)我們不應(yīng)該訪問(wèn)的部分代碼瞳购,例如,我們可以訪問(wèn)類的私有字段并更改它的值亏推。這可能是嚴(yán)重的安全威脅学赛,并導(dǎo)致您的應(yīng)用程序出現(xiàn)異常行為。
  • 高維護(hù) - 反射代碼很難理解和調(diào)試吞杭,在編譯時(shí)也無(wú)法找到代碼的任何問(wèn)題罢屈,因?yàn)檫@些類可能不可用,使其不太靈活且難以維護(hù)篇亭。
  1. Class 對(duì)象
  2. 類名
  3. 修飾符
  4. 包信息
  5. 父類
  6. 實(shí)現(xiàn)的接口
  7. 構(gòu)造器
  8. 方法
  9. 變量
  10. 注解

二缠捌、獲得 Class 對(duì)象

在運(yùn)行期間,一個(gè)類译蒂,只有一個(gè)Class對(duì)象產(chǎn)生

1曼月、類的靜態(tài)方法(常用) :

  1. 說(shuō)明
    獲取指定的類完整的路徑相關(guān)聯(lián)接口的Class對(duì)象。
  2. 方法
    // 掌握
    public static Class<?> forName(String className)
     // 了解
    public static Class<?> forName(String className, boolean initialize,ClassLoader loader)
    
  3. 區(qū)別
    第一個(gè)默認(rèn)進(jìn)行初始化操作柔昼,
    第二個(gè)可以指定是否進(jìn)行初始化操作哑芹。當(dāng)initialize=false 不進(jìn)行初始化操作,即不會(huì)執(zhí)行靜態(tài)代碼塊捕透。
  4. 舉個(gè)栗子
    Class clazz = Class.forName("com.wener.reflect.Xxx")
    // 或者
    Class clazz = Class.forName("com.wener.reflect.Xxx",initialize,this.getClass().getClassLoader)
    

2聪姿、使用 .class 靜態(tài)語(yǔ)法。

  1. 說(shuō)明
    任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個(gè)“靜態(tài)”的class屬性
  2. 方法
    Class<?> cls = 類型.class;
    
  3. 舉個(gè)栗子
    Class<String> cls = String.class;
    System.out.println(cls.toString());
    

3、使用類對(duì)象的 getClass()

  1. 說(shuō)明
    通過(guò)對(duì)象的實(shí)例來(lái)返回Class對(duì)象
  2. 方法
    Class<?> cls = instance.getClass()
    
  3. 舉個(gè)栗子
    public class User {
    }
    User user = new User();
    Class<? extends User> clz = user.getClass();
    

4、總結(jié)

  1. 常用的是類的靜態(tài)方法,
  2. getClass()的話一般在繼承的時(shí)候用的比較多一點(diǎn),比如Android里的注解框架
  3. .class 靜態(tài)語(yǔ)法: 需要導(dǎo)入類的包稀蟋,依賴太強(qiáng)脉顿,不導(dǎo)包就拋編譯錯(cuò)誤

三腺办、創(chuàng)建實(shí)例

1.4、注意

  1. cls.newInstance()方法返回的是一個(gè)泛型T,我們要強(qiáng)轉(zhuǎn)成自定義類
  2. cls.newInstance()默認(rèn)返回的是類的無(wú)參數(shù)構(gòu)造對(duì)象
  3. 被反射機(jī)制加載的類必須有無(wú)參數(shù)構(gòu)造方法,否者運(yùn)行會(huì)拋出異常

四、屬性操作

1骆撇、說(shuō)明

類的成員變量也是一個(gè)對(duì)象擎场,它是java.lang.reflect.Field的一個(gè)對(duì)象羽德,所以我們通過(guò)java.lang.reflect.Field里面封裝的方法來(lái)獲取這些信息并且操作這些屬性

2、獲取單個(gè)成員字段

  1. 方法
// 通過(guò)字段名迅办,返回一個(gè)具體的具有public屬性的成員變量(包括父類的)
Field getField(String name)
// 通過(guò)字段名所有已聲明的所有成員變量(私有的 默認(rèn)的 共有的)宅静,但不能得到其父類的成員變量
Field getDeclaredField(String name)
  1. 舉個(gè)栗子
    public class User {
        private String name;
        public String detail;
    }
    public static void main(String[] args) {
      try {
        Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
        /**
         * 獲取類的指定名稱公開(kāi)的屬性
         */
        Field detail = cls.getField("detail");
        System.out.println(detail);
        /**
         * 獲取類的指定名稱的的屬性(包括私有的屬性)
         */
        Field name = cls.getDeclaredField("name");
        System.out.println(name);
      } catch (ClassNotFoundException | NoSuchFieldException e) {
        e.printStackTrace();
      }
    }
    

3、獲取所有成員字段

  1. 方法
    // 獲取所有的”公有字段”
    Field[] getFields()
    // 獲取所有字段(私有站欺、受保護(hù)姨夹、默認(rèn)、公有)
    Field[] getDeclaredFields()
    
  2. 舉個(gè)栗子
    public class TestReflectUserField {
        public static void main(String[] args) {
            try {
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                Field[] fields = cls.getFields();
                for (Field field : fields) {
                    System.out.println("類型: " + field.getType() + "方法名:  " + field.getName());
                }
                Field[] declaredFields = cls.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    System.out.println("類型: " + declaredField.getType() + "方法名:  " + declaredField.getName());
                }
            } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    

4镊绪、字段賦值

  1. 方法
    // 將指定對(duì)象參數(shù)上的此Field對(duì)象表示的字段設(shè)置為指定的新值
    field.set(Object obj,Object value)
    
  2. 參數(shù)說(shuō)明
    • Object obj: 字段所在的類的實(shí)例對(duì)象
    • Object value : 新值
  3. 注意
    // 如果要給私有變量賦值必須取消權(quán)限的訪問(wèn)控制
     field.setAccessible(true);
    
  4. 舉個(gè)栗子
        public static void main(String[] args) {
            try {
               // 1 實(shí)例化Class對(duì)象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                /**
                 * 獲取類的指定名稱公有的屬性
                 */
                Field detail = cls.getField("detail");
                System.out.println(detail);
                /**
                 * 獲取類的所有的屬性(包括私有 默認(rèn)的 公有的)
                 */
                Field name = cls.getDeclaredField("name");
                // 2.創(chuàng)建對(duì)象
                Object o = cls.newInstance();
                 // 3 通過(guò)字段的set方法設(shè)置
                name.set(o, "嬌嬌");
                System.out.println(o.toString());
            } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    

五、方法操作

1洒忧、獲取單個(gè)方法

  1. 方法
    // 方法返回一個(gè)特定的方法蝴韭,其中第一個(gè)參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)對(duì)應(yīng)Class的對(duì)象
    public Method getMethod(String name, Class<?>... parameterTypes)
    
  2. 舉個(gè)栗子
    public class User {
        private String name = "木木";
        public String detail = "hello";
        private int age;
        private BigDecimal balance;
        public void increment() {
            this.age++;
            System.out.println(age);
        }
        public BigDecimal getBalance() {
            return balance;
        }
        public void setBalance(BigDecimal balance) {
            this.balance = balance;
        }
        private void say(int num) {
            System.out.println(num + "號(hào)技師");
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", detail='" + detail + '\'' +
                    '}';
        }
    }
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.實(shí)例化class對(duì)象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.實(shí)例化User對(duì)象
                Object o = cls.newInstance();
                // 3.獲取increment方法
                Method method = cls.getMethod("increment");
                // 有參數(shù)無(wú)返回值
                Method setBalance = cls.getMethod("setBalance", BigDecimal.class);
                // 有返回值值無(wú)參數(shù)
                Method methodGet = cls.getMethod("getBalance");
                 // 獲取私有的方法
                Method say = cls.getDeclaredMethod("say", int.class);
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

2熙侍、獲取所有的方法

  1. 方法
    // 返回類或接口聲明的所有方法榄鉴,包括公共、保護(hù)蛉抓、默認(rèn)(包)訪問(wèn)和私有方法庆尘,但不包括繼承的方法。
    public Method[] getDeclaredMethods() throws SecurityException
    
    // 返回某個(gè)類的所有公用(public)方法巷送,包括其繼承類的公用方法驶忌。
    public Method[] getMethods() throws SecurityException
    
  2. 舉個(gè)栗子
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.實(shí)例化class對(duì)象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.實(shí)例化User對(duì)象
                Object o = cls.newInstance();
                // 獲取所有的共有的方法(包括父類的方法)
                Method[] methods = cls.getMethods();
                for (Method method1 : methods) {
                    System.out.println(method1.getName());
                }
                // 獲取所有的方法(包括私有的,共有的,默認(rèn)的)
                Method[] declaredMethods = cls.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    System.out.println(declaredMethod.getName());
                }
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

3、調(diào)用方法

  1. 方法
    Object invoke(Object obj, Object... args)
    
  2. 參數(shù)說(shuō)明
    • obj - 從中調(diào)用底層方法的對(duì)象笑跛,必須是實(shí)例化的對(duì)象
    • args - 用于方法調(diào)用的參數(shù)付魔,是個(gè)Object數(shù)組,因?yàn)閰?shù)有可能有多個(gè)
  3. 返回值
    使用參數(shù) args 在 obj 上指派該對(duì)象所表示方法的結(jié)果
  4. 舉個(gè)栗子
    public class User {
        private String name = "木木";
        public String detail = "hello";
        private int age;
        private BigDecimal balance;
        public void increment() {
            this.age++;
            System.out.println(age);
        }
        public BigDecimal getBalance() {
            return balance;
        }
        public void setBalance(BigDecimal balance) {
            this.balance = balance;
        }
        private void say(int num) {
            System.out.println(num + "號(hào)技師");
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", detail='" + detail + '\'' +
                    '}';
        }
    }
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.實(shí)例化class對(duì)象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.實(shí)例化User對(duì)象
                Object o = cls.newInstance();
                // 3.獲取set方法
                Method method = cls.getMethod("increment");
                // 4 執(zhí)行方法
                method.invoke(o);
                // 有參數(shù)無(wú)返回值
                Method setBalance = cls.getMethod("setBalance", BigDecimal.class);
                Object methodSet = setBalance.invoke(o, new BigDecimal(100.00));
                System.out.println(methodSet);
                // 有返回值值無(wú)參數(shù)
                Method methodGet = cls.getMethod("getBalance");
                Object invoke = methodGet.invoke(o);
                System.out.println(invoke);
                 // 獲取私有的方法
                Method say = cls.getDeclaredMethod("say", int.class);
                //  運(yùn)行時(shí)取消訪問(wèn)權(quán)限檢測(cè)機(jī)制
                say.setAccessible(true);
                 //  執(zhí)行方法
                say.invoke(o, 1);
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

4飞蹂、其它API

返回值 方法 說(shuō)明
String getName() 獲取方法的名稱
int getModifiers() 獲取方法的修飾符
Class<?> getReturnType() Type getGenericReturnType 返回方法的返回值類型
Class<?>[] getParameterTypes() Type[] getGenericParameterTypes() 返回方法的參數(shù)(列表)
Class<?>[] getExceptionTypes() Type[] getGenericExceptionTypes() 返回方法的異常信息

六几苍、綜合案例

1、通過(guò)配置文件動(dòng)態(tài)切換

  1. 說(shuō)明
    反射非常強(qiáng)大陈哑,但是學(xué)習(xí)了之后妻坝,會(huì)不知道該如何使用,反而覺(jué)得還不如直接調(diào)用方法來(lái)的直接和方便惊窖。
    但等我們后面接觸到一些框架之后才會(huì)有一些感觸
  2. 測(cè)試類
    package com.wener.reflect.demo2;
    public class ReflectDemo1 {
        public void say() {
            System.out.println("ReflectDemo1");
        }
    }
    public class ReflectDemo2 {
        public void say() {
            System.out.println("ReflectDemo2");
        }
    }
    
  3. 配置文件reflect.properties
    class=ReflectDemo2.
    method=say
    
  4. 測(cè)試代碼
     public static void main(String[] args) {
            //從spring.txt中獲取類名稱和方法名稱
            File springConfigFile = new File("/Users/zhangwei/work/IdeaProjects/JavaExample/ReflectExample/src/reflect.properties");
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream(springConfigFile));
                String className = (String) properties.get("class");
                String methodName = (String) properties.get("method");
                //根據(jù)類名稱獲取類對(duì)象
                Class cls = Class.forName(className);
                //根據(jù)方法名稱刽宪,獲取方法對(duì)象
                Method m = cls.getMethod(methodName);
                //根據(jù)構(gòu)造器,實(shí)例化出對(duì)象
                Object service = cls.newInstance();
                //調(diào)用對(duì)象的指定方法
                m.invoke(service);
            } catch (IOException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    
  5. 優(yōu)點(diǎn)
    當(dāng)需要從調(diào)用第一個(gè)類的方法界酒,切換到調(diào)用第二類的方法的時(shí)候纠屋,不需要修改一行代碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盾计,隨后出現(xiàn)的幾起案子售担,更是在濱河造成了極大的恐慌赁遗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件族铆,死亡現(xiàn)場(chǎng)離奇詭異岩四,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)哥攘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門剖煌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人逝淹,你說(shuō)我怎么就攤上這事耕姊。” “怎么了栅葡?”我有些...
    開(kāi)封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵茉兰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我欣簇,道長(zhǎng)规脸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任熊咽,我火速辦了婚禮莫鸭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘横殴。我一直安慰自己被因,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布衫仑。 她就那樣靜靜地躺著氏身,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惑畴。 梳的紋絲不亂的頭發(fā)上蛋欣,一...
    開(kāi)封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音如贷,去河邊找鬼陷虎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杠袱,可吹牛的內(nèi)容都是我干的尚猿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼楣富,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凿掂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庄萎,失蹤者是張志新(化名)和其女友劉穎踪少,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體糠涛,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡援奢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忍捡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片集漾。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖砸脊,靈堂內(nèi)的尸體忽然破棺而出具篇,到底是詐尸還是另有隱情,我是刑警寧澤凌埂,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布驱显,位于F島的核電站,受9級(jí)特大地震影響侨舆,放射性物質(zhì)發(fā)生泄漏秒紧。R本人自食惡果不足惜绢陌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一挨下、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脐湾,春花似錦臭笆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至闻鉴,卻和暖如春茵乱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孟岛。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工瓶竭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渠羞。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓斤贰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親次询。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荧恍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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