Java 11 –基于嵌套的访问控制

JEP 181:基于嵌套的访问控制 ,直接支持嵌套成员内部的私有访问,而不再通过自动生成的桥接方法access$000 。 此外,新的嵌套API用于验证并允许嵌套成员内的私有反射访问。

PS无需更改代码,这是Java编译器的优化,可删除桥接方法访问。

1.在Java 11之前,桥接方法access $ 000

1.1复查以下嵌套类。 所有嵌套类都尝试访问Alphabet的私有成员,即NestHost

Alphabet.java
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如果我们编译上述源代码,它将生成四个类, AlphabetAlphabet$AAlphabet$BAlphabet$B.B1 ,即使嵌套类也是具有唯一名称的典型类。 JVM访问规则将不允许不同类内的私有访问。

但是,Java允许嵌套成员内的私有访问,因此Java编译器创建了一个桥接方法access$000来应用于JVM访问规则。

Terminal
$ 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桥接方法。

Terminal
$ 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。

Terminal
$ 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

例如,查看以下嵌套类。 AlphabetNestHost ,所有其他Alphabet NestHost都是NestMembersNestmateOf

Alphabet.java
public class Alphabet {

    public class A {
    }

    public class B {
        public class B1 {
        }
    }
}

3.2以下源代码显示了新嵌套API的用法。 唯一的技巧是B.B1.class.getNestHost()返回Alphabet ,而不是B 其他结果很明显,请阅读注释。

Alphabet.java
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私有方法。

Outer.java
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

Terminal
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来私有访问嵌套成员。

Terminal
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/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值