Java - 打破訪問權(quán)限的方式

本文為《Java 編程思想》14.9節(jié)的讀書筆記,文章內(nèi)容僅限交流使用!

我們先看看使用接口時(shí)方法的訪問權(quán)限(就這一個(gè)小標(biāo)題括堤,真沒地方加小標(biāo)題啊!)

使用interface關(guān)鍵字定義的接口主要是為了實(shí)現(xiàn)代碼間的隔離悄窃,用以避免 使用接口的代碼實(shí)現(xiàn)類 之間的耦合讥电。通常一個(gè)實(shí)現(xiàn)了某個(gè)接口的類中擁有自己的非來自于接口的方法,向上轉(zhuǎn)型為接口的時(shí)候轧抗,就無法通過轉(zhuǎn)型后的接口對(duì)象來調(diào)用子類自己另添加的方法恩敌。

這個(gè)是完全合理的,但是可以使用類型信息繞過這種限制横媚,可以對(duì)實(shí)現(xiàn)類中的非接口方法進(jìn)行調(diào)用纠炮。

首先定義一個(gè)接口A:

package com.henevalf.learn.typeinfo

public interface A {
    void f();
}

然后用類B實(shí)現(xiàn)接口A,在后面的InterfaceViolation中我們可以看到如何繞過限制 :

package com.henvealf.learn.typeinfo;

class B implements A {

    @Override
    public void f() {

    }

    public void g(){

    }
}

public class InterfaceViolation {
    private A a;

    public InterfaceViolation(A a) {
        this.a = a;
    }

    public void invoke() {
        a.f();
        //a.g();
        System.out.println(a.getClass().getName());
        // 看見沒!先檢查一下類型灯蝴,然后轉(zhuǎn)型調(diào)用抗碰。。绽乔。弧蝇。滋滋滋,真不要臉折砸。
        if(a instanceof B) {
            B b = (B)a;
            b.g();
        }
    }

    public static void main(String[] args){
        InterfaceViolation inv = new InterfaceViolation(new B());
    }

}

嗯看疗,沒錯(cuò),在invoke()方法里睦授,就是強(qiáng)制類型轉(zhuǎn)換两芳,就可以使用在接口中未定義的方法g(),在本例中先用 instanceof 檢測(cè)了一下類型是否可轉(zhuǎn)去枷。 想必也都使用過這種方式怖辆。這樣并沒有什么不妥,可以正常運(yùn)行删顶,但是他違背了我們當(dāng)初使用接口的本意竖螃,類 InterfaceViolation 與類 B 無意之間就增加耦合。

如果你有難以克制的強(qiáng)迫癥逗余,就是不希望使用你的類的其他程序員這樣做特咆。那么有兩種解決方法:

  1. 到他面前義正言辭的告訴他,不許你這樣用录粱。然而誰理你D甯瘛!
  2. 自己在代碼中進(jìn)行控制啥繁。

怎么控制那菜职?最簡(jiǎn)單的方式就是對(duì)實(shí)現(xiàn)類使用包訪問權(quán)限。意思是將你的實(shí)現(xiàn)類放在一個(gè)包中旗闽,并設(shè)置實(shí)現(xiàn)類只能在包中才能被訪問到酬核,使用你類的程序員就找不到你的實(shí)現(xiàn)類的存在蜜另,就無法完成轉(zhuǎn)型,看代碼:

package com.henvealf.learn.typeinfo.packageaccess;

import com.henvealf.learn.typeinfo.A;

/**
 * Created by Henvealf on 2016/9/10.
 */

class C implements A {

    @Override
    public void f() {
        System.out.println("public C.f()");
    }

    public void g() {
        System.out.println("public C.g()");
    }
    void u() {
        System.out.println("package C.u()");
    }
    protected void v() {
        System.out.println("protected C.v()");
    }

    public void w() {
        System.out.println("private C.w()");
    }
}
public class HiddenC {
    public static A makeA(){
        return new C();
    }
}

注意包名愁茁,現(xiàn)在A的實(shí)現(xiàn)類C是在一個(gè)獨(dú)立的包中蚕钦,在這個(gè)包里面亭病,唯一對(duì)外開放的public既是HiddenC,它有一個(gè)靜態(tài)方法鹅很,返回C的一個(gè)對(duì)象,這樣的話你就無法在包的外部調(diào)用A以外的任何方法了罪帖,因?yàn)槟銦o法在包的外部找到C的類型信息促煮,就無法進(jìn)行轉(zhuǎn)型:

package com.henvealf.learn.typeinfo;

import com.henvealf.learn.typeinfo.packageaccess.HiddenC;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *
 * Created by Henvealf on 2016/9/10.
 */
public class HiddenImplementation {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        A a  = HiddenC.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        /*if(a instanceof C) { 編譯錯(cuò)誤,因?yàn)檎也坏紺
            .....
            C c = (C)a;
            c.g();
        }*/
        
        
        //我的天整袁,反射竟然可以讓你繼續(xù)調(diào)用個(gè)g()
        callHiddenMethod(a,"g");
        // 甚至私有方法都可以
        callHiddenMethod(a,"u");
        callHiddenMethod(a,"v");
        callHiddenMethod(a,"w");
    }

    static void callHiddenMethod(Object a, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 先獲得類型信息(Class對(duì)象)菠齿,然后獲取其Method對(duì)象。
        Method g = a.getClass().getDeclaredMethod(methodName);
        // 就是這里坐昙,可以允許訪問私有方法绳匀。
        g.setAccessible(true);
        //調(diào)用
        g.invoke(a);
    }

}

可以發(fā)現(xiàn),在包外我們無法找到類型C炸客,無法進(jìn)行相應(yīng)的轉(zhuǎn)換疾棵。除此之外,可以看到竟然可以通過反射來調(diào)用對(duì)象C中的方法痹仙。甚至是私有方法是尔,其原因就是在Method對(duì)象上調(diào)用了setAccessible(true),顧名思義就是設(shè)置訪問權(quán)限。

你可能會(huì)想开仰,要想使用這種方式拟枚,就必須要獲得類C的方法列表,如果我們得到的只是類C編譯后的字節(jié)碼(.class文件)众弓,我們大可以使用javap來反編譯字節(jié)碼恩溅,以來的到方法列表。

反射除了能夠突破包訪問的權(quán)限谓娃,還能夠訪問到私有內(nèi)部類暴匠,匿名類的所有方法。

當(dāng)然除了方法傻粘,對(duì)于域(字段/屬性)每窖,也同樣如此,不過在域的訪問中有一個(gè)特殊的弦悉,就是final字段窒典,它只能被讀取,不通過反射被再次賦值稽莉。

你可能會(huì)問瀑志,這樣反射不就無法無天了嗎!什么都能夠訪問到。而反射存在原因就是為了給程序員提供一個(gè)后門劈猪,能夠讓程序員解決一些難以解決的某些特定類型的問題(至于什么樣的問題我也不清楚)昧甘。如果沒有反射,這些問題將會(huì)難以或者不可能解決战得,所以說反射的好處是毋庸置疑的充边。

End...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市常侦,隨后出現(xiàn)的幾起案子浇冰,更是在濱河造成了極大的恐慌,老刑警劉巖聋亡,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肘习,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坡倔,警方通過查閱死者的電腦和手機(jī)漂佩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罪塔,“玉大人投蝉,你說我怎么就攤上這事」父ぃ” “怎么了墓拜?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)请契。 經(jīng)常有香客問我咳榜,道長(zhǎng),這世上最難降的妖魔是什么爽锥? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任涌韩,我火速辦了婚禮,結(jié)果婚禮上氯夷,老公的妹妹穿的比我還像新娘臣樱。我一直安慰自己,他們只是感情好腮考,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布雇毫。 她就那樣靜靜地躺著,像睡著了一般踩蔚。 火紅的嫁衣襯著肌膚如雪棚放。 梳的紋絲不亂的頭發(fā)上柿顶,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天基协,我揣著相機(jī)與錄音绸吸,去河邊找鬼鳞青。 笑死耀找,一個(gè)胖子當(dāng)著我的面吹牛忠怖,可吹牛的內(nèi)容都是我干的虾宇。 我是一名探鬼主播昭娩,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼峦甩!你這毒婦竟也來了赘来?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤穴店,失蹤者是張志新(化名)和其女友劉穎撕捍,沒想到半個(gè)月后拿穴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泣洞,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年默色,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了球凰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腿宰,死狀恐怖呕诉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吃度,我是刑警寧澤甩挫,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站椿每,受9級(jí)特大地震影響伊者,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜间护,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一亦渗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汁尺,春花似錦法精、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辽装,卻和暖如春帮碰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背如迟。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工收毫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攻走,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓此再,卻偏偏與公主長(zhǎng)得像昔搂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子输拇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理摘符,服務(wù)發(fā)現(xiàn),斷路器策吠,智...
    卡卡羅2017閱讀 134,637評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,846評(píng)論 25 707
  • 有人說:“人這一輩子啊逛裤,難得糊涂;又有人說:“最重要的是難得清醒猴抹。 小時(shí)候愛吃冰棍带族,愛吃零食;因?yàn)楦改刚f這些是...
    萌里花閱讀 216評(píng)論 0 2
  • 再一次 想起你的感覺 就像一個(gè)餓了許久沒有進(jìn)食空蕩蕩的胃 燒灼的疼 莫名焦躁 只能拼命的做事 轉(zhuǎn)移自己的注意力 卻...
    晴歌晴歌閱讀 196評(píng)論 1 0
  • 自己給自己雙手服務(wù)择克,自己給自己內(nèi)心撫平,盡量通過這些活動(dòng)前普,發(fā)泄自己的不良情緒肚邢,不讓不良情緒左右我的心靈。
    用愛感動(dòng)世界閱讀 226評(píng)論 0 0