1. 程式人生 > >Java反射實踐:從反射中理解Class

Java反射實踐:從反射中理解Class

寫在前面

今天在需求評審的時候,遇到了挺有意思的要求。需求是什麼樣子就不說了。總之完成這個需求需要一個呼叫系統api的操作。然而這個api因為並不穩定的原因。被谷歌hide掉了。
這個時候我們最直接的方式就是去通過反射去呼叫這個系統api。(當然這種方式治標不治本,因為既然被hide,就說明這個api很不穩定。所以這個版本可以用,有可能下個版本就沒了)
不過這裡我們不考慮這個問題,因為如題所說,這次主角是反射。
之前在用反射的時候,其實並沒有過去思考反射所帶來的東西。而這次在用反射的時候,自己多少加入了思考。
我在看JVM的時候:瞭解到一個概念,那就是我們在new一個物件的時候,JVM會先使用雙親委派機制去載入這個class物件,這個class物件會唯一的被加載出來。然後才是我們的例項物件建立到堆中。
之前我去上述的理解沒有任何概念,僅僅是當做文字給記了下來。直到我幾天再使用反射的時候,突然對上述的概念有了實質性的認識。
不過不著急,讓我們一點點的深入….

反射的使用

首先我們先宣告一個物件:

我是一個android彩筆開發,因此這裡的日誌使用了Log,而非是System.out.print。各位看官想看效果,可以換成自己熟悉的即可。

public class ReflectModel {
    private ReflectBean mReflectBean;
    private String mContent = "A";
    public int mNum = 1;
    public static int sNum = 666;

    public ReflectModel() {
        LogUtils.d(TAG, "ReflectModel(),無參構造方法執行"
); } public ReflectModel(String content) { LogUtils.d(TAG, "ReflectModel(String content),一個String引數的構造方法執行"); mContent = content; } private ReflectModel(String content, int num) { LogUtils.d(TAG, "ReflectModel(String content,int num),倆個引數的私有構造方法執行"); mContent = content; mNum = num; } public
void fun() { LogUtils.d(TAG, "我就是一個方法,在本例子中。我是被反射生成的物件呼叫的->mContent:" + mContent + "-mNum:" + mNum); } private void printContent() { LogUtils.d(TAG, "我就是私有的列印方法->mContent:" + mContent + "-mNum:" + mNum); } private void setContent(String content) { LogUtils.d(TAG, "我就是一個帶有一個String引數的私有方法->setContent:" + content); mContent = content; } public void printBean() { LogUtils.d(TAG, "我是ReflectBean的列印方法->mName:" + mReflectBean.getName()); } public static void staticFun() { LogUtils.d(TAG, "我是靜態方法staticFun"); } }

ReflectBean

public class ReflectBean {
    private String mName = "a";

    public ReflectBean(String name) {
        mName = name;
    }

    public String getName() {
        return mName;
    }
}

反射的使用:


public void fun() {
        //第一種方式獲取Class物件:產生一個Student物件的例項,以及一個唯一的Class物件。
        ReflectModel model1 = new ReflectModel();
        //獲取Class物件:這種方式沒什麼意義,既然有了物件的例項,何必再去反射
        Class modelClass = model1.getClass();

        //第二種方式獲取Class物件:需要我們導包,但是有些時候這個類是隱藏的(比如很多系統不穩定的類,@Hide)
        Class modelClass2 = ReflectModel.class;

        Class modelClass3 = null;
        try {
            //第三種方式獲取Class物件:類的全路徑
            modelClass3 = Class.forName("com.example.mbenben.studydemo.basenote.reflect.ReflectModel");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //開始進行具體內部操作,此時我們僅僅是拿到了ReflectModel的class這個物件,而非是這個ReflectModel物件的例項
        if (modelClass3 != null) {
            //獲取ReflectModel.class對應的所有構造方法(public的)
            Constructor[] constructors = modelClass3.getConstructors();
            for (Constructor constructor : constructors) {
                LogUtils.d(TAG, "當前獲取的構造方法:" + constructor.getName());
            }
            LogUtils.d(TAG, "----以上是public的----");
            //所有構造方法(包括:私有、受保護、預設、公有)
            constructors = modelClass3.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                LogUtils.d(TAG, "當前獲取的構造方法:" + constructor);
            }

            Object object = null;
            try {
                // 獲取共有無參建構函式
                Constructor con = modelClass3.getConstructor(null);
                // 呼叫此無參構造方法,那麼此時我們就會獲取到Reflect的例項物件了(預設返回object)
                ReflectModel reflectModel = (ReflectModel) con.newInstance();
                // 有了ReflectModel物件例項,我們就可以正常使用了
                /**
                 * 但是此時我們知道,我們強制型別轉成了我們想要的型別,但是我們上文中提到,有些類是hide的。
                 * 因此我們很多情況下,我們被限制只能得到object物件。
                 */
                reflectModel.fun();
                // 獲取私有的含有String和int引數的構造方法
                con = modelClass3.getDeclaredConstructor(new Class[]{String.class, int.class});
                // 此時我們獲取到了這個private的含參構造方法物件,但是因為private許可權原因,我們沒辦法直接呼叫newInstance()
                // 我們需要呼叫下面的方法,無視private修飾符(呼叫後,我們就可以執行private的構造方法)
                con.setAccessible(true);
                //呼叫私有倆參構造方法
                object = con.newInstance("B", 2);


                /**
                 * 反射呼叫方法
                 */
                //上訴提到,如果我們反射的類是hide,此時我們肯定沒辦法把Object轉成ReflectModel型別。因此,我們拿到這個例項後想要呼叫其方法,還需要使用反射的方式
                // private方法應該使用getDeclaredMethod()去獲取
                Method printContent = modelClass3.getDeclaredMethod("printContent", new Class[]{});
                //同樣因為private的原因,我們在執行方法時,要先清除許可權問題
                printContent.setAccessible(true);
                //使用object例項物件,呼叫printContent方法,因為沒有引數,所以傳null
                printContent.invoke(object, null);
                //生成名為setContent的含有一個String引數的方法物件
                Method setContent = modelClass3.getDeclaredMethod("setContent", new Class[]{String.class});
                setContent.setAccessible(true);
                //呼叫setContent方法,去改變mContent的值
                setContent.invoke(object, "C");
                //再次執行列印操作
                printContent.invoke(object, null);

                //呼叫靜態方法
                Method staticMethod = modelClass3.getMethod("staticFun", new Class[]{});
                //因為靜態方法屬於類,所以我們不需要傳例項物件,因為它在class被載入的時候,就已經被建立了。
                staticMethod.invoke(null, null);


                /**
                 * 反射呼叫變數
                 */
                //獲取所有public變數封裝的Field物件
                Field[] fields = modelClass3.getFields();
                //獲取所有變數封裝的Field物件
                fields = modelClass3.getDeclaredFields();

                //獲取private的mContent的變數
                Field content = modelClass3.getDeclaredField("mContent");
                content.setAccessible(true);
                Field num = modelClass3.getField("mNum");
                num.set(object, 3);
                //獲取static變數
                Field sNum = modelClass3.getField("sNum");
                LogUtils.d(TAG, "mNum在object例項物件中的值:" + num.get(object) + "-static變數sNum的值:" + sNum.get(null));
                //將object例項的mContent物件,設定為D
                content.set(object, "D");
                printContent.invoke(object, null);

                Field reflectBean = modelClass3.getDeclaredField("mReflectBean");
                Class reflectBeanClass = Class.forName("com.example.mbenben.studydemo.basenote.reflect.ReflectBean");
                Constructor beanCon = reflectBeanClass.getConstructor(new Class[]{String.class});
                Object beanObject = beanCon.newInstance("b");
                //給mReflectBean賦值
                reflectBean.setAccessible(true);
                reflectBean.set(object, beanObject);
                Method printBean = modelClass3.getMethod("printBean", new Class[]{});
                printBean.invoke(object, null);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

繼續理解

我們知道JVM在使用一個類的時候,會先去載入這個類。也就是生成一個Class物件。這個Class物件擁有我們的java程式碼的變數,方法結構。但是它並不是一個例項。因此我們在反射的時候,要先獲取構造方法物件,也就是Class返回給我們的Constructor。此時我們執行這個物件的newInstance,我們就初始化了這個Class,獲取了這個Class的例項。那麼此時我們就可以正常的呼叫方法了。
我們知道,static是屬於類,不需要初始化。那麼此時,反射也側面證實了這個問題。在我們使用static的變數和方法時,set或者invoke的引數傳的是null,因此此時我們使用的是Class物件中的變數和方法。

尾聲

時隔很久都還沒有寫部落格了,是因為自己的確不知道該寫些寫什麼,唉,迷茫,好菜…

相關推薦

Java反射實踐反射理解Class

寫在前面 今天在需求評審的時候,遇到了挺有意思的要求。需求是什麼樣子就不說了。總之完成這個需求需要一個呼叫系統api的操作。然而這個api因為並不穩定的原因。被谷歌hide掉了。 這個時候我們最直接的方式就是去通過反射去呼叫這個系統api。(當然這種方式治標

#Java技術分享程式碼角度理性分析Java的非同步與AIO

非同步程式設計提供了一個非阻塞的,事件驅動的程式設計模型。 這種程式設計模型利用系統中多核執行任務來提供並行,因此提高了應用的吞吐率。Java非同步程式設計通常需要使用Future,FutureTask和Callable,其中Future代表未來的某個結果,一般是向執行緒池提交任務時返回。 如果

java演算法題排序陣列刪除重複項

題目: 給定一個排序陣列,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後陣列的新長度。 不要使用額外的陣列空間,你必須在原地修改輸入陣列並在使用 O(1) 額外空間的條件下完成。 示例1: 給定陣列 nums = [1,1,2], 函式應該返回新的長度 2

Java之XML操作XML直接獲取數據

proc arraylist Coding xml文件 art ioe input roc demo   本文介紹如何將數據記錄在XML文件中,然後通過DOM4J直接從XML中讀取到數據。 依賴包: <dependency> <groupId&g

Java程式設計 KeyStore獲取私鑰與證書

用keytool可以生成JSK型別的keystore,其中可以存放很多個Key(私鑰和對應的證書)。import java.io.*; import java.security.*; import java.secur

劍指Offer面試題15(Java版)鏈表倒數第K個結點

head 計數器 easy sta 相同 ret white style 輸出 題目: 輸入一個鏈表。輸出該鏈表中倒數第k哥結點。 為了符合大多數人的習慣,本題從1開始計數。即鏈表的尾結點是倒數第1個結點。 比如一個鏈表有6個結點。從頭結點開始它們的值依次是1。2。

分布式學習最佳實踐分布式系統的特征開始(附思維導圖)

擴展 問題 sca ref 調度 這也 集中 技術 park     我的探索歷程   這一部分,與分布式不大相關,記錄的是我是如何在分布式學習這條道路上摸索的,不感興趣的讀者請直接跳到下一章。   過去的一年,我在分布式學習這條道路上苦苦徘徊,始終沒有找到一個好的學

多執行緒——生活理解什麼是多執行緒

     每一個程式可以包含至少一個執行緒,而多個執行緒之間可以“併發”執行。        在介紹執行緒前先來用生活中最常見的一個小例子來理解什麼是執行緒:   &nbs

JAVA---面向物件之生活抽取例項物件

public class Car{ private String brand; private int num; private String colour; private int seats; //建構函式 public Car(String brand){

java使用poi把資料庫取出的資料寫入到excel檔案並儲存到指定檔案路徑

  有時候我們要把從資料庫中取出的資料匯入到excel中,使取到的資料看起來更加的直觀和方便,在java中如何實現取到的資料匯入到excel中呢?以下就是使用poi工具吧資料寫入excel檔案中的解決方法: Excel表格副檔名有.xlsx和.xls兩種格式     &n

#Java乾貨分享面試過程經常碰到的9大難題解析

第一,談談final, finally, finalize的區別。 final?修飾符(關鍵字)如果一個類被宣告為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被宣告為 abstract的,又被宣告為final的。將變數或方法宣告為final,可以保證它們在使用中

JAVA實驗四鍵盤錄入若干個學生的姓名和分數

題目 編寫一個程式,使用者可以從鍵盤錄入若干個學生的姓名和分數(程式每次提示使用者輸入“Y”或“N”決定是否繼續錄入學生資訊,如果使用者輸入“N”則使用者輸入完畢。輸入的“Y”、“N”不區分大小寫)。使用者錄入完畢後,程式按成績由高到低的順序輸出學生的姓名和分數(姓名和分數之間用一個空格分割

分散式學習最佳實踐分散式系統的特徵開始(附思維導圖)

什麼是分散式系統 回到頂部   分散式系統是由一組通過網路進行通訊、為了完成共同的任務而協調工作的計算機節點組成的系統。分散式系統的出現是為了用廉價的、普通的機器完成單個計算機無法完成的計算、儲存任務。其目的是利用更多的機器,處理更多的資料。   首先需要明確的是,只

深入淺出經典面試題瀏覽器輸入URL到頁面載入發生了什麼 - Part 2

備註: 因為文章太長,所以將它分為三部分,本文是第二部分。 第一部分:深入淺出經典面試題:從瀏覽器中輸入URL到頁面載入發生了什麼 - Part 1 TCP連線 DNS解析返回域名的IP之後,接下來就是瀏覽器要和該IP建立TCP連線了。為什麼是TCP而不是UDP?那是因為HTTP是基於TCP上的。

深入淺出經典面試題瀏覽器輸入URL到頁面載入發生了什麼 - Part 3

深入淺出經典面試題:從瀏覽器中輸入URL到頁面載入發生了什麼 - Part 3 備註: 因為文章太長,所以將它分為三部分,本文是第三部分。 第一部分:深入淺出經典面試題:從瀏覽器中輸入URL到頁面載入發生了什麼 - Part 1 第二部分:深入淺出經典面試題:從瀏覽器中輸入URL到頁

Java入門系列-07-控制檯接收輸入

這篇文章幫你使用Scanner類從控制檯接收輸入 從控制檯接收字串 敲一敲: import java.util.Scanner; public class DemoScanner { public static void main(String[] args) { Scanner i

達達O2O後臺架構演進實踐0到4000高並發請求背後的努力

net 表數據 接下來 發布 消息 出現 重復 圖片文件 方案 1、引言 達達創立於2014年5月,業務覆蓋全國37個城市,擁有130萬註冊眾包配送員,日均配送百萬單,是全國領先的最後三公裏物流配送平臺。 達達的業務模式與滴滴以及Uber很相似,以眾包的方式利

24點遊戲是經典的紙牌益智遊戲。 常見遊戲規則 撲克每次取出4張牌。使用加減乘除,第一個能得出24者為贏。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求程式設計解決24點遊戲

24點遊戲是經典的紙牌益智遊戲。 常見遊戲規則: 從撲克中每次取出4張牌。使用加減乘除,第一個能得出24者為贏。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求程式設計解決24點遊戲。 基本要求: 隨機生成4個代表撲克牌牌面的數字字母,程式自動列

0day --第11章--11.4繞過SafeSEH

實驗原理: SHE的安全校驗存在一個嚴重的缺陷:如果SHE中異常函式指標執行堆區,即使安全校驗發現SHE不可信,仍會呼叫已經被修改過的異常處理函式!因此基於這個原理我們將shellcode佈置到堆區中執行! 實驗環境: Winxp sp3 vs2010 Rele

Java設計模式之[遊戲的兵種狀態轉換]分析狀態(State)模式

  假設我們正在做一個即時戰略遊戲,我們設計一個兵種,他在剛剛生產出來的時候是步兵,但是他可以切換武器,第一次切換會變成弓箭手,第二次切換會變成舉著盾牌的裝甲兵,第三次切換則又變成了步兵……如何實現這個切換的機制?我們一開始會想到,在步兵這個類中加入switch語句,然而這