JavaSE進(jìn)階十一 反射機(jī)制一

1姥闭,反射機(jī)制

  • 反射機(jī)制有什么作用
    • 通過(guò)java語(yǔ)言中的反射機(jī)制可以操作字節(jié)碼文件丹鸿;有點(diǎn)類似于黑客,可以讀和修改字節(jié)碼文件棚品。
    • 通過(guò)反射機(jī)制可以操作代碼片段靠欢。(class文件)
  • 反射機(jī)制的相關(guān)類在哪個(gè)包下?
    • java.lang.reflect
  • 反射機(jī)制相關(guān)的重要類:
    • java.lang.Class 代表字節(jié)碼文件 代表一個(gè)類型
    • java.lang.reflect.Method 代表字節(jié)碼中的方法字節(jié)碼
    • java.lang.reflect.Constructor 代表字節(jié)碼中的構(gòu)造方法字節(jié)碼
    • java.lang.reflect.Field 代表字節(jié)碼中的屬性字節(jié)碼
  • 獲取一個(gè)類的字節(jié)碼文件有三種方式
    • 1铜跑,Class c = Class.forName("完整的類名帶包名")
    • 2门怪,Class c = 引用/對(duì)象.getClass();
    • 3,Class c = 任何類型.class();
代碼示例
import java.util.Date;

public class ReflectTest {
    public static void main(String[] args) {
        /*
        第一種方式:
        Class.forName();
            靜態(tài)方法
            方法的參數(shù)是一個(gè)字符串
            字符串需要是一個(gè)完整的類名
            完整類名必須帶有包名
         */
        Class c1 = null;
        try {
            c1 = Class.forName("java.lang.String"); // c1代表String.class字節(jié)碼文件疼进,或者說(shuō)代表String類型
            Class c2 = Class.forName("java.lang.System");// c2代表Date類型
            Class c3 = Class.forName("java.util.Date");  // c3代表Integer類型
            Class c4 = Class.forName("java.lang.Integer");// c4代表System類型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 第二種方式:java對(duì)象中都有一個(gè)方法:getClass()
        // 在方法區(qū)中薪缆,同一個(gè)類型字節(jié)碼文件只有一個(gè)秧廉。
        String s = "abc";
        Class x = s.getClass();// x代表String.class字節(jié)碼文件伞广,或者說(shuō)代表String類型拣帽。
        // 在方法區(qū)中,String類型字節(jié)碼文件只有一個(gè)嚼锄。
        System.out.println(c1 == x); // true 說(shuō)明c1和x的內(nèi)存地址是一樣的

        // 第三種方式:java語(yǔ)言中任何一種類型减拭,包括基本數(shù)據(jù)類型,都有.class屬性区丑。
        Class z = String.class;  // z代表String類型拧粪。
        Class k = Date.class;   // k代表Date類型。
        Class f = int.class;    // f代表int類型沧侥。
        Class e = double.class; // e代表double類型可霎。

        System.out.println(x == z); // true 說(shuō)明x和z的內(nèi)存地址是一樣的
    }
}

2,通過(guò)反射機(jī)制實(shí)例化對(duì)象

  • 獲取到Class能做什么
    • 通過(guò)Class的newInstance方法來(lái)實(shí)例化對(duì)象宴杀。
    • 注意:newInstance內(nèi)部調(diào)用的是無(wú)參構(gòu)造方法癣朗,必須保證無(wú)參構(gòu)造方法的存在。
  • Class.forName()方法的執(zhí)行時(shí)發(fā)生了什么
    • Class.forName()方法的執(zhí)行會(huì)導(dǎo)致類加載旺罢;類加載時(shí)旷余,靜態(tài)代碼塊執(zhí)行。
    • 如果你只希望執(zhí)行一個(gè)類的靜態(tài)代碼塊扁达,其他代碼不執(zhí)行可以使用它正卧。
代碼示例
public class ReflectTest01 {
    public static void main(String[] args) {

        try {
            // 通過(guò)反射機(jī)制獲取Class,通過(guò)Class來(lái)實(shí)例化對(duì)象
            Class c = Class.forName("com.javaSE.reflects.User");
            // newInstance這個(gè)方法會(huì)調(diào)用User類的無(wú)參構(gòu)造方法跪解,完成對(duì)象的創(chuàng)建炉旷。
            // newInstance調(diào)用的是無(wú)參構(gòu)造方法,必須保證無(wú)參構(gòu)造方法的存在惠遏。
            Object obj = c.newInstance();
            System.out.println(obj);

            //  Class.forName()方法的執(zhí)行會(huì)導(dǎo)致類加載
            Class.forName("com.javaSE.reflects.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

class User{
    public User() {
        System.out.println("User無(wú)參構(gòu)造方法");
    }
    public User(String s) {
        System.out.println("User有參構(gòu)造方法");
    }
}

class MyClass{
    // 靜態(tài)代碼塊在類加載時(shí)執(zhí)行砾跃,并且只執(zhí)行一次。
    static {
        System.out.println("靜態(tài)代碼塊執(zhí)行了");
    }
}

3节吮,驗(yàn)證反射機(jī)制的靈活性

java代碼寫(xiě)一遍抽高,在不改變java源代碼的情況下,可以做到不同對(duì)象的實(shí)例化透绩,非常靈活翘骂。(符合OCP開(kāi)閉原則:對(duì)擴(kuò)展開(kāi)發(fā),對(duì)修改關(guān)閉)

代碼示例
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class ReflectTest02 {
    public static void main(String[] args) {
        // 1帚豪,創(chuàng)建一個(gè)名為ClassName的properties配置文件碳竟,把User類放到配置文件里:className=com.javase.reflects.User
        FileReader reader = null;
        try {
            // 通過(guò)IO流讀取ClassName.properties文件
             reader = new FileReader("src/ClassName.properties");
            // 創(chuàng)建屬性類對(duì)象
            Properties pro = new Properties();
            // 加載
            pro.load(reader);

            // 通過(guò)key獲取value
            String cn = pro.getProperty("className");
            System.out.println(cn);

            // 通過(guò)反射機(jī)制實(shí)例化對(duì)象

            Class c = Class.forName(cn);
            Object obj = c.newInstance();
            System.out.println(obj);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } finally {
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4,研究一下文件路徑問(wèn)題

怎么獲取一個(gè)文件的絕對(duì)路徑狸臣,以下講解的這種方式是通用的莹桅,前提:文件需要在類路徑下。

代碼示例
public class ReflectTest03 {
    public static void main(String[] args) throws Exception {
        // 這種方式路徑缺點(diǎn):移植性差烛亦,在IDEA中默認(rèn)的當(dāng)前路徑是project的跟诈泼。
        // 這個(gè)代碼假設(shè)離開(kāi)了IDEA懂拾,換到了其他位置,可能當(dāng)前路徑就是project的跟了铐达,這個(gè)時(shí)候路徑就無(wú)效了岖赋。
        // FileReader reader = new FileReader("src/ClassName.properties");

        // 下面說(shuō)一種比較通用的路徑,即使代碼位置更改了瓮孙,仍然是有用的唐断。
        // 注意:使用以下通用方式的前提是:這個(gè)文件必須在類路徑下。
        // 什么是類路徑杭抠?在src下的都是類路徑下脸甘。src是類的跟路徑

        /*
        解析以下代碼:
            Thread.currentThread()  當(dāng)前線程對(duì)象。
            getContextClassLoader()  是線程對(duì)象的方法偏灿,獲取當(dāng)前線程的類加載器對(duì)象斤程。
            getResource()  [獲取資源]這是類加載器的方法,當(dāng)前線程的類加載器默認(rèn)從類的根路徑下加載資源菩混。
         */
        String className = "ClassName.properties";
        // String className = "com/javaSE/reflects/ReflectTest.class";
        System.out.println(className);
        // 獲取絕對(duì)路徑
        String path = Thread.currentThread().getContextClassLoader().
                getResource(className).getPath();
        // 采用以上的代碼可以拿到一個(gè)文件的絕對(duì)路徑
        System.out.println(path);

        // 把絕對(duì)路徑傳給流
        // FileReader reader = new FileReader(path);

        // 直接以流的反射返回
        InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties");

        // --------------------------------------------------------------------------------------------------------------------------

        // 創(chuàng)建屬性對(duì)象
        Properties pro = new Properties();
        // 加載流
        pro.load(reader);
        // 關(guān)閉流
        reader.close();

        // 通過(guò)key值獲取value
        String cl = pro.getProperty("className");
        System.out.println(cl);

        Class c2 = Class.forName(cl);
        c2.newInstance();
    }
}

5忿墅,資源綁定器(ResourceBundle)

  • java.util包下提供了一個(gè)資源綁定器,便于獲取屬性配置文件中的內(nèi)容
  • 使用這種方式的時(shí)候沮峡,屬性配置文件xx.properties必須放到類路徑下疚脐。
代碼示例
import java.util.ResourceBundle;

public class ReflectTest04 {
    public static void main(String[] args) throws Exception {

        // 資源綁定器 只能綁定xx.properties文件 這個(gè)文件必須在類路徑下 文件擴(kuò)展名不用寫(xiě)
        ResourceBundle bundle = ResourceBundle.getBundle("ClassName");
        // 通過(guò)key值獲取value
        String cl = bundle.getString("className");
        System.out.println(cl);
        // 獲取字節(jié)碼文件
        Class c2 = Class.forName(cl);
        c2.newInstance();
    }
}

關(guān)于JDK中自帶的類加載器(了解)

  • 什么是類加載器

    • 專門負(fù)責(zé)加載類的命令/工具。ClassLoader
  • JDK中自帶了3個(gè)類加載器

    • 啟動(dòng)類加載器
    • 擴(kuò)展類加載器
    • 應(yīng)用類加載器
  • 假設(shè)有這樣一段代碼:String s = "abc";

    • 代碼再開(kāi)始執(zhí)行之前邢疙,會(huì)將所需要類全部加載到JVM當(dāng)中棍弄,通過(guò)類加載器加載,看到以上代碼類加載器會(huì)找到Sting.class文件進(jìn)行加疟游。

    • 怎么進(jìn)行加載的呼畸?

      • 首先通過(guò)啟動(dòng)類加載器加載;
        • 啟動(dòng)類加載器專門加載:jdk目錄下的/jre/lib/rt.jar
        • rt.jar中都是JDK中最核心的類庫(kù)颁虐。
      • 如果通過(guò)啟動(dòng)加載器加載不到蛮原,就會(huì)通過(guò)擴(kuò)展類加載器進(jìn)行加載;
        • 擴(kuò)展類加載器專門加載:jdk目錄下的/jre/lib/ext/*.jar
      • 如果通過(guò)擴(kuò)展類加載器也加載不到另绩,就會(huì)通過(guò)應(yīng)用類加載器進(jìn)行加載儒陨。
        • 應(yīng)用類加載器專門加載:classpath中的jar包。(class文件)
  • java中為了保證類加載的安全笋籽,使用了雙親委派機(jī)制

    • 優(yōu)先從啟動(dòng)類加載器中加載蹦漠,這個(gè)稱為"父";"父"無(wú)法加載到车海,再?gòu)臄U(kuò)展類加載器加載笛园,
      這個(gè)稱為"母";雙親委派如果都加載不到,才會(huì)考慮從應(yīng)用類加載器中加載研铆,直到加載到為止闸度。

6,反射屬性Field

6.1蚜印,通過(guò)反射機(jī)制獲取一個(gè)對(duì)象的屬性(Filed)

Field 翻譯為字段,其實(shí)就是屬性成員

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    public static void main(String[] args) throws Exception {
        // 獲取整個(gè)類
        Class student = Class.forName("com.javaSE.reflects.Student");
        System.out.println("完整類名:" + student.getName());
        System.out.println("簡(jiǎn)寫(xiě)類名:" + student.getSimpleName());

        // 獲取類中所有public修飾的Field
        Field[] fields = student.getFields();
        System.out.println(fields.length);// 測(cè)試數(shù)組中只有2個(gè)元素
        // 取出整個(gè)Field
        Field f = fields[0];
        // 取出整個(gè)Field名字
        String fieldName = f.getName();
        System.out.println(fieldName);

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

        // 獲取類中所有的Field
        Field[] fs = student.getDeclaredFields();
        System.out.println(fs.length);// 測(cè)試數(shù)組中 有5個(gè)元素
        //  遍歷
        for (Field field : fs){
            // 獲取屬性名字
            System.out.println(field.getName());

            // 獲取屬性類型
            Class fieldType = field.getType();
            System.out.println(field.getName() + "完整類名:" + fieldType.getName() + "留量;簡(jiǎn)寫(xiě)類名:" + fieldType.getSimpleName());

            // 獲取屬性修飾符列表
            int mods = field.getModifiers();// 返回的修飾符是一個(gè)數(shù)字窄赋,每個(gè)數(shù)組是修飾符的代號(hào)。
            // 將代號(hào)數(shù)字轉(zhuǎn)換成修飾符
            String mos = Modifier.toString(mods);
            System.out.println(field.getName() + "修飾符代號(hào):" + mods + "楼熄;修飾符是:" + mos);

        }

        System.out.println("-------------通過(guò)反射機(jī)制忆绰,反編譯一個(gè)類的屬性---------------------------------------------");
        // 通過(guò)反射機(jī)制,反編譯一個(gè)類的屬性Field
        fbyStudent();
    }

    /**
     * 通過(guò)反射機(jī)制可岂,反編譯一個(gè)類的屬性
     * @throws ClassNotFoundException
     */
     static void fbyStudent() throws ClassNotFoundException {
         // 獲取整個(gè)類
         Class student = Class.forName("com.javaSE.reflects.Student");
//         Class student = Class.forName("java.lang.Integer");
         // 創(chuàng)建一個(gè)拼接字符串對(duì)象
         StringBuilder sr = new StringBuilder();
         // 開(kāi)始拼寫(xiě)
         sr.append(Modifier.toString(student.getModifiers()) + " class " + student.getSimpleName() + "{\n");
         Field[] fds = student.getDeclaredFields();
         for (Field field : fds){
             sr.append("\t");
             sr.append(Modifier.toString(field.getModifiers()));
             sr.append(" ");
             sr.append(field.getType().getSimpleName());
             sr.append(" ");
             sr.append(field.getName());
             sr.append(";\n");
         }
         sr.append("}");
         System.out.println(sr);
    }
}

// 反射屬性Field
class Student{
    // Field 翻譯為字段错敢,其實(shí)就是屬性成員
    // 4個(gè)Field,分別采用不同的訪問(wèn)控制權(quán)限修飾符
    public int no;
    private String name;
    protected int age;
    boolean sex;

    public static final double MIN_PI = 3.1415926;
    
}

6.2,通過(guò)反射機(jī)制操作一個(gè)對(duì)象的屬性(Field)

給屬性賦值set, 獲取屬性的值get缕粹。

public class ReflectTest07 {
    public static void main(String[] args) throws Exception {
        // 使用反射機(jī)制獲取/修改一個(gè)對(duì)象屬性的值
        Class cStudent = Class.forName("com.javaSE.reflects.Student");
        Object obj = cStudent.newInstance(); // obj就是Student對(duì)象稚茅。(底層調(diào)用無(wú)參構(gòu)造方法)

        // 獲取no屬性 (根據(jù)屬性的名稱獲取Field)
        Field noField = cStudent.getField("no");
        // 給obj對(duì)象的no屬性賦值
        noField.set(obj,1234);
        // 讀取no屬性的值
        System.out.println(noField.get(obj));

        // 獲取私有屬性name
        Field nameField = cStudent.getDeclaredField("name");
        // 打破封裝
        // 這樣設(shè)置之后,在外部也可以訪問(wèn)private的
        nameField.setAccessible(true);
        // 給name屬性賦值
        nameField.set(obj,"kitty");
        // 獲取name屬性的值
        System.out.println(nameField.get(obj));
    }
}

上篇:JavaSE進(jìn)階十 線程二
下篇:JavaSE進(jìn)階十一 反射機(jī)制二

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末平斩,一起剝皮案震驚了整個(gè)濱河市亚享,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绘面,老刑警劉巖欺税,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異揭璃,居然都是意外死亡晚凿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門瘦馍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歼秽,“玉大人,你說(shuō)我怎么就攤上這事情组≌芤” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵呻惕,是天一觀的道長(zhǎng)荆责。 經(jīng)常有香客問(wèn)我,道長(zhǎng)亚脆,這世上最難降的妖魔是什么做院? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上键耕,老公的妹妹穿的比我還像新娘寺滚。我一直安慰自己,他們只是感情好屈雄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布村视。 她就那樣靜靜地躺著,像睡著了一般酒奶。 火紅的嫁衣襯著肌膚如雪蚁孔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天惋嚎,我揣著相機(jī)與錄音杠氢,去河邊找鬼。 笑死另伍,一個(gè)胖子當(dāng)著我的面吹牛鼻百,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摆尝,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼温艇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了堕汞?” 一聲冷哼從身側(cè)響起中贝,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臼朗,沒(méi)想到半個(gè)月后邻寿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡视哑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年绣否,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挡毅。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒜撮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跪呈,到底是詐尸還是另有隱情段磨,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布耗绿,位于F島的核電站苹支,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏误阻。R本人自食惡果不足惜债蜜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一晴埂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寻定,春花似錦儒洛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至向胡,卻和暖如春恼蓬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捷枯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留专执,地道東北人淮捆。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像本股,于是被迫代替她去往敵國(guó)和親攀痊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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