RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 559431
Accepted
Ihor Savchenko
Ihor Savchenko
Asked:2020-08-25 23:00:26 +0000 UTC2020-08-25 23:00:26 +0000 UTC 2020-08-25 23:00:26 +0000 UTC

访问祖先类的私有字段

  • 772

据我了解,当通过new创建一个类对象时,在Heap中分配了一个区域来存储类本身的所有字段和它所有祖先的非静态公共(和保护)字段。然后依次调用所有祖先的构造函数,从最旧的构造函数开始,初始化并可能覆盖该对象字段的值。并且在最后,子对象的构造函数自己初始化它的新字段,或者可能覆盖祖先构造函数已经初始化的字段的值。问题如下。如果祖先有它们的私有字段和公共getter,而继承人没有重写这些字段和getter,那么从继承人的对象中,通过调用继承的getter,可以从祖先的私有字段中获取该字段的值. 它是如何发生的?创建一个对象时,它所有祖先的对象是否都在单独的内存区域中创建,并具有它们的所有字段?或者,在我看来更有可能的是,JVM 理解如果祖先有公共 getter,继承人可以访问其私有字段,因此在子对象的内存区域中创建其祖先的私有字段?

jvm
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Sergey Gornostaev
    2020-08-18T16:35:26Z2020-08-18T16:35:26Z

    除其他信息外,类对象模块还包含有关继承层次结构、字段顺序及其大小的信息。也就是说,类加载后,元空间中的虚拟机总是有一个这个类的对象的“映射”,通过它可以计算出这个或那个字段距内存块开头的偏移量. 当创建一个新对象时,会分配一个足够大小的块来存储对象的标题、它的字段以及它所有超类的字段。此外,字段从继承根开始按顺序排列——首先是超类的字段,然后是子类的字段。由于这种安排,超类的“地图”适用于导航子类的对象。为了清楚起见,我们定义了一个原始类层次结构

    class A {
        int x;
        int y;
    }
    
    class B extends A {
        int z;
    }
    

    该操作B obj = new B()将在堆上分配这样一个块

     -----------   --
    | Заголовок |    |
     -----------     |_ Класс A
    |     x     |    |
    |     y     |    |
     -----------   --
    |     z     |    |- Класс B
     -----------   --
    

    如果我们现在将对象类型转换为基本类型,那么虚拟机将对对象的字段执行访问操作,就好像没有尾部包含该字段一样z。

    感谢Alexey Shipilev,我们可以使用 jol(Java 对象布局)工具看到这一点。

    import org.openjdk.jol.info.ClassLayout;
    import org.openjdk.jol.vm.VM;
    
    public class ShowLayout {
        public static void main(String[] args) throws Exception {
            System.out.println(VM.current().details());
            System.out.println(ClassLayout.parseClass(B.class).toPrintable());
        }
    }
    

    编译

    javac -cp jol-cli-0.9-full.jar ShowLayout.java
    

    发射

    java -javaagent:jol-cli-0.9-full.jar ShowLayout
    

    我们得到

    # Running 64-bit HotSpot VM.
    # Using compressed oop with 3-bit shift.
    # Using compressed klass with 3-bit shift.
    # Objects are 8 bytes aligned.
    # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
    # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
    
    B object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0    12    (object header)                               N/A
         12     4    int A.x                                       N/A
         16     4    int A.y                                       N/A
         20     4    int B.z                                       N/A
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    

    对于每个类的方法,JVM 也有一个“映射”,指示特定方法的开始位置在内存中的偏移量。有两种方法可以使用这个“地图”——早期绑定和后期绑定(实际上更多,但在当前上下文中无关紧要)。普通方法调用

    obj.getZ()
    

    将被编译为字节码

    aload_1           // Загрузка в стек ссылки на obj
    invokevirtual #4  // Method B.getZ:()I
    

    该指令invokevirtual使用后期绑定。也就是说,当一个方法被调用时,JVM 分析调用上下文(调用点),确定需要哪个方法并在所需的偏移处转移控制。因此,多态性是可能的。

    调用超类方法(以及调用构造函数和私有方法)

    super.getX();
    

    将被编译为字节码

    aload_0           // Ссылка this на объект класса B
    invokespecial #2  // Method A.getX:()I
    

    该指令invokespecial使用较早的绑定。也就是说,即使在加载类的阶段,也很清楚需要调用哪个特定类的哪个方法,JVM 会将这个方法的偏移量“缝合”到字节码中。

    收到控制权后,该方法getX(当然是在 JVM 的帮助下)将计算从传递的引用指向的内存块的开头到x类中字段应位于的位置所需的偏移量A,读取其值,将其放在堆栈顶部并将控制权返回给调用代码。甚至没有意识到这个引用this实际上指向了一个更大的对象,也没有意识到子类中是否有与它同名的方法。

    • 5

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5