c++ 嵌套私有类

当您在另一个类中有一个类时,他们可以看到彼此的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,它将通过引入一个新概念嵌套来解决此问题。 当前,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

c++ 嵌套私有类

c++ 嵌套私有类_嵌套类和私有方法相关推荐

  1. java 通配符 类_关于类:具有多个类的Java泛型通配符

    我想要一个类对象,但是我想要强制它所代表的任何类来扩展类A和实现接口B. 我能做到: Class extends ClassA> 或: Class extends InterfaceB> ...

  2. java元类_元类 - 一心不乱 - 博客园

    我们使用class机制产生的类,然后在通过类产生的对象 而类实际上都是通过type来实现的 首先确定type里需要添加哪些元素 class_name = 'A'   类名 class_base = ( ...

  3. java电脑类_计算机类在Java中的设计于实现码

    计算机类在Java中的设计于实现码 问题描述: 一台计算机是由主板.CPU.显卡.声卡等部件组成的,这些部件通过接口可以直接安插在主板的插槽上,也就是说只要将这些部件简单的安插在一起就可以成功组装出一 ...

  4. python 嵌套函数过多_嵌套函数的性能开销是多少?

    性能惩罚肯定存在.如果函数是在对另一个函数的调用中创建的,那么每次调用外部函数时都会创建函数对象.但这种惩罚很小,而且通常可以忽略不计.尤其要考虑到一个显而易见的事实:在大多数情况下,只有当嵌套函数不 ...

  5. vba 定义类_在类中定义和触发自己的事件(上)

    大家好,我们今天继续讲解类的相关知识.在上一讲中我们讲了使用WithEvents定义响应事件的对象,那么有了对象自然要跟进到事件了,这讲就给大家讲解两个新技能--在类中定义和触发自己的事件(Event ...

  6. python定义一个汽车类_汽车类Python程序

    您的代码有几个问题:您尚未创建实例:mycar = Car(year, make, speed) 您已将mycar重命名为my_car:即my_car.brake() 类属性称为self.__spee ...

  7. java中友元类_友元类成员的依赖关系|循环依赖

    定义一个CBottle类,另一个类CCarton的某个成员对CBottle进行操作,因此在CBottle类中赋予CCarton成员的友元权利.我们很容易写出如下代码: //CBottle类的头文件 b ...

  8. weblogic启动项目报错找不到类_启动类报错是经常出现的事但是单一的从一个地方找原因会越找越错...

    Error starting ApplicationContext. To display the conditions   report rerun your application with 'd ...

  9. C++_类和对象_对象特性_友元_友元类_在一个类中声明另一类作为自己的友元类_可以访问自己类中的private变量---C++语言工作笔记053

    然后我们再来一个去看看友元类,用类来做友元,上面我们说的是全局函数做友元. 首先我们先去写一个类叫GooGay好基友. 然后里面去定义一个成员变量,这个成员变量可以看到类型是 Building这个类, ...

最新文章

  1. 数据库MYSQL学习系列三
  2. r730xd服务器文档,r730xd配置服务器远程
  3. HDU 1402——A * B Problem Plus
  4. Unity加载模块深度解析(纹理篇)
  5. javascript配置ckfinder的路径
  6. linux系统有gotoxy函数,linux下有没有类似gotoxy()的函数
  7. 在Win 10 中插入U盘,电脑没有弹出也不显示盘符,在其他电脑就可以【亲测有效】
  8. 买水货必看!你必须知道的10件事情
  9. cint、int、fix的区别
  10. linux 浏览器崩溃,Firefox DoS漏洞导致浏览器崩溃 影响到Windows操作系统
  11. JavaSE进阶之(十)Map 子接口之 ConcurrentHashMap
  12. python实现之导数
  13. 提高程序并发量的几个建议(不看保证后悔死你)
  14. 简单介绍快速开始使用Unity引擎的步骤
  15. linux无法切用户,为什么我的linux 无法进行用户的切换?(ylmf os3.0)
  16. 激活 MarkDownPad 2
  17. 运用Acronis True Image恢复系统
  18. 《致我们终将逝去的青春》经典语录
  19. 前端实现 PDF 预览的常见方案
  20. SpringCloud Gateway打印请求、响应内容和唯一流水号配置

热门文章

  1. 【CF 1195】Basketball Exercise/Submarine in the Rybinsk Sea (hard edition)/OpenStreetMap+二维单调队列滑动窗口模板
  2. YBTOJ:消除格子(二分图匹配)
  3. 新疆大学ACM新生赛(公开赛)
  4. 【DP】楼梯(jzoj 1520)
  5. 【动态规划】数字金字塔
  6. SpringBoot2.1.9 多数据源JDBC配置
  7. Hadoop入门(十六)Mapreduce的单表关联程序
  8. 一次堆外OOM问题的排查过程
  9. IntelliJ IDEA 2018.2 发布,支持 Java 11
  10. 一次恐怖的 Java 内存泄漏排查实战