探索Java中隱藏的訪問(wèn)權(quán)限synthetic

首先參考一下 Java8 虛擬機(jī)規(guī)范中對(duì)The Synthetic Attribute的相關(guān)描述:

A class member that does not appear in the source code must be marked using a Synthetic attribute, or else it must have its ACC_SYNTHETIC flag set. The only exceptions to this requirement are compiler-generated methods which are not considered implementation artifacts, namely the instance initialization method representing a default constructor of the Java programming language, the class initialization method, and the Enum.values() and Enum.valueOf() methods.

The Synthetic attribute was introduced in JDK 1.1 to support nested classes and interfaces.

一個(gè)類(lèi)的源碼中未出現(xiàn)的成員必須使用synthetic屬性或者ACC_SYNTHETIC標(biāo)志,但例外是編譯期所生成的默認(rèn)構(gòu)造函數(shù)、類(lèi)初始化方法,以及Enum.values()Enum.valueOf()。

首先我來(lái)嘗試著編譯出一些存在synthetic訪問(wèn)權(quán)限的字節(jié)碼:

public final class Singleton {
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
    private static final class Holder {
        private static final Singleton INSTANCE = new Singleton();
        private Holder() {
            throw new UnsupportedOperationException();
        }
    }
    private Singleton() {
        // TODO
    }
}

這是一個(gè)典型的靜態(tài)內(nèi)部類(lèi)單例模式,人工反編譯結(jié)果如下:

public final class Singleton {
    public static Singleton getInstance() {
        return Holder.access$000();
    }
    private static final class Holder {
        private static final Singleton INSTANCE;
        static {
            INSTANCE = new Singleton(null);
        }
        private Holder() {
            throw new UnsupportedOperationException();
        }
        static Singleton access$000() { // synthetic
            return INSTANCE;
        }
    }
    private Singleton() {
        super();
        // TODO
    }
    Singleton(Singleton$1 var0) { // synthetic
        this();
    }
    static class Singleton$1 { // synthetic
    }
}

在 Java8 版本下,編譯出的代碼實(shí)際上會(huì)多出來(lái)兩個(gè)方法和一個(gè)類(lèi),從而增大包體積,如果比較在意二進(jìn)制包體積的大小,建議在這種場(chǎng)景下不要添加private訪問(wèn)權(quán)限修飾符。

private訪問(wèn)權(quán)限我們很熟悉,私有成員僅被允許在其所在的類(lèi)以及與其有嵌套關(guān)系的類(lèi)中使用,在 Java8 版本下實(shí)現(xiàn)嵌套類(lèi)之間訪問(wèn)私有成員的方式,就是在編譯期額外生成一些包私有的方法,比如上例中的三處。

嵌套類(lèi)之間訪問(wèn)私有方法與私有變量,編譯期會(huì)額外生成帶有ACC_SYNTHETIC標(biāo)志的包私有方法去間接地訪問(wèn)私有成員,這些方法的名字形如 access$000,數(shù)字與這些自動(dòng)生成的方法的順序有關(guān)。對(duì)于構(gòu)造函數(shù),由于其名稱(chēng)必然為<init>,無(wú)法用改名的方式實(shí)現(xiàn),因此編譯器會(huì)額外定義一個(gè)類(lèi),并以重載的方式自動(dòng)生成包私有構(gòu)造函數(shù)。實(shí)際上在編譯后將上例中自動(dòng)生成的類(lèi)直接刪除也不會(huì)影響程序正常運(yùn)行,因?yàn)檫@個(gè)參數(shù)被傳入null,所以這個(gè)類(lèi)并不會(huì)被加載。

在我上次一篇文章講switch枚舉的時(shí)候所生成的類(lèi),和這里生成的類(lèi)是同一個(gè),那個(gè)用于switch枚舉的int[]也帶有ACC_SYNTHETIC標(biāo)志。

由于這些類(lèi)、方法、字段在源代碼中并不存在,因此當(dāng)發(fā)布二進(jìn)制包后,引用這些二進(jìn)制包的代碼,也不應(yīng)訪問(wèn)得到這些帶有ACC_SYNTHETIC標(biāo)志的符號(hào),于是編譯器將以找不到符號(hào)為理由拒絕編譯,甚至即使帶有ACC_PUBLIC符號(hào)也不行。

可以通過(guò)使用asm框架修改字節(jié)碼的方式,強(qiáng)行添加ACC_SYNTHETIC標(biāo)志,從而將一部分public的API隱藏起來(lái)。我已經(jīng)通過(guò)實(shí)驗(yàn)證實(shí)了這種操作的可行性,在此就不詳細(xì)展開(kāi)了。

參考文獻(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開(kāi)了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽(tīng)閱讀 10,905評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書(shū)法閱讀 4,496評(píng)論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來(lái)的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過(guò)就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,762評(píng)論 2 7

友情鏈接更多精彩內(nèi)容