首页 > 作文

Java11中基于嵌套关系的访问控制优化详解

更新时间:2023-04-04 10:51:32 阅读: 评论:0

目录
前言java11 之前的实现方式技术债务java11 中的实现nestmate 新增的 apigetnesthostgetnestmembersisnestmateof后黄山天都峰资料续的改进文末总结

前言

java 语言很强大,但是,有人的地方就有江湖,有猿的地方就有 bug,java 的核心代码并非十全十美。比如在 jdk 中居然也有反模式接口常量 中介绍的反模式实现,以及本文说到的这个技术债务:嵌套关系(nestmate)调用方式。

在 java 语言中,类和接口可以相互嵌套,这种组合之间可以不受限制的彼此访问,包括访问彼此的构造函数、字段、方法等。即使是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、方法print11和print12都是私有的,但是可以在inner类中直接访问,inner类的字段j、方法print5是私有的,也可以在outer类中使用。这种设计是为了更好的封装,在用户看来,这几个彼此嵌套的类/接口是一体的,分开定义是为了更好的封装自己,隔离不同特性,但是有因为彼此是一体,所以私有元素也应该是共有的。

java11 之前的实现方式

我们使用 java8 编译,然后借助javap -c命令分别查看outer和inner的结果。

$ 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:世界大学排名500强 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的编译结果,这里需要注意的是,内部类会使用特殊的命名方式定义inner类,最终会将编译结果存储在两个文件中:

$ javap -c outer$inner.classcompiled 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: a人教版小学语文课本load_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/howard深圳 最低工资liu/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}

我们可以看到,outer和inner中多出了几个方法,方法名格式是access$*00。

outer中的access$200方法返回了属性i,access$300和access$400分别调用了print11和print12方法。这些新增的方法都是静态方法,作用域是默认作用域,即包内可用。这些方法最终被inner类中的print3和print4调用,相当于间接调用outer中的私有属性或方法。

我们称这些生成的方法为“桥”方法(bridge method),是一种实现嵌套关系内部互相访问的方式。

在编译的时候,java 为了保持类的单一特性,会将嵌套类编译到多个 class 文件中,同时为了保证嵌套类能够彼此访问,自动创建了调用私有方法的“桥”方法,这样,在保持原有定义不变的情况下,又实现了嵌套语法。

技术债务

“桥”方法的实现是比较巧妙的,但是这会造成源码与编译结果访问控制权限不一致,比如,我们可以在inner中调用outer中的私有方法,按照道理来说,我们可以在inner中通过反射调用outer的方法,但实际上不行,会抛出illegalaccesxception异常。我们验证一下:

public class outer {    // 省略其他方法    public void callinnerreflectionmethod()            throws invocationtargetexception, nosuchmethodexception, illegalaccesxception {        final inner inner = new inner();        inner.callouterprivatemethod(this);    }    public class inner {        // 省略其他方法        public void callouterprivatemethod(outer outer)                throws nosuchmethodexception, invocationtargetexception, illegalaccesxception {            final method method = outer.getclass().getdeclaredmethod("print12");            method.invoke(outer);        }    }}

定义测试用例:

@testvoid gotanexceptioninjava8() {    final outer outer = new outer();    final exception e = asrtthrows(illegalaccesxception.class, outer::callinnerreflectionmethod);    e.printstacktrace();    asrtdoesnotthrow(outer::callinnermethod);}

打印的异常信息是:

java.lang.illegalaccesxception: 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.ba/jdk.internal.reflect.reflection.newillegalaccesxception(reflection.java:361)
at java.ba/java.lang.reflect.accessibleobject.checkaccess(accessibleobject.java:591)
at java.ba/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)

通过反射直接调用私有方法会失败,但是可以直接的或者通过反射访问这些“桥”方法,这样就比较奇怪了。所以提出 jep181 改进,修复这个技术债务的同时,为后续的改进铺路。

java11 中的实现

我们再来看看 java11 编译之后的结果:

$ 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类的源码结构是一致的。我们再看看inner有没有什么变化:

$ javap -c outer$inner.classcompiled 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}

同样干净。

我们在通过测试用例验证一下反射调用:

@testvoid doesnotgotanexceptioninjava11() {    final outer outer = new outer();    asrtdoesnotthrow(outer::callinnerreflectionmethod);    asrtdoesnotthrow(outer::callinnermethod);}

结果是正常运行。

这就是 jep181 期望的结果,源码和编译结果一致,访问控制一致。

nestmate 新增的 api

在 java11 中还新增了几个 api,用于嵌套关系的验证:

getnesthost

这个方法是返回嵌套主机(nesthost),转成普通话就是找到嵌套类的外层类。对于非嵌套类,直接返回自身(其实也算是返回外层类)。

我们看下用法:

@testvoid checknesthostname() {    final string outernesthostname = outer.class.getnesthost().getname();    asrtequals("cn.howardliu.tutorials.java11.nest.outer", outernesthostname);    final string innernesthostname = inner.class.getnesthost().getname();    asrtequals("cn.howardliu.tutorials.java11.nest.outer", innernesthostname);    asrtequals(outernesthostname, innernesthostname);    final string notnestclass = notnestclass.class.getnesthost().getname();    asrtequals("cn.howardliu.tutorials.java11.nest.notnestclass", notnestclass);}

对于outer和inner都是返回了cn.howardliu.tutorials.java11.nest.outer。

getnestmembers

这个方法是返回嵌套类的嵌套成员数组,下标是 0 的元素确定是 nesthost 对应的类,其他元素顺序没有给出排序规则。我们看下使用:

@testvoid getnestmembers() {    final list<string> outernestmembers = arrays.stream(outer.class.getnestmembers())            .map(class::getname)            .collect(collectors.tolist());    asrtequals(2, outernestmembers.size());    asrttrue(outernestmembers.contains("cn.howardliu.tutorials.java11.nest.outer"));    asrttrue(outernestmembers.contains("cn.howardliu.tutorials.java11.nest.outer$inner"));    final list<string> innernestmembers = arrays.stream(inner.class.getnestmembers())            .map(class::getname)            .collect(collectors.tolist());    asrtequals(2, innernestmembers.size());    asrttrue(innernestmembers.contains("cn.howardliu.tutorials.java11.nest.outer"));    asrttrue(innernestmembers.contains("cn.howardliu.tutorials.java11.nest.outer$inner"));}

isnestmateof

这个方法是用于判断两个类是否是彼此的 nestmate,彼此形成嵌套关系。判断依据还是嵌套主机,只要相同,两个就是 nestmate。我们看下使用:

@testvoid checkisnestmateof() {    asrttrue(inner.class.isnestmateof(outer.class));    asrttrue(outer.class.isnestmateof(inner.class));}

后续的改进

嵌套关系是作为 valhalla 项目的一部分,这个项目的主要目标之一是改进 java 中的值类型和泛型。后续会有更多的改进:

在泛型特化(generic specialization)中,每个特化类型(specialized type)可被创建为泛型的一个 nestmate。支持对unsafe.defineanonymousclass() api 的安全替换,实现将新类创建为已有类的 nestmate。可能会影响“密封类”(aled class),仅允许 nestmate 的子类作为密封类。可能会影响私有嵌套类型。私有嵌套类型当前定义为包内可访问(package-access)。

文末总结

本文阐述了基于嵌套关系的访问控制优化,其中涉及nestmate、nesthost、nestmember等概念。这次优化是 valhalla 项目中一部分,主要改进 java 中的值类型和泛型等。

到此这篇关于java11中基于嵌套关系的访问控制优化的文章就介绍到这了,更多相关java11嵌套关系的访问控制内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

本文发布于:2023-04-04 10:51:30,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/339b0031613dc27c6be846cddf9c87d0.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:Java11中基于嵌套关系的访问控制优化详解.doc

本文 PDF 下载地址:Java11中基于嵌套关系的访问控制优化详解.pdf

标签:嵌套   方法   关系   都是
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图