JEP 181:基于嵌套的访问控制 ,直接支持嵌套成员内部的私有访问,而不再通过自动生成的桥接方法access$000
。 此外,新的嵌套API用于验证并允许嵌套成员内的私有反射访问。
PS无需更改代码,这是Java编译器的优化,可删除桥接方法访问。
1.在Java 11之前,桥接方法access $ 000
1.1复查以下嵌套类。 所有嵌套类都尝试访问Alphabet
的私有成员,即NestHost
。
public class Alphabet {
private String name = "I'm Alphabet!";
public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
public class B {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
public class B1 {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
}
1.2如果我们编译上述源代码,它将生成四个类, Alphabet
, Alphabet$A
, Alphabet$B
和Alphabet$B.B1
,即使嵌套类也是具有唯一名称的典型类。 JVM访问规则将不允许不同类内的私有访问。
但是,Java允许嵌套成员内的私有访问,因此Java编译器创建了一个桥接方法access$000
来应用于JVM访问规则。
$ javac Alphabet.java
Alphabet$A.class
Alphabet$B.class
Alphabet$B.B1.class
Alphabet.class
1.3桥接方法访问的源代码与此类似:
public class Alphabet {
private String name = "I'm Alphabet!";
String access$000(){
return name;
}
}
public class Alphabet$A {
final Alphabet obj;
public void printName(){
System.out.println(obj.access$000());
}
}
public class Alphabet$B {
final Alphabet obj;
public void printName(){
System.out.println(obj.access$000());
}
}
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public void printName(){
System.out.println(obj.access$000());
}
}
2.反汇编代码
我们可以使用javap -c
来分解上述四个类,以查看桥接方法访问前后的差异。
2.1在Java 11之前,它会为嵌套的私有访问自动创建一个新的access$000
桥接方法。
$ javac Alphabet.java
$ javap -c Alphabet
Compiled from "Alphabet.java"
public class Alphabet {
public Alphabet();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #3 // String I'm Alphabet!
7: putfield #1 // Field name:Ljava/lang/String;
10: return
static java.lang.String access$000(Alphabet);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn
}
$ javap -c Alphabet$A
Compiled from "Alphabet.java"
public class Alphabet$A {
final Alphabet this$0;
public Alphabet$A(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B
Compiled from "Alphabet.java"
public class Alphabet$B {
final Alphabet this$0;
public Alphabet$B(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B$B1
Compiled from "Alphabet.java"
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public Alphabet$B$B1(Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:LAlphabet$B;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:LAlphabet$B;
7: getfield #4 // Field Alphabet$B.this$0:LAlphabet;
10: invokestatic #5 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}
2.2在Java 11中,它使用NO桥方法直接支持嵌套成员内的私有访问。
经过Java 11。
$ javac Alphabet.java
$ javap -c Alphabet
Compiled from "Alphabet.java"
public class Alphabet {
public Alphabet();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String I\'m Alphabet!
7: putfield #3 // Field name:Ljava/lang/String;
10: return
}
$ javap -c Alphabet$A
Compiled from "Alphabet.java"
public class Alphabet$A {
final Alphabet this$0;
public Alphabet$A(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: getfield #4 // Field Alphabet.name:Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B
Compiled from "Alphabet.java"
public class Alphabet$B {
final Alphabet this$0;
public Alphabet$B(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: getfield #4 // Field Alphabet.name:Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B$B1
Compiled from "Alphabet.java"
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public Alphabet$B$B1(Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:LAlphabet$B;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:LAlphabet$B;
7: getfield #4 // Field Alphabet$B.this$0:LAlphabet;
10: getfield #5 // Field Alphabet.name:Ljava/lang/String;
13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}
3.新的嵌套类API。
3.1一些用于验证嵌套成员的新API。
-
getNestHost
-
getNestMembers
-
isNestmateOf
例如,查看以下嵌套类。 Alphabet
是NestHost
,所有其他Alphabet
NestHost
都是NestMembers
或NestmateOf
。
public class Alphabet {
public class A {
}
public class B {
public class B1 {
}
}
}
3.2以下源代码显示了新嵌套API的用法。 唯一的技巧是B.B1.class.getNestHost()
返回Alphabet
,而不是B
其他结果很明显,请阅读注释。
package com.mkyong.java11.jep181;
import java.util.Arrays;
public class Alphabet {
private String name = "I'm Alphabet!";
public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
public class B {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
public class B1 {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
public static void main(String[] args) {
A objA = new Alphabet().new A();
objA.printName();
B objB = new Alphabet().new B();
objB.printName();
B.B1 objB1 = new Alphabet().new B().new B1();
objB1.printName();
System.out.println(Alphabet.class.getNestHost()); // Alphabet
System.out.println(A.class.getNestHost()); // Alphabet
System.out.println(B.class.getNestHost()); // Alphabet
System.out.println(B.B1.class.getNestHost()); // Alphabet!, not B
System.out.println("---");
System.out.println(Arrays.toString(Alphabet.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(A.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(B.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(B.B1.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println("---");
System.out.println(Alphabet.class.isNestmateOf(Alphabet.class)); // true
System.out.println(Alphabet.class.isNestmateOf(A.class)); // true
System.out.println(Alphabet.class.isNestmateOf(B.class)); // true
System.out.println(Alphabet.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(A.class.isNestmateOf(Alphabet.class)); // true
System.out.println(A.class.isNestmateOf(A.class)); // true
System.out.println(A.class.isNestmateOf(B.class)); // true
System.out.println(A.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(B.class.isNestmateOf(Alphabet.class)); // true
System.out.println(B.class.isNestmateOf(A.class)); // true
System.out.println(B.class.isNestmateOf(B.class)); // true
System.out.println(B.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(B.B1.class.isNestmateOf(Alphabet.class)); // true
System.out.println(B.B1.class.isNestmateOf(A.class)); // true
System.out.println(B.B1.class.isNestmateOf(B.class)); // true
System.out.println(B.B1.class.isNestmateOf(B.B1.class)); // true
}
}
4.反射访问
3.1 InnerA
尝试通过反射API访问InnerB
私有方法。
package com.mkyong.java11.jep181;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Outer {
public static class InnerA {
// InnerA access InnerB private method, via reflection!
// Before Java 11 - IllegalAccessException
// Java 11 - OK
public void printName() throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
InnerB obj = new InnerB();
final Method m = InnerB.class.getDeclaredMethod("printName");
m.invoke(obj);
}
}
public static class InnerB {
// private!!!
private void printName() {
System.out.println("I'm InnerB!");
}
}
public static void main(String[] args) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
InnerA obj = new InnerA();
obj.printName();
}
}
在Java 11之前,它将引发java.lang.IllegalAccessException
。
Exception in thread "main" java.lang.IllegalAccessException: Class com.mkyong.java11.jep181.Outer$InnerA can not access a member of class com.mkyong.java11.jep181.Outer$InnerB with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.mkyong.java11.jep181.Outer$InnerA.printName(Outer.java:14)
at com.mkyong.java11.jep181.Outer.main(Outer.java:28)
与Java 11一起运行,没问题。 新的基于嵌套的访问控制甚至可以通过反射API来私有访问嵌套成员。
I'm InnerB!
下载源代码
$ git clone https://github.com/mkyong/core-java
$ cd java-11
$ cd src / main / java / com / mkyong / java11 / jep181
参考文献
翻译自: https://mkyong.com/java/java-11-nest-based-access-control/