Java反射機(jī)制及其應(yīng)用

一戳寸、反射的概述

JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)郑诺,都能夠知道這個(gè)的所有 屬性和方法;對(duì)于任意一個(gè)對(duì)象杉武,都能夠調(diào)用它的任意一個(gè)方法和屬性辙诞;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。

Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class艺智,獲悉其完整構(gòu)造(但不包括methods定義)倘要,并生成其對(duì)象實(shí)體、或?qū)ζ?code>fields設(shè)值、或喚起其methods封拧。

要想解剖一個(gè)類,必須先要獲取到該類字節(jié)碼文件對(duì)象志鹃。而解剖使用的就是Class類中的方法.所以先要獲取到每一個(gè)字節(jié)碼文件對(duì)應(yīng)的Class類型的對(duì)象

使用的前提條件:必須先得到代表的字節(jié)碼的Class泽西,Class類用于表示.class文件(字節(jié)碼)反射是框架設(shè)計(jì)的靈魂曹铃,以上的總結(jié)就是什么是反射

反射就是把java類中的各種成分映射成一個(gè)個(gè)的Java對(duì)象

例如:一個(gè)類有:成員變量、方法捧杉、構(gòu)造方法陕见、包等等信息,利用反射技術(shù)可以對(duì)一個(gè)類進(jìn)行解剖味抖,把個(gè)個(gè)組成部分映射成一個(gè)個(gè)對(duì)象评甜。
(其實(shí):一個(gè)類中這些成員方法、構(gòu)造方法仔涩、在加入類中都有一個(gè)類來描述)
如圖是類的正常加載過程:反射的原理在于class對(duì)象忍坷。
加載的時(shí)候:Class對(duì)象的由來是將class文件入內(nèi)存,并為之創(chuàng)建一個(gè)Class對(duì)象熔脂。

在運(yùn)行期間佩研,如果我們要產(chǎn)生某個(gè)類的對(duì)象,Java虛擬機(jī)(JVM)會(huì)檢查該類型的Class對(duì)象是否已被加載霞揉。如果沒有被加載旬薯,JVM會(huì)根據(jù)類的名稱找到.class文件并加載它。一旦某個(gè)類型的Class對(duì)象已被加載到內(nèi)存适秩,就可以用它來產(chǎn)生該類型的所有對(duì)象绊序。

image.png

反射機(jī)制主要提供的功能

  • 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;
  • 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象秽荞;
  • 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法政模;
  • 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;

Java中創(chuàng)建對(duì)象大概有這幾種方式:

  • 1蚂会、使用new關(guān)鍵字:這是我們最常見的也是最簡(jiǎn)單的創(chuàng)建對(duì)象的方式

  • 2、使用Clone的方法:無論何時(shí)我們調(diào)用一個(gè)對(duì)象的clone方法耗式,JVM就會(huì)創(chuàng)建一個(gè)新的對(duì)象胁住,將前面的對(duì)象的內(nèi)容全部拷貝進(jìn)去

  • 3、使用反序列化:當(dāng)我們序列化和反序列化一個(gè)對(duì)象刊咳,JVM會(huì)給我們創(chuàng)建一個(gè)單獨(dú)的對(duì)象

  • 4彪见、反射

獲取類的字節(jié)碼:

  • (1)、Class.forName("com.test.User");

  • (2)娱挨、對(duì)象.getClass();

  • (3)余指、類名.class;

java中的Class中一些重要的方法

  • public Annotation[] getAnnotations ()獲取這個(gè)類中所有注解
  • getClassLoader()獲取加載這個(gè)類的類加載器
  • getDeclaredMethods() 獲取這個(gè)類中的所有方法
  • getReturnType()獲取方法的返回類型
  • getParameterTypes() 獲取方法的傳入?yún)?shù)類型*
  • isAnnotation() 測(cè)試這類是否是一個(gè)注解類
  • getDeclaredConstructors() 獲取所有的構(gòu)造方法
  • getDeclaredMethod(String name, Class… parameterTypes) 獲取指定的構(gòu)造方法(參數(shù):參數(shù)類型.class
  • getSuperclass() 獲取這個(gè)類的父類
  • getInterfaces()獲取這個(gè)類實(shí)現(xiàn)的所有接口
  • getFields() 獲取這個(gè)類中所有被public修飾的成員變量
  • getField(String name) 獲取指定名字的被public修飾的成員變量
  • newInstance() 返回此Class所表示的類,通過調(diào)用默認(rèn)的(即無參數(shù))構(gòu)造函數(shù)創(chuàng)建的一個(gè)新實(shí)例

注意:

在反射私有的構(gòu)造函數(shù)時(shí),用普通的clazz.getConstructor()會(huì)報(bào)錯(cuò)酵镜,因?yàn)樗撬接械牡锏铮蕴峁┝藢iT反射私有構(gòu)造函數(shù)的方法。
//讀取私有的構(gòu)造函數(shù)淮韭,用這個(gè)方法讀取完還需要設(shè)置一下暴力反射才可以

  • clazz.getDeclaredConstructor(int.class);
    //暴力反射
  • c.setAccessible(true);

User類

package com.test;

public class User {
    private int id;
    private String name;
    private String age;
    public User() {
    }
    public User(int id) {
        this.id = id;
    }
    private User(String name) {
        this.name = name;
    }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private void setIdName(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

實(shí)例化反射對(duì)象 構(gòu)造方法

constructor.setAccessible(true)是獲取到反射的權(quán)限垢粮,如果構(gòu)造方法為私有的則需要設(shè)置為true, 參數(shù)值為true靠粪,打開禁用訪問控制檢查蜡吧,setAccessible(true)并不是將方法的訪問權(quán)限改成了public,而是取消java的權(quán)限控制檢查占键。所以即使是public方法昔善,其accessible屬相默認(rèn)也是false

            /***
             * 采用默認(rèn)構(gòu)造方法實(shí)例化對(duì)象
             * com.test.User想反射類的全路徑
             */
            Class mClass = Class.forName("com.test.User");
            //得到User類的構(gòu)造方法,可以創(chuàng)建對(duì)象
            Constructor constructor = mClass.getConstructor();
            Object mStu = constructor.newInstance();
            /***
             * 采用參數(shù)構(gòu)造
             */
            Class mClass2 = Class.forName("com.test.User");
            Constructor constructor2 = mClass2.getConstructor( int.class,String.class);
            constructor2.setAccessible(true);
            Object object2 = constructor2.newInstance(10,"xx1");
            /***
             * 調(diào)用private的有參構(gòu)造方法
             */
            Class mClass3 = Class.forName("com.test.User");
            Constructor constructor3 = mClass3.getDeclaredConstructor(String.class);
            //獲取到反射的權(quán)限畔乙,如果構(gòu)造方法為私有的則需要設(shè)置為true君仆。
            constructor3.setAccessible(true);
            Object object3 = constructor3.newInstance("xx2");

反射調(diào)用方法

            /***
             * 調(diào)用無參的方法
             */
            Method nameMethod = mClass.getDeclaredMethod("getName");
            nameMethod.setAccessible(true);
            nameMethod.invoke(object1);
            /***
             * 反射調(diào)用setName方法
             */
            Method getNameMethod = mClass.getDeclaredMethod("setName", String.class);
            getNameMethod.invoke(object3, "一個(gè)參數(shù)");

            /***
             * 反射調(diào)用setIdName
             */
            Method setNameAndAgeMethod = mClass.getDeclaredMethod("setIdName", int.class, String.class);
            setNameAndAgeMethod.setAccessible(true);
            setNameAndAgeMethod.invoke(object2,  8,  "2個(gè)參數(shù)");

反射設(shè)置屬性的值

    Field nameField = mClass.getDeclaredField("name");
    nameField.setAccessible(true);
    nameField.set(object3, "xxxx");

Hook

package com.cc.reflection;


import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class HookClickListenerUtils {

    private static HookClickListenerUtils mHookClickListenerUtils;

    private HookClickListenerUtils() {
    }

    public static HookClickListenerUtils getInstance() {
        synchronized ("getInstance") {
            if (mHookClickListenerUtils == null) {
                mHookClickListenerUtils = new HookClickListenerUtils();
            }
        }
        return mHookClickListenerUtils;
    }

    /***
     * 遞歸調(diào)用
     * @param decorView
     */
    public void hookDecorViewClick(View decorView) {
        if (decorView instanceof ViewGroup) {
            int count = ((ViewGroup) decorView).getChildCount();
            for (int i = 0; i < count; i++) {
                if (((ViewGroup) decorView).getChildAt(i) instanceof ViewGroup) {
                    hookDecorViewClick(((ViewGroup) decorView).getChildAt(i));
                } else {
                    hookViewClick(((ViewGroup) decorView).getChildAt(i));
                }
            }
        } else {
            hookViewClick(decorView);
        }
    }

    public void hookViewClick(View view) {
        try {
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                }
            });
            Class viewClass = Class.forName("android.view.View");
            Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
            if (!getListenerInfoMethod.isAccessible()) {
                getListenerInfoMethod.setAccessible(true);
            }
            // 反射view中的getListenerInfo方法
            Object listenerInfoObject = getListenerInfoMethod.invoke(view);

            // 第二步:獲取到view中的ListenerInfo中的mOnClickListener屬性
            Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
            Field mOnClickListenerField = mListenerInfoClass.getDeclaredField("mOnClickListener");
            mOnClickListenerField.setAccessible(true);
            // 通過Java反射機(jī)制操作成員變量, set 和 get,View.OnClickListener為Field.get()的值
            mOnClickListenerField.set(listenerInfoObject, new HookClickListener((View.OnClickListener) mOnClickListenerField.get(listenerInfoObject)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class HookClickListener implements View.OnClickListener {

        private View.OnClickListener onClickListener;

        public HookClickListener(View.OnClickListener onClickListener) {
            this.onClickListener = onClickListener;
        }
        @Override
        public void onClick(View v) {
            Toast.makeText(v.getContext(), "hook住點(diǎn)擊事件了,禽獸", Toast.LENGTH_SHORT).show();
            if (onClickListener != null) {
                onClickListener.onClick(v);
            }
        }
    }

}

Java基礎(chǔ)之—反射(非常重要)
夯實(shí)JAVA基本之二 —— 反射(1):基本類周邊信息獲取
Java中的反射機(jī)制介紹
android的hook技術(shù)之hook所有view的監(jiān)聽
學(xué)習(xí)java應(yīng)該如何理解反射啸澡?
java反射之Method的invoke方法實(shí)現(xiàn)
Android反射機(jī)制:手把手教你實(shí)現(xiàn)反射
Android反射

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袖订,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嗅虏,更是在濱河造成了極大的恐慌洛姑,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皮服,死亡現(xiàn)場(chǎng)離奇詭異楞艾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)龄广,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門硫眯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人择同,你說我怎么就攤上這事两入。” “怎么了敲才?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵裹纳,是天一觀的道長。 經(jīng)常有香客問我紧武,道長剃氧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任阻星,我火速辦了婚禮朋鞍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己滥酥,他們只是感情好更舞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恨狈,像睡著了一般疏哗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上禾怠,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天返奉,我揣著相機(jī)與錄音,去河邊找鬼吗氏。 笑死芽偏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弦讽。 我是一名探鬼主播污尉,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼往产!你這毒婦竟也來了被碗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤仿村,失蹤者是張志新(化名)和其女友劉穎馁蒂,沒想到半個(gè)月后淹办,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肛走,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡映凳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畏鼓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酱酬。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖云矫,靈堂內(nèi)的尸體忽然破棺而出膳沽,到底是詐尸還是另有隱情,我是刑警寧澤让禀,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布贵少,位于F島的核電站,受9級(jí)特大地震影響堆缘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜普碎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一吼肥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦缀皱、人聲如沸斗这。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽表箭。三九已至,卻和暖如春钮莲,著一層夾襖步出監(jiān)牢的瞬間免钻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工崔拥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留极舔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓链瓦,卻偏偏與公主長得像拆魏,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慈俯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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

  • 反射注解動(dòng)態(tài)代理相關(guān)閱讀 Java基礎(chǔ):類加載器 Java基礎(chǔ):反射 Java基礎(chǔ):注解 Java基礎(chǔ):動(dòng)態(tài)代理 ...
    JackChen1024閱讀 927評(píng)論 3 27
  • 一渤刃、類的加載 (一) 定義及過程: 當(dāng)程序需要使用某個(gè)類的時(shí)候,如果這個(gè)類還沒有被加載到內(nèi)存中贴膘,則系統(tǒng)會(huì)通過加載卖子、...
    VictorBXv閱讀 320評(píng)論 0 2
  • 整理來自互聯(lián)網(wǎng) 1,JDK:Java Development Kit步鉴,java的開發(fā)和運(yùn)行環(huán)境揪胃,java的開發(fā)工具...
    Ncompass閱讀 1,538評(píng)論 0 6
  • 一:java概述: 1,JDK:Java Development Kit氛琢,java的開發(fā)和運(yùn)行環(huán)境喊递,java的開發(fā)...
    慕容小偉閱讀 1,790評(píng)論 0 10
  • 一:java概述:1,JDK:Java Development Kit阳似,java的開發(fā)和運(yùn)行環(huán)境骚勘,java的開發(fā)工...
    ZaneInTheSun閱讀 2,654評(píng)論 0 11