更好的理解 Java 中的 对象引用 是面向对象技术的前提,本文旨在讨论如何理解它们的含义以及它们之间的关系。如有错误,还请指出。

对象

《编程思想》中说到:**对象是某个类(class)的一个实例(instance)。**如果单纯的理解这句话,我们可以说:对象是类所产生的实例。例如对于 人类 这一类别来说,小明 这一具体的对象就是 人类 产生的 实例

为了便于理解,我们首先定义一个简单的类:

1
2
3
4
class Person {
    private String name;
    private int age;
}

然后可以使用以下语句创建对象:

1
Person p = new Person();

这是我初次学习 Java 面向对象的时候,视频中的老师给出的引子。对于一个 来说,它是构造对象的模板和蓝图。而通过 来构造对象的过程称为 创建类的实例,也叫 实例化

上面的语句创建了一个对象,有如下几步:

  • new Person 是以 Person 类为模板或者蓝图,在堆空间中创建了一个 Person 类对象;
  • 后面的 () 表示在创建对象后,调用了 Person 类中的构造函数,对刚刚创建的对象进行初始化操作;

此时,我们再看如下代码:

1
2
Person p;
p = new Person();

这看似和上面的代码没有什么差别,但更方便理解。这里首先创建了一个 Person 类型的引用变量 p,然后用对象的引用 p 通过 = 操作符指向刚才创建的 Person 对象。

很显然,这里的 p 是对象的一个引用,是一个可以指向 Person 类的对象的引用,而不是所创建的对象,真正创建对象的语句是 new Person()

引用

一个 Person 类可以创建出无数的对象,我们只能通过 对象引用 来间接访问对象。这里做一个形象的比喻:一根绳子系住气球。我们将 对象 比作气球,将 引用变量 比作绳子。

如果仅执行 Person p; 语句的话,此时创建的一个引用变量 p 还没有指向任何一个对象,其默认是 null。这就相当于一根还没有系住任何气球的绳子。如果执行了 p = new Person(); 语句,则就可以说明对象引用 p 指向了一个对象,也就是绳子系住了气球。

此时,再声明一个 Person 类型的引用,如下所示:

1
Person p1;

表示又做了一根绳子,但是还没有系上气球。如果再加上一句 p1 = p;,则说明系上了气球。因为这里对象没有被复制,而是对象的引用被复制了,所以 p1 也指向了 p 所指的对象。两根绳子(p 引用 和 p1 引用)系上的是同一个气球(对象)。

如果执行了以下语句:

1
p1 = new Person();

则说明 p1 这一引用指向了另外一个对象,即另一个气球。

此时,可以得到:

  • 一个引用可以指向 0 个或 1 个对象,即一根绳子可以不系住气球,也可以系住一个气球;
  • 一个对象可以被多个引用指向,即一个气球可以被多个绳子系住。

此时,再执行下面的语句:

1
p = p1;

同理,p 指向了第二个对象,也就是说,p 指向了 p1 所指的对象。那么第一个对象去哪儿了呢?第一个气球没有绳子了,它往哪儿飞了呢?其实,第一个对象被 Java 中的垃圾回收机制给回收了。

好了,现在我们不看上面的语句,只看下面的语句:

1
new Person();

这样写也是合法的,表明生成一个临时对象,只是没有引用变量来引用它而已。

例子

创建一个对象

为了更详细的说明 Java 创建对象的过程,可以参考以下图像:

image.png

如上图所示,左侧表示代码,右侧表示其在 Java 虚拟机中具体的表现形式。关于 JVM 的内存区域这里不再做详细的解释,你可以查看笔者的《Java 内存区域概述》这篇文章。

这里解释一下代码执行的流程:

  • 首先 Demo1_Car 类中的 main 方法进入方法区,当遇到 Car c1 = new Car(); 语句的时候,Car 类及其内部的成员变量和成员方法也进入到方法区;
  • 然后开始在堆中创建对象,并且 对象引用 c1 指向堆中的地址为 0x0011
  • 由于刚刚创建对象,所以需要将对象进行初始化,则引用类型的 color 一开始的默认值是 null,基本数据类型的 num 一开始的默认值是 0
  • 经过 c1.color = "red"; 以及 c1.num = 8; 语句后,color 被修改为 rednum 被修改为 8
  • 最后在栈中,也就是 main 函数的上面压入一个 run() 方法(图中未画出);
  • 这里最重要的就是 c1 是对象的一个引用,就像绳子一样。

创建两个对象

下面给出创建两个对象,具有三个引用的示例:

image.png

流程和上面的大体相同:

  • 在执行到 Car c1 = new Car(); 语句的时候,堆中先创建了一个地址为 0x0011 的对象,并且栈中的 c1 指向第一个对象的地址,即 0x0011
  • 执行到 Car c2 = new Car(); 语句的时候,堆中也是先创建了另外一个地址为 0x0022 的对象,栈中的 c2 指向第二个对象的地址,即 0x0022
  • 执行到 Car c3 = c2; 的时候,先生成一个对象引用 c3,然后 c3 指向和 c2 同样的地址,即 0x0022,此时 c2c3 指向的就是同一个对象。

以上就是对 Java 中的 对象 以及 对象的引用 进行的总结,理解这些对于 Java 中的值传递是非常有意义的。

参考