簡介
巢狀是一種訪問控制上下文,它允許多個class同屬一個邏輯程式碼塊,但是被編譯成多個分散的class檔案,它們訪問彼此的私有成員無需通過編譯器新增訪問擴充套件方法。
動機
很多jvm語言支援在一個原始檔中放多個class。這對於使用者是透明的,使用者認為它們在一個class中,所以希望它們共享同一套訪問控制體系。為了達到目的,編譯器需要經常需要通過附加的access bridge擴大private成員的訪問許可權到package。這種bridge和封裝相違背,並且輕微的增加程式的大小,會干擾使用者和工具。所以我們希望一種更直接,更安全,更透明的方式。
一個更大的坑就是反射的時候會有問題。當使用java.lang.reflect.Method.invoke從一個nestmate呼叫另一個nestmate私有方法時會報IllegalAccessError錯誤。這個是讓人不能理解的,因為反射應該和原始碼級訪問擁有相同許可權。
話不多說,看段程式碼
public class JEP181 {
public static class Nest1 {
private int varNest1;
public void f() throws Exception {
final Nest2 nest2 = new Nest2();
//這裡沒問題
nest2.varNest2 = 2;
final Field f2 = Nest2.class.getDeclaredField("varNest2");
//這裡在java8環境下會報錯,在java11中是沒問題的
f2.setInt(nest2, 2);
System.out.println(nest2.varNest2);
}
}
public static class Nest2 {
private int varNest2;
}
public static void main(String[] args) throws Exception {
new Nest1().f();
}
}
複製程式碼
為了提供一種更大的,更廣泛的,不僅僅是java語言的巢狀型別,並且補足訪問控制檢測的不足,引入了兩個新的class檔案屬性。定義了兩種nest member,一種叫nest host(也叫top-level class),它包含一個NestMembers屬性用於確定其他靜態的nest members,其他的就是nest member,它包含一個NestHost屬性用於確定它的nest host。
大家可以看一下上述程式碼的class檔案詳情。
JVM針對巢狀成員的訪問控制
調整了jvm訪問規則,增加了如下條款:
一個field或method R可以被class或interface D訪問,當且僅當如下任一條件為真:
- … …(原條款不變)
- R是私有的,並且宣告在另一個class或interface C中,並且C和D是nestmates
C和D是nestmates表名他們肯定有一個相同的host
這個鬆散的訪問規則會作用在如下幾個地方:(這一段我就貼原文了,感覺翻譯過來味道就變了)
- Resolving fields and methods (JVMS 5.4.3.2, etc.)
- Resolving method handle constants (JVMS 5.4.3.5)
- Resolving call site specifiers (JVMS 5.4.3.6)
- Checking Java language access by instances of java.lang.reflect.AccessibleObject
- Checking access during queries to java.lang.invoke.MethodHandles.Lookup
針對上述訪問規則的改變,相應的調整位元組碼:
- invokespecial for private nestmate constructors,
- invokevirtual for private non-interface, nestmate instance methods,
- invokeinterface for private interface, nestmate instance methods; and
- invokestatic for private nestmate, static methods
巢狀類的校驗
巢狀類必須在訪問前校驗。校驗最遲要發生在訪問成員之前,最早可以發生在對class檔案的校驗時,或者在兩者之間,比如JIT時。校驗巢狀關係,需要載入nest host類,為了防止無意義的載入,這一步儘量放到最後做。
為了保證巢狀的完整性,建議禁止修改nest classfile屬性