Рубрики
Без рубрики

Java 11 – Управление доступом На Основе Гнезд

– Java 11 – Управление Доступом На Основе Nest

Это JEP 181: Управление доступом на основе Nest , поддерживает частный доступ внутри участников nest напрямую, не более чем с помощью автоматически сгенерированного метода моста доступ $000 . Кроме того, новые API-интерфейсы nest для проверки и разрешили доступ к частному отражению внутри участников nest.

P.S Нет необходимости изменять код, это оптимизация компилятора Java для удаления доступа к методу моста.

1. До Java 11 доступ к мостовому методу составлял $000

1.1 Просмотрите следующий вложенный класс. Все вложенные классы пытаются получить доступ к закрытому члену Алфавита , он же Узел гнезда .

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 Если мы скомпилируем приведенный выше источник, он сгенерирует четыре класса, Алфавит , Алфавит$A , Алфавит$B и Алфавит$B.B1 , даже вложенный класс является типичным классом с уникальным именем. Правило доступа JVM не разрешает частный доступ в разных классах.

Однако Java разрешила частный доступ внутри членов nest, поэтому компилятор 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 bridge для вложенного частного доступа.

$ 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."":()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."":()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."":()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."":()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 он поддерживает частный доступ внутри членов nest напрямую, без метода моста.

Протестировано с 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."":()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."":()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."":()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."":()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-интерфейсы для проверки вложенных элементов.

  • getnexthost - получить
  • получить участников Гнезда
  • является ли Гнездо партнером

Например, просмотрите следующие вложенные классы. Алфавит является Хостом Гнезда , а все остальные являются Членами Гнезда или Оценка друг другу.

public class Alphabet {

    public class A {
    }

    public class B {
        public class B1 {
        }
    }
}

3.2 Приведенный ниже исходный код показывает использование новых вложенных API. Единственный трюк – это B.B1.class.getmethod() возвращает Алфавит , а не 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/|Внутренний пытается получить доступ к Внутреннему закрытому методу через API отражения.

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. Исключение незаконного доступа .

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 https://github.com/mkyong/core-java $cd java-11 $cd src/главная/java/com/mkyong/java 11/jep181

Рекомендации

Оригинал: “https://mkyong.com/java/java-11-nest-based-access-control/”