最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java11?中基于嵌套關(guān)系的訪問控制優(yōu)化問題

 更新時間:2022年01月19日 16:23:39   作者:看山  
在?Java?語言中,類和接口可以相互嵌套,這種組合之間可以不受限制的彼此訪問,包括訪問彼此的構(gòu)造函數(shù)、字段、方法,接下來通過本文給大家介紹Java11中基于嵌套關(guān)系的訪問控制優(yōu)化問題,感興趣的朋友一起看看吧

你好,我是看山。

Java 語言很強(qiáng)大,但是,有人的地方就有江湖,有猿的地方就有 bug,Java 的核心代碼并非十全十美。比如在 JDK 中居然也有反模式接口常量 中介紹的反模式實(shí)現(xiàn),以及本文說到的這個技術(shù)債務(wù):嵌套關(guān)系(NestMate)調(diào)用方式。

在 Java 語言中,類和接口可以相互嵌套,這種組合之間可以不受限制的彼此訪問,包括訪問彼此的構(gòu)造函數(shù)、字段、方法等。即使是private私有的,也可以彼此訪問。比如下面這樣定義:

public class Outer {
    private int i;
    public void print1() {
        print11();
        print12();
    }
    private void print11() {
        System.out.println(i);
    }
    private void print12() {
        System.out.println(i);
    }
    public void callInnerMethod() {
        final Inner inner = new Inner();
        inner.print4();
        inner.print5();
        System.out.println(inner.j);
    }
    public class Inner {
        private int j;
        public void print3() {
            System.out.println(i);
            print1();
        }
        public void print4() {
            System.out.println(i);
            print11();
            print12();
        }
        private void print5() {
            System.out.println(i);
            print11();
            print12();
        }
    }
}

上例中,Outer類中的字段i、方法print11print12都是私有的,但是可以在Inner類中直接訪問,Inner類的字段j、方法print5是私有的,也可以在Outer類中使用。這種設(shè)計是為了更好的封裝,在用戶看來,這幾個彼此嵌套的類/接口是一體的,分開定義是為了更好的封裝自己,隔離不同特性,但是有因?yàn)楸舜耸且惑w,所以私有元素也應(yīng)該是共有的。

Java11 之前的實(shí)現(xiàn)方式

我們使用 Java8 編譯,然后借助javap -c命令分別查看OuterInner的結(jié)果。

$ javap -c Outer.class      
Compiled from "Outer.java"
public class cn.howardliu.tutorials.java8.nest.Outer {
  public cn.howardliu.tutorials.java8.nest.Outer();
    Code:
       0: aload_0
       1: invokespecial #4                  // Method java/lang/Object."<init>":()V
       4: return
  public void print1();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method print11:()V
       4: aload_0
       5: invokespecial #1                  // Method print12:()V
       8: return
  public void callInnerMethod();
    Code:
       0: new           #7                  // class cn/howardliu/tutorials/java8/nest/Outer$Inner
       3: dup
       4: aload_0
       5: invokespecial #8                  // Method cn/howardliu/tutorials/java8/nest/Outer$Inner."<init>":(Lcn/howardliu/tutorials/java8/nest/Outer;)V
       8: astore_1
       9: aload_1
      10: invokevirtual #9                  // Method cn/howardliu/tutorials/java8/nest/Outer$Inner.print4:()V
      13: aload_1
      14: invokestatic  #10                 // Method cn/howardliu/tutorials/java8/nest/Outer$Inner.access$000:(Lcn/howardliu/tutorials/java8/nest/Outer$Inner;)V
      17: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      20: aload_1
      21: invokestatic  #11                 // Method cn/howardliu/tutorials/java8/nest/Outer$Inner.access$100:(Lcn/howardliu/tutorials/java8/nest/Outer$Inner;)I
      24: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
      27: return
  static int access$200(cn.howardliu.tutorials.java8.nest.Outer);
    Code:
       0: aload_0
       1: getfield      #3                  // Field i:I
       4: ireturn
  static void access$300(cn.howardliu.tutorials.java8.nest.Outer);
    Code:
       0: aload_0
       1: invokespecial #2                  // Method print11:()V
       4: return
  static void access$400(cn.howardliu.tutorials.java8.nest.Outer);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method print12:()V
       4: return
}

再來看看Inner的編譯結(jié)果,這里需要注意的是,內(nèi)部類會使用特殊的命名方式定義Inner類,最終會將編譯結(jié)果存儲在兩個文件中:

$ javap -c Outer\$Inner.class
Compiled from "Outer.java"
public class cn.howardliu.tutorials.java8.nest.Outer$Inner {
  final cn.howardliu.tutorials.java8.nest.Outer this$0;
  public cn.howardliu.tutorials.java8.nest.Outer$Inner(cn.howardliu.tutorials.java8.nest.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
       5: aload_0
       6: invokespecial #4                  // Method java/lang/Object."<init>":()V
       9: return
  public void print3();
    Code:
       0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
       7: invokestatic  #6                  // Method cn/howardliu/tutorials/java8/nest/Outer.access$200:(Lcn/howardliu/tutorials/java8/nest/Outer;)I
      10: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
      13: aload_0
      14: getfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
      17: invokevirtual #8                  // Method cn/howardliu/tutorials/java8/nest/Outer.print1:()V
      20: return
  public void print4();
    Code:
       0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
       7: invokestatic  #6                  // Method cn/howardliu/tutorials/java8/nest/Outer.access$200:(Lcn/howardliu/tutorials/java8/nest/Outer;)I
      10: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
      13: aload_0
      14: getfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
      17: invokestatic  #9                  // Method cn/howardliu/tutorials/java8/nest/Outer.access$300:(Lcn/howardliu/tutorials/java8/nest/Outer;)V
      20: aload_0
      21: getfield      #3                  // Field this$0:Lcn/howardliu/tutorials/java8/nest/Outer;
      24: invokestatic  #10                 // Method cn/howardliu/tutorials/java8/nest/Outer.access$400:(Lcn/howardliu/tutorials/java8/nest/Outer;)V
      27: return
  static void access$000(cn.howardliu.tutorials.java8.nest.Outer$Inner);
    Code:
       0: aload_0
       1: invokespecial #2                  // Method print5:()V
       4: return
  static int access$100(cn.howardliu.tutorials.java8.nest.Outer$Inner);
    Code:
       0: aload_0
       1: getfield      #1                  // Field j:I
       4: ireturn
}

我們可以看到,OuterInner中多出了幾個方法,方法名格式是access$*00。

Outer中的access$200方法返回了屬性iaccess$300access$400分別調(diào)用了print11print12方法。這些新增的方法都是靜態(tài)方法,作用域是默認(rèn)作用域,即包內(nèi)可用。這些方法最終被Inner類中的print3print4調(diào)用,相當(dāng)于間接調(diào)用Outer中的私有屬性或方法。

我們稱這些生成的方法為“橋”方法(Bridge Method),是一種實(shí)現(xiàn)嵌套關(guān)系內(nèi)部互相訪問的方式。

在編譯的時候,Java 為了保持類的單一特性,會將嵌套類編譯到多個 class 文件中,同時為了保證嵌套類能夠彼此訪問,自動創(chuàng)建了調(diào)用私有方法的“橋”方法,這樣,在保持原有定義不變的情況下,又實(shí)現(xiàn)了嵌套語法。

技術(shù)債務(wù)

“橋”方法的實(shí)現(xiàn)是比較巧妙的,但是這會造成源碼與編譯結(jié)果訪問控制權(quán)限不一致,比如,我們可以在Inner中調(diào)用Outer中的私有方法,按照道理來說,我們可以在Inner中通過反射調(diào)用Outer的方法,但實(shí)際上不行,會拋出IllegalAccessException異常。我們驗(yàn)證一下:

public class Outer {
    // 省略其他方法
    public void callInnerReflectionMethod()
            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        final Inner inner = new Inner();
        inner.callOuterPrivateMethod(this);
    }

    public class Inner {
        // 省略其他方法
        public void callOuterPrivateMethod(Outer outer)
                throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            final Method method = outer.getClass().getDeclaredMethod("print12");
            method.invoke(outer);
        }
    }
}

定義測試用例:

@Test
void gotAnExceptionInJava8() {
    final Outer outer = new Outer();

    final Exception e = assertThrows(IllegalAccessException.class, outer::callInnerReflectionMethod);
    e.printStackTrace();

    assertDoesNotThrow(outer::callInnerMethod);
}

打印的異常信息是:

java.lang.IllegalAccessException: class cn.howardliu.tutorials.java8.nest.Outer$Inner cannot access a member of class cn.howardliu.tutorials.java8.nest.Outer with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
    at java.base/java.lang.reflect.Method.invoke(Method.java:558)
    at cn.howardliu.tutorials.java8.nest.Outer$Inner.callOuterPrivateMethod(Outer.java:62)
    at cn.howardliu.tutorials.java8.nest.Outer.callInnerReflectionMethod(Outer.java:36)

通過反射直接調(diào)用私有方法會失敗,但是可以直接的或者通過反射訪問這些“橋”方法,這樣就比較奇怪了。所以提出 JEP181 改進(jìn),修復(fù)這個技術(shù)債務(wù)的同時,為后續(xù)的改進(jìn)鋪路。

Java11 中的實(shí)現(xiàn)

我們再來看看 Java11 編譯之后的結(jié)果:

$ javap -c Outer.class      
Compiled from "Outer.java"
public class cn.howardliu.tutorials.java11.nest.Outer {
  public cn.howardliu.tutorials.java11.nest.Outer();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void print1();
    Code:
       0: aload_0
       1: invokevirtual #2                  // Method print11:()V
       4: aload_0
       5: invokevirtual #3                  // Method print12:()V
       8: return

  public void callInnerMethod();
    Code:
       0: new           #7                  // class cn/howardliu/tutorials/java11/nest/Outer$Inner
       3: dup
       4: aload_0
       5: invokespecial #8                  // Method cn/howardliu/tutorials/java11/nest/Outer$Inner."<init>":(Lcn/howardliu/tutorials/java11/nest/Outer;)V
       8: astore_1
       9: aload_1
      10: invokevirtual #9                  // Method cn/howardliu/tutorials/java11/nest/Outer$Inner.print4:()V
      13: aload_1
      14: invokevirtual #10                 // Method cn/howardliu/tutorials/java11/nest/Outer$Inner.print5:()V
      17: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      20: aload_1
      21: getfield      #11                 // Field cn/howardliu/tutorials/java11/nest/Outer$Inner.j:I
      24: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
      27: return
}

是不是很干凈,與Outer類的源碼結(jié)構(gòu)是一致的。我們再看看Inner有沒有什么變化:

$ javap -c Outer\$Inner.class
Compiled from "Outer.java"
public class cn.howardliu.tutorials.java11.nest.Outer$Inner {
  final cn.howardliu.tutorials.java11.nest.Outer this$0;

  public cn.howardliu.tutorials.java11.nest.Outer$Inner(cn.howardliu.tutorials.java11.nest.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void print3();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
       7: getfield      #4                  // Field cn/howardliu/tutorials/java11/nest/Outer.i:I
      10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
      13: aload_0
      14: getfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
      17: invokevirtual #6                  // Method cn/howardliu/tutorials/java11/nest/Outer.print1:()V
      20: return

  public void print4();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
       7: getfield      #4                  // Field cn/howardliu/tutorials/java11/nest/Outer.i:I
      10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
      13: aload_0
      14: getfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
      17: invokevirtual #7                  // Method cn/howardliu/tutorials/java11/nest/Outer.print11:()V
      20: aload_0
      21: getfield      #1                  // Field this$0:Lcn/howardliu/tutorials/java11/nest/Outer;
      24: invokevirtual #8                  // Method cn/howardliu/tutorials/java11/nest/Outer.print12:()V
      27: return
}

同樣干凈。

我們在通過測試用例驗(yàn)證一下反射調(diào)用:

@Test
void doesNotGotAnExceptionInJava11() {
    final Outer outer = new Outer();

    assertDoesNotThrow(outer::callInnerReflectionMethod);
    assertDoesNotThrow(outer::callInnerMethod);
}

結(jié)果是正常運(yùn)行。

這就是 JEP181 期望的結(jié)果,源碼和編譯結(jié)果一致,訪問控制一致。

Nestmate 新增的 API

在 Java11 中還新增了幾個 API,用于嵌套關(guān)系的驗(yàn)證:

getNestHost

這個方法是返回嵌套主機(jī)(NestHost),轉(zhuǎn)成普通話就是找到嵌套類的外層類。對于非嵌套類,直接返回自身(其實(shí)也算是返回外層類)。

我們看下用法:

@Test
void checkNestHostName() {
    final String outerNestHostName = Outer.class.getNestHost().getName();
    assertEquals("cn.howardliu.tutorials.java11.nest.Outer", outerNestHostName);
    final String innerNestHostName = Inner.class.getNestHost().getName();
    assertEquals("cn.howardliu.tutorials.java11.nest.Outer", innerNestHostName);
    assertEquals(outerNestHostName, innerNestHostName);
    final String notNestClass = NotNestClass.class.getNestHost().getName();
    assertEquals("cn.howardliu.tutorials.java11.nest.NotNestClass", notNestClass);
}

對于OuterInner都是返回了cn.howardliu.tutorials.java11.nest.Outer。

getNestMembers

這個方法是返回嵌套類的嵌套成員數(shù)組,下標(biāo)是 0 的元素確定是 NestHost 對應(yīng)的類,其他元素順序沒有給出排序規(guī)則。我們看下使用:

@Test
void getNestMembers() {
    final List<String> outerNestMembers = Arrays.stream(Outer.class.getNestMembers())
            .map(Class::getName)
            .collect(Collectors.toList());
    assertEquals(2, outerNestMembers.size());
    assertTrue(outerNestMembers.contains("cn.howardliu.tutorials.java11.nest.Outer"));
    assertTrue(outerNestMembers.contains("cn.howardliu.tutorials.java11.nest.Outer$Inner"));
    final List<String> innerNestMembers = Arrays.stream(Inner.class.getNestMembers())
            .map(Class::getName)
            .collect(Collectors.toList());
    assertEquals(2, innerNestMembers.size());
    assertTrue(innerNestMembers.contains("cn.howardliu.tutorials.java11.nest.Outer"));
    assertTrue(innerNestMembers.contains("cn.howardliu.tutorials.java11.nest.Outer$Inner"));
}

isNestmateOf

這個方法是用于判斷兩個類是否是彼此的 NestMate,彼此形成嵌套關(guān)系。判斷依據(jù)還是嵌套主機(jī),只要相同,兩個就是 NestMate。我們看下使用:

@Test
void checkIsNestmateOf() {
    assertTrue(Inner.class.isNestmateOf(Outer.class));
    assertTrue(Outer.class.isNestmateOf(Inner.class));
}

后續(xù)的改進(jìn)

嵌套關(guān)系是作為 Valhalla 項(xiàng)目的一部分,這個項(xiàng)目的主要目標(biāo)之一是改進(jìn) JAVA 中的值類型和泛型。后續(xù)會有更多的改進(jìn):

  • 在泛型特化(generic specialization)中,每個特化類型(specialized type)可被創(chuàng)建為泛型的一個 Nestmate。
  • 支持對Unsafe.defineAnonymousClass() API 的安全替換,實(shí)現(xiàn)將新類創(chuàng)建為已有類的 Nestmate。
  • 可能會影響“密封類”(sealed classes),僅允許 Nestmate 的子類作為密封類。
  • 可能會影響私有嵌套類型。私有嵌套類型當(dāng)前定義為包內(nèi)可訪問(package-access)。 

到此這篇關(guān)于Java11 中基于嵌套關(guān)系的訪問控制優(yōu)化的文章就介紹到這了,更多相關(guān)Java訪問控制優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot 中的 @PutMapping 注解原理及使用小結(jié)

    Spring Boot 中的 @PutMapping 注解原理及使用小結(jié)

    在本文中,我們介紹了 Spring Boot 中的 @PutMapping 注解,它可以將 HTTP PUT 請求映射到指定的處理方法上,我們還介紹了 @PutMapping 注解的原理以及如何在 Spring Boot 中使用它,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • Java學(xué)習(xí)隨記之多線程編程

    Java學(xué)習(xí)隨記之多線程編程

    這篇文章主要介紹了Java中的多線程編程的相關(guān)知識,文中的示例代碼介紹詳細(xì),對我們的學(xué)習(xí)或工作有一定的價值,感興趣的小伙伴可以了解一下
    2021-12-12
  • 關(guān)于Redis鍵值出現(xiàn)\xac\xed\x00\x05t\x00&錯誤的解決方法

    關(guān)于Redis鍵值出現(xiàn)\xac\xed\x00\x05t\x00&錯誤的解決方法

    這篇文章主要介紹了關(guān)于Redis鍵值出現(xiàn)\xac\xed\x00\x05t\x00&的解決方法,出現(xiàn)該問題的原因是, redis template向redis存放使用java對象序列化的值,序列化方式和string的一般方式不同,需要的朋友可以參考下
    2023-08-08
  • Jenkins環(huán)境搭建之下載與安裝過程

    Jenkins環(huán)境搭建之下載與安裝過程

    Jenkins是一個功能強(qiáng)大的應(yīng)用程序,允許持續(xù)集成和持續(xù)交付項(xiàng)目,集成Jenkins可以用于一些測試和部署技術(shù),對Jenkins環(huán)境搭建之下載與安裝過程感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Idea2023配置tomcat服務(wù)器的圖文教程

    Idea2023配置tomcat服務(wù)器的圖文教程

    這篇文章主要介紹了Idea2023配置tomcat服務(wù)器的圖文教程,本文是javaweb新手版教程,IDEA2023+JDK1.8+apache-tomcat-8.5.91,沒有使用Maven,需要的朋友可以參考下
    2023-10-10
  • Spring IOC基于注解啟動示例詳析

    Spring IOC基于注解啟動示例詳析

    這篇文章主要給大家介紹了Spring IOC基于注解啟動的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 解決Java?properties文件里面如何寫"\"的問題

    解決Java?properties文件里面如何寫"\"的問題

    由于properties使用“\”相當(dāng)于是java的轉(zhuǎn)義符,如果想要寫出\的效果,只需修改相應(yīng)的寫法即可,對java?properties文件里的"\"寫法感興趣的朋友一起看看吧
    2022-04-04
  • Java?AQS?原理與?ReentrantLock?實(shí)現(xiàn)方法

    Java?AQS?原理與?ReentrantLock?實(shí)現(xiàn)方法

    AQS 的作用是解決同步器的實(shí)現(xiàn)問題,它將復(fù)雜的同步器實(shí)現(xiàn)分解為簡單的框架方法,開發(fā)者只需要實(shí)現(xiàn)少量特定的方法就能快速構(gòu)建出可靠的同步器,這篇文章主要介紹Java AQS原理與ReentrantLock實(shí)現(xiàn),需要的朋友可以參考下
    2025-03-03
  • Java枚舉(enum) 詳解7種常見的用法

    Java枚舉(enum) 詳解7種常見的用法

    這篇文章主要介紹了Java枚舉(enum) 詳解7種常見的用法,具有一定的參考價值,有需要的可以了解一下。
    2016-11-11
  • Mybatis-plus動態(tài)條件查詢QueryWrapper的使用案例

    Mybatis-plus動態(tài)條件查詢QueryWrapper的使用案例

    mybatis-plus框架功能很強(qiáng)大,把很多功能都集成了,下面這篇文章主要給大家介紹了關(guān)于Mybatis-plus動態(tài)條件查詢QueryWrapper的使用教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07

最新評論

永平县| 泰宁县| 千阳县| 安化县| 东山县| 东丽区| 顺昌县| 临沭县| 永德县| 汉源县| 陆丰市| 闽清县| 浦城县| 阳曲县| 昌邑市| 拜泉县| 新和县| 武胜县| 兖州市| 偏关县| 商洛市| 通江县| 牡丹江市| 诸暨市| 奇台县| 桃江县| 溧水县| 碌曲县| 通江县| 乌拉特后旗| 龙门县| 昌平区| 卫辉市| 大荔县| 开化县| 资阳市| 禄劝| 娄底市| 台湾省| 娱乐| 乐平市|