单个引用变量如何访问所有对象字段?

时间:2018-03-20 14:36:11

标签: java jvm

Animal myAnimal = new Animal();

我上面有这个代码。据我所知,它会做这些事情:

  1. 将在堆内存上创建动物对象
  2. 对该对象的引用将传递给引用变量“myAnimal” 换句话说,“myAnimal”变量保存堆上“Animal”对象的内存地址。
  3. 我不明白的是

    1. 内存地址值如何?
    2. 是一个地址还是众多地址值?如果只有一个,myAnimal如何访问Animal对象的所有对象字段,如myAnimal.name,myAnimal.height,......?
    3. 有人可以解释一下吗?提前谢谢。

4 个答案:

答案 0 :(得分:6)

这是过于简单化了:

  1. 这只是一个代表某个地址位置的数字;这个数字有多大取决于您的计算机架构(32位或64位)

  2. 这是一个地址值;它是您的对象在内存中的对象表示的地址位置开始

  3. 您可以将其与您家的地址进行比较。它有一个地址。您家(场)中的所有房间都有不同的位置,但您必须通过前门(“开始”位置)进入。房间位置与前门相关。我承认这个例子有点做作,但你明白了......

答案 1 :(得分:3)

Java虚拟机规范states

  

有三种引用类型:类类型,数组类型,   和接口类型。 他们的值是动态参考   创建了类实例,数组或类实例或数组   分别实现接口。

clarifies

  

reference 类型的值可以被认为是作为对象的指针

所以

中的变量myAnimal
Animal myAnimal = new Animal();

存储指向堆上Animal对象的指针。

你问

  

内存地址值如何?

存储器地址通常只是一个数值,它是进程中的一个偏移量。分配内存。当进程读取该值时,它可以直接寻址该位置并对其进行读取或写入(将其视为数组中的偏移索引)。

对象本身不仅仅是它的地址。事实上,当垃圾收集器移动对象时,它的地址可以在JVM进程的生命周期内多次更改。

然而,JVMS does not specify对象的内部结构。

  

Java虚拟机不强制要求任何特定的内部   对象的结构。

     

在Oracle的一些Java虚拟机实现中,a   对类实例的引用是指向自身句柄的指针   一对指针:一对包含方法的表   object和指向Class对象的指针,表示类型   对象,另一个是从堆分配的内存   对象数据。

这并没有太大帮助,但是我们可以假设为对象数据分配的内存必须足够大以包含所有对象的字段,并且数据需要快速访问,即。在恒定时间内,而不是与数据量成比例。典型的解决方案是再次使用偏移量。以此课程为例

class Animal {
    private String name;
    private byte height;
}

字段name的类型为String,是一种引用类型,我们知道它实际上只存储一个指针。如果我们假设我们的JVM只需要32位来存储指针,我们知道这个字段只需要32位。

字段height的类型为byte,指定只需要8位。

因此每个Person对象实际上只需要32+8位,其数据为5个字节。 JVM最有可能为其内部组织分配更多,但让我们简化为这5个字节。在您的示例中,JVM将为name分配前4个字节,为age分配下一个字节。像

这样的东西
 0        8       16       24       32       40
 +--------+--------+--------+--------+--------+
 |               name                | height |

你问

  

这是一个地址还是众多地址值?如果只有一个,怎么可以   myAnimal可以访问Animal对象的所有对象字段   myAnimal.name,myAnimal.height,......?

myAnimal只包含一个地址,但只包含

这样的表达式
myAnimal.height

可以被认为是从存储器读取1个字节(因为我们知道height是字节类型),通过加4来确定地址(因为height的数据位置被偏移了name}所需的4个字节到myAnimal中存储的值。

考虑myAnimal存储指向内存地址12的指针,初始化为name指向内存地址为1345的字符串和height值6.在内存中,这看起来像

                   myAnimal
                      |
                      v
Heap:             ...12.......13.......14.......15.......16.......17
Object offsets:   ....0        1        2        3        4        5
                  ....+--------+--------+--------+--------+--------+ 
                      |               1345                |    6   | 

要读取myAnimal.height,JVM将计算12 + 4 = 16并读取该偏移处的字节(6)。要为myAnimal.name分配新值,JVM将计算12 + 0,并写入代表新指针值的4个字节,覆盖1345

答案 2 :(得分:2)

地址仅为long个数字,表示对象的内存位置。它们取决于机器的位数。它在64位机器上是64位长,在32位机器上是32位长。

  

内存地址值如何?

如果您真的热衷于在控制台中看到这些大数字,那么您可以实际探索Unsafe API

public native long getAddress(long address)
  

从给定的内存地址获取本机指针。如果地址是   零,或者没有指向从#allocateMemory获得的块,结果是未定义的。

     

如果本机指针的宽度小于64位,则扩展为   一个Java长的无符号数。指针可以由任何索引   给定字节偏移量,只需将该偏移量(作为一个简单整数)添加到   表示指针的long。实际读取的字节数   从目标地址可以通过咨询#addressSize确定。

  

这是一个地址还是众多地址值?如果只有一个,myAnimal如何访问Animal对象的所有对象字段,如myAnimal.name,myAnimal.height,......?

它应该在顶级一个,地址位置可能包含其他人的地址位置(我不太确定)

我还没有尝试在我的机器上运行它。

答案 3 :(得分:0)

创建对象时,java不会使用引用变量共享其Actual Memory Address,而不是Java为index number创建object,并将其传递给reference variable那个对象。因此,您可以借助index number访问您的对象。此外,您的对象会保留其refrences的{​​{1}}并帮助您访问它们。