嵌套类和私有方法

当您在另一个类中有一个类时,他们可以看到彼此的private方法。 在Java开发人员中并不为人所知。 面试中的许多候选人说, private是一种可见性,它使代码可以查看成员是否属于同一班级。 这实际上是对的,但是更准确地说,代码和成员都在一个类中。当我们嵌套了内部类时, private成员和使用它的代码可能会出现在同一班级,同时他们也处于不同的班级。

例如,如果我在一个顶级类中有两个嵌套类,则其中一个嵌套类中的代码可以看到另一个嵌套类的private成员。

当我们查看生成的代码时,它开始变得很有趣。 JVM不在乎其他类中的类。 它处理JVM“顶级”类。 当您在类A有一个名为B的类时,编译器将创建.class文件,其名称将类似于A$B.classB有一个可从A调用的private方法,然后JVM看到A.class中的代码调用A$B.class的方法。 JVM检查访问控制。 当我们与初级用户讨论此问题时,有人建议JVM可能不在乎修饰符。 那是不对的。 尝试编译A.javaB.java ,两个顶级班,在一些代码A调用一个public的方法B 。 当你拥有A.classB.class修改方法B.javapublicprivate ,并重新编译Bた新B.class 。 启动应用程序,您将看到JVM非常关心访问修饰符。 尽管如此,您仍可以在上面的示例中从A.class调用A$B.class的方法。

为了解决此冲突,Java生成了额外的合成方法,这些合成方法本来就是公共的,可以在同一类内调用原始的私有方法,并且在考虑JVM访问控制的情况下可以调用。 另一方面,如果您找出生成的方法的名称并尝试直接从Java源代码中调用,则Java编译器将不会编译代码。 我在4年前就写了详细的文章。

如果您是一位经验丰富的开发人员,那么您可能会认为这是一个奇怪而令人反感的技巧。 除了此hack外,Java非常干净,优雅,简洁,纯净。 还有可能是Integer缓存的破解,它使用==使小的Integer对象(典型的测试值)相等,而较大的值仅equals() ,而== (典型的生产值)。 但是除了合成类和Integer缓存hack之外,Java都是干净,优雅,简洁和纯净的。 (您可能会发现我是Monty Python的粉丝。)

这样做的原因是嵌套类不是原始Java的一部分,而是仅添加到1.1版中。解决方案是一个hack,但是那时还有许多重要的事情要做,例如引入JIT编译器,JDBC,RMI,反思和其他一些我们今天认为理所当然的事情。 那个时候的问题不是解决方案是否干净。 相反,问题是Java是否将完全存活下来并成为主流编程语言,或者死掉并仍然是一个不错的尝试。 那个时候我还担任销售代表,编码只是一种业余爱好,因为东欧的编码工作很少,它们主要是无聊的簿记应用程序,而且薪水低。 那段时间有些不同,搜索引擎名为AltaVista,我们从水龙头里喝水,而Java具有不同的优先级。

结果是20多年来,我们的JAR文件略大,Java执行速度稍慢(除非JIT优化了调用链)以及IDE中令人讨厌的警告提示我们最好在嵌套类中使用包保护的方法,而不是private当我们从顶级或其他嵌套类中使用它时。

巢主机

现在看来,这20年的技术债务将得到解决。 http://openjdk.java.net/jeps/181进入Java 11,它将通过引入一个新概念来解决此问题:nest。 当前,Java字节码包含一些有关类之间关系的信息。 JVM知道某个类是另一个类的嵌套类,而不仅仅是名称。 该信息可以使JVM决定是否允许一个类中的一段代码访问另一类的private成员,但是JEP-181开发具有更一般的含义。 随着时间的推移,JVM不再是Java虚拟机。 好吧,是的,至少它是名称,但是,它是一个虚拟机,恰好执行从Java编译的字节码。 或其他语言的问题。 有许多针对JVM的语言,请记住JEP-181不想将JVM的新访问控制功能与Java语言的特定功能联系在一起。

JEP-181将NestHostNestMembers的概念定义为类的属性。 编译器将填充这些字段,并且当可以从另一个类访问某个类的私有成员时,JVM访问控制可以检查:两个类是否在同一嵌套中? 如果它们在同一巢中,则允许访问,否则不允许访问。 我们将在反射访问中添加方法,因此我们可以获得嵌套中的类的列表。

简单的嵌套示例

使用

$ java -version
java version "11-ea" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11-ea+25)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11-ea+25, mixed mode)

今天的Java版本我们可以进行实验。 我们可以创建一个简单的类:

package nesttest;
public class NestingHost {public static class NestedClass1 {private void privateMethod() {new NestedClass2().privateMethod();}}public static class NestedClass2 {private void privateMethod() {new NestedClass1().privateMethod();}}
}

很简单,它什么也不做。 私有方法互相调用。 没有这个,编译器就会发现它们只是什么也不做,并且不需要它们,而字节码只是不包含它们。
读取嵌套信息的类

package nesttest;import java.util.Arrays;
import java.util.stream.Collectors;public class TestNest {public static void main(String[] args) {Class host = NestingHost.class.getNestHost();Class[] nestlings = NestingHost.class.getNestMembers();System.out.println("Mother bird is: " + host);System.out.println("Nest dwellers are :\n" +Arrays.stream(nestlings).map(Class::getName).collect(Collectors.joining("\n")));}
}

打印输出符合预期:

Mother bird is: class nesttest.NestingHost
Nest dwellers are :
nesttest.NestingHost
nesttest.NestingHost$NestedClass2
nesttest.NestingHost$NestedClass1

请注意,嵌套主机也列在嵌套成员中,尽管此信息应该非常明显且多余。 但是,这样的使用可能允许某些语言从访问中公开嵌套主机本身的私有成员,并使访问仅允许嵌套。

字节码

使用JDK11编译器进行编译会生成文件

  • NestingHost$NestedClass1.class
  • NestingHost$NestedClass2.class
  • NestingHost.class
  • TestNest.class

没有变化。 另一方面,如果我们使用javap反编译器查看字节码,则将看到以下内容:

$ javap -v build/classes/java/main/nesttest/NestingHost\$NestedClass1.class
Classfile .../packt/Fundamentals-of-java-18.9/sources/ch08/bulkorders/build/classes/java/main/nesttest/NestingHost$NestedClass1.classLast modified Aug 6, 2018; size 557 bytesMD5 checksum 5ce1e0633850dd87bd2793844a102c52Compiled from "NestingHost.java"
public class nesttest.NestingHost$NestedClass1minor version: 0major version: 55flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #5                          // nesttest/NestingHost$NestedClass1super_class: #6                         // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:*** CONSTANT POOL DELETED FROM THE PRINTOUT ***{public nesttest.NestingHost$NestedClass1();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 6: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lnesttest/NestingHost$NestedClass1;
}
SourceFile: "NestingHost.java"
NestHost: class nesttest/NestingHost
InnerClasses:public static #13= #5 of #20;           // NestedClass1=class nesttest/NestingHost$NestedClass1 of class nesttest/NestingHostpublic static #23= #2 of #20;           // NestedClass2=class nesttest/NestingHost$NestedClass2 of class nesttest/NestingHost

如果我们使用JDK10编译器编译相同的类,则反汇编行如下:

$ javap -v build/classes/java/main/nesttest/NestingHost\$NestedClass1.class
Classfile /C:/Users/peter_verhas/Dropbox/packt/Fundamentals-of-java-18.9/sources/ch08/bulkorders/build/classes/java/main/nesttest/NestingHost$NestedClass1.classLast modified Aug 6, 2018; size 722 bytesMD5 checksum 8c46ede328a3f0ca265045a5241219e9Compiled from "NestingHost.java"
public class nesttest.NestingHost$NestedClass1minor version: 0major version: 54flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #6                          // nesttest/NestingHost$NestedClass1super_class: #7                         // java/lang/Objectinterfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:*** CONSTANT POOL DELETED FROM THE PRINTOUT ***{public nesttest.NestingHost$NestedClass1();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #2                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 6: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lnesttest/NestingHost$NestedClass1;static void access$100(nesttest.NestingHost$NestedClass1);descriptor: (Lnesttest/NestingHost$NestedClass1;)Vflags: (0x1008) ACC_STATIC, ACC_SYNTHETICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method privateMethod:()V4: returnLineNumberTable:line 6: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0    x0   Lnesttest/NestingHost$NestedClass1;
}
SourceFile: "NestingHost.java"
InnerClasses:public static #14= #6 of #25;           // NestedClass1=class nesttest/NestingHost$NestedClass1 of class nesttest/NestingHostpublic static #27= #3 of #25;           // NestedClass2=class nesttest/NestingHost$NestedClass2 of class nesttest/NestingHost

Java 10编译器生成access$100方法。 Java 11编译器没有。 相反,它在类文件中具有嵌套主机字段。 我们终于摆脱了那些在一些反映框架的代码中列出所有方法时引起意外的综合方法。

窝窝

让我们玩一些布谷鸟吧。 我们可以稍微修改一下代码,以便现在可以执行以下操作:

package nesttest;
public class NestingHost {
//    public class NestedClass1 {
//        public void publicMethod() {
//            new NestedClass2().privateMethod(); /* <-- this is line 8 */
//        }
//    }public class NestedClass2 {private void privateMethod() {System.out.println("hallo");}}
}

我们还创建了一个简单的测试类

package nesttest;public class HackNest {public static void main(String[] args) {
//        var nestling =new NestingHost().new NestedClass1();
//        nestling.publicMethod();}
}

首先,从所有行的开头删除所有//并编译项目。 它像魅力一样工作并打印出hallo 。 之后,将生成的类复制到安全的位置,例如项目的根目录。

$ cp build/classes/java/main/nesttest/NestingHost\$NestedClass1.class .
$ cp build/classes/java/main/nesttest/HackNest.class .

让我们编译项目,这次使用注释,然后将之前的编译中的两个类文件复制回去:

$ cp HackNest.class build/classes/java/main/nesttest/
$ cp NestingHost\$NestedClass1.class build/classes/java/main/nesttest/

现在我们有了一个NestingHost ,它知道它只有一个NestedClass2NestedClass2 。 但是,测试代码认为还有另一个NestedClass1 ,并且它还具有可以调用的公共方法。 这样,我们尝试将额外的雏鸟潜入巢中。 如果执行代码,则会出现错误:

$ java -cp build/classes/java/main/ nesttest.HackNest
Exception in thread "main" java.lang.IncompatibleClassChangeError: Type nesttest.NestingHost$NestedClass1 is not a nest member of nesttest.NestingHost: current type is not listed as a nest memberat nesttest.NestingHost$NestedClass1.publicMethod(NestingHost.java:8)at nesttest.HackNest.main(HackNest.java:7)

从代码中认识到导致错误的行是我们要调用私有方法的那一行,这一点很重要。 Java运行时仅在那一点而不是更快地进行检查。

我们喜欢还是不喜欢? 快速失败原则在哪里? 为什么Java运行时仅在非常需要时才开始执行类并检查嵌套结构? 原因,在Java情况下,原因很多:向后兼容。 加载所有类后,JVM可以检查嵌套结构的一致性。 这些类仅在使用时加载。 可以更改Java 11中的类加载,并与嵌套主机一起加载所有嵌套的类,但是这会破坏向后兼容性。 如果没有别的,懒惰的单例模式会崩溃,我们不希望那样。 我们爱单身,但只有单麦芽(是)。

结论

JEP-181是Java的一个小改动。 大多数开发人员甚至不会注意到。 它消除了技术债务,如果核心Java项目没有消除技术债务,那么我们对普通开发人员有何期待?

就像古老的拉丁语所说:“技术需要借记卡。”

翻译自: https://www.javacodegeeks.com/2018/08/nested-classes-private-methods.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/345984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 信号没有被处理方法,[计算机]Linux 信号signal处理机制.doc

[计算机]Linux 信号signal处理机制Linux 信号signal处理机制信号是Linux编程中非常重要的部分&#xff0c;本文将详细介绍信号机制的基本概念、Linux对信号机制的大致实现方法、如何使用信号&#xff0c;以及有关信号的几个系统调用。 信号机制是进程之间相互传递消息的一种方法…

自相关函数与互相关函数

1 概念 1 自相关函数 2 互相关函数 从定义式中可以看到&#xff0c;互相关函数和卷积运算类似&#xff0c;也是两个序列滑动相乘&#xff0c;但是区别在于&#xff1a;互相关的两个序列都不翻转&#xff0c;直接滑动相乘&#xff0c;求和&#xff1b;卷积的其中一个序列需要先…

Linux as4开启telnet,linux as4 虚拟机 上开启 telnet 和ssh 和 ftp 服务

1.telnet服务开启(1)输入[rootrehat ~]# chkconfig krb5-telnet --listkrb5-telnet on这是你的服务存在的状态&#xff0c;如果没有的话&#xff0c;可能是你的telnet名字和我的不一样&#xff0c;也可能是你的那个rpm包没有安装。我第一次的Linux中是没有安装的&#xff0…

解决MATLAB不能设置为.m文件默认打开方式

转载&#xff1a;https://blog.csdn.net/yujiaerzong/article/details/77624167 将下面代码复制保存为 associateFiles.m 文件。 或者从下面链接下载文件https://ww2.mathworks.cn/matlabcentral/fileexchange/51165-matlab-file-association-fix 在MATLAB中运行 associateFil…

linux 我的世界 跨平台联机,我的世界跨平台联机 PC、手机等平台数据互通

我的世界 ( MineCraft&#xff0c;简称 MC ) 》是一款开放世界沙盒建造游戏&#xff0c;有着超高的自由度&#xff0c;在国内外有着相当高的人气&#xff0c;各年龄层的玩家都非常的喜欢玩。在这次 E3 2017 微软展前发布会上&#xff0c;微软除了公布新主机 Xbox One X ( 原名 …

展望Java的未来:空值类型

尽管有前途的Java值类型不是迫在眉睫&#xff0c;但我偶尔还是喜欢在OpenJDK valhalla-dev邮件列表中打听一下&#xff0c;以了解事情的进展情况并了解即将发生的事情。 诚然&#xff0c;由于我对所用术语的了解有限&#xff0c;并且其中某些消息的底层细节&#xff0c;使我无法…

5G的场景、需求、通信速率

5G三大典型场景 5G有三大典型场景&#xff0c;这三大场景描述了5G的需求也反应了5G与4G的不同&#xff0c;如图所示&#xff0c;三大场景分别为&#xff1a;增强型移动宽带通信&#xff08;eMBB&#xff09;&#xff0c;大规模机器型通信&#xff08;eMTC&#xff09;和超高可…

fceux模拟器linux,超强FC模拟器fceux-2.2.3最新版

超强FC模拟器fceux-2.2.3最新版fceux一款超好用的FC模拟器软件&#xff0c;这个是最新版本的fceux-2.2.3-win32.zip较之早前版本&#xff0c;2.2.2 版本修正部分 bug 并添加了新功能&#xff0c;主要是调试和逆向编译工程的功能。较之早前版本&#xff0c;2.2.1 版本修正大量 b…

linux7禁用ipv6,RHEL 7 及 CentOS 7 彻底禁用IPv6的方法

原标题&#xff1a;RHEL 7 及 CentOS 7 彻底禁用IPv6的方法IPv6在未来可能成为主流&#xff0c;但是就目前而言&#xff0c;很多软件对IPv6的支持并不是很完善&#xff0c;可能导致各类问题。RHEL 7 & CentOS 7 在启动时默认是加载IPv6相关模块的&#xff0c;而禁用IPV6的方…

jpa 分页 排序 过滤_使用JPA标准@ViewScoped通过分页,过滤和排序进行Primefaces DataTable延迟加载...

jpa 分页 排序 过滤Primefaces数据表惰性分页有效&#xff0c;但是在Web上使用Criteria搜索完整示例后&#xff0c;我感到非常沮丧。 所以我混合了来自 http://stackoverflow.com/questions/13972193/how-to-query-data-for-primefaces-datatable-with-lazy-loading-and-pagin…

通信中的backhaul

backhaul 可以翻译成回程,也叫回程线路在现有的无线通信中,backhaul指的是基站和基站控制器之间的链接(一般用户先接入基站,基站再与基站控制器通信,然后进入核心网)。在无线技术中&#xff0c;回程&#xff08;backhaul&#xff09;指的是从信元站点向交换机传送语音和数据流量…

西班牙语言,字母c的发音规则,西语初学者必看:西语29个字母解读

其实西班牙语并没有英语那么复杂的发音规则&#xff0c;除了r这个字母比较难发&#xff0c;其他几乎每一个字母都只有一个特定的音。所以对于西班牙语初学者而言&#xff0c;不用太担心&#xff0c;因为难就退却了。为了更好地帮助初学者进入学习状态&#xff0c;就西班牙语的2…

宏基站、分布式基站、小基站

基站即公用移动通信基站&#xff0c;是无线电台站的一种形式&#xff0c;是指在一定的无线电覆盖区中&#xff0c;通过移动通信交换中心&#xff0c;与移动电话终端之间进行信息传递的无线电收发信电台。 目前&#xff0c;在 5G时代 &#xff0c;“ 宏基站 为主&#xff0c; 小…

OAUTH 2.0授权码授予

OAuth 2.0提供了许多安全性流程&#xff08;或授权类型&#xff09;&#xff0c;以允许一个应用程序访问另一个应用程序中的用户数据。 在此博客中&#xff0c;我们将介绍OAuth 2.0授权&#xff1a;授权代码授权。 首先&#xff0c;有许多定义&#xff1a; 客户端 &#xff…

什么是通信卫星有效载荷(payload)

卫星一般都是由两大部分组成&#xff0c;即有效载荷平台。 有效载荷是指卫星上用于直接实现卫星的自用目的或科研任务的仪器设备&#xff0c;如遥感卫星上使用的照相机&#xff0c;通信卫星上使用的通信转发器和通信天线等&#xff0c;按卫星的各种用途包括&#xff1a;通信转发…

c语言open参数,ifstream :: open()的C类型参数

我必须使用哪种类型的文件名作为ifstream.open()的参数&#xff1f;int main(int argc, char *argv[]) {string x,y,file;string file argv[1];ifstream in;in.open(file);in >> x;in >> y;...使用此代码,我收到以下错误&#xff1a;main.cpp|20|error: no matchi…

单播、广播、组播(多播)

当前的网络中有三种通讯模式&#xff1a;单播、广播、组播(多播)&#xff0c;其中的组播出现时间最晚但同时具备单播和广播的优点&#xff0c;最具有发展前景。 一、单播&#xff08;Unicast&#xff09; 主机之间“一对一”的通讯模式&#xff0c;网络中的交换机和路由器对数…

c语言错误re,c语言malloc之后再realloc的有关问题

C/C code#include #include #include "../Status.h"#define STACK_INIT_SIZE 5 //堆栈初始大小#define STACKINCREMENT 5 //堆栈满之后再增加的大小typedef char *stackelem;typedef struct{stackelem *base,*top;int stacksize;}sqstack;/*------堆栈基本操作------…

struts2面试问题_Struts2面试问答

struts2面试问题Struts2是用Java开发Web应用程序的著名框架之一。 最近&#xff0c;我写了很多Struts2教程 &#xff0c;在这篇文章中&#xff0c;我列出了一些重要的Struts2面试问题以及答案&#xff0c;以帮助您进行面试。 什么是Struts2&#xff1f; Struts1和Struts2之间…

什么是Mesh网络

网络间的通信原理 假设你的名字叫小不点&#xff0c;你住在一个大院子里&#xff0c;你的邻居有很多小伙伴&#xff0c;在门口传达室还有个看大门的李大爷&#xff0c;李大爷就是你的网关。当你想跟院子里的某个小伙伴玩&#xff0c;只要你在院子里大喊一声他的名字&#xff0…