CS2 软件设计与数据结构

Chapter 5 Introduction to Pointers in Java

| 关于   «  3. 基本引用 部分 2   ::   目录   ::   5. 局部内存  »

4. 指针语法

4.1. 语法

前述关于引用、被引用对象、解引用和赋值的基本特性,是构建引用代码所需的唯一概念。然而,为了讨论引用代码,我们需要使用一种已知的语法,而这种语法的趣味性大概就和……语法本身一样。我们将使用 Java 语言语法,它的优势在于影响了多种语言的语法。

4.1.1. 声明引用变量

对非原始数据类型(即对任何 object 的引用)的引用是通过声明该 object 类型的变量来定义的。换句话说,引用变量的声明方式与其他任何变量相同。声明会指定新变量的类型和标识符,并预留内存以存储其值。但是—这里是最重要的一点—声明引用变量并不会为该引用分配一个 pointee。在将其赋值给某个对象之前,该引用的初始值为“无效”值。

    Employee empPtr1; // Declare a reference to an Employee
    // This allocates space for the reference, but not the pointee.
    // The reference starts out as "null"
    Employee empPtr1; // Declare a reference to an Employee
    // This allocates space for the reference, but not the pointee.
    // The reference starts out as "null"

4.1.2. 将指针赋值给引用

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

4.1.3. 解引用该指针

每当引用变量出现在赋值语句左侧 以外 的任何位置时,它都会被解引用。这与原始类型变量(如 int )的使用方式并无不同。请看以下代码:

int a = 5; int b = a;

在此情况下,第二行中 a 的值仅为 5。完全相同地,在此代码中:

Employee empPtr = johnRef;

johnRef 的值仅仅是它所引用的对象的位置。这就是为什么 empPtr 最终指向同一个对象的原因。当然,每当你对任何引用变量进行解引用时,它最好有一个被指向的对象。否则,你会得到一个类型为 NullPointerException 的运行时错误。

4.1.4. 示例参考代码

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

4.2. changeHeadPointer1

4.2.1. 引用规则总结

无论引用结构变得多么复杂,规则列表始终很短。

  • 引用变量存储指向其目标的引用。目标本身则存储有用的内容。

  • 对引用的解引用操作会访问其指向的对象。引用只有在被赋值为指向某个对象后,才能进行解引用。大多数引用相关的错误都源于违反这一规则。

  • 分配引用并不会自动将其指向某个被引用对象。将引用赋值为指向特定被引用对象是一个独立的操作,这一点很容易被遗忘。

  • 两个引用之间的赋值使它们指向同一个被引用对象,从而允许 object sharing 。

4.3. Java 引用与指针

Java 引用具有两个主要特性,使其区别于 C 或 C++ 等语言中限制较少的指针。

  1. 更少的错误。由于语言准确且自动地实现了引用操作,最常见的引用错误已不再可能发生。太好了!此外,Java 运行时系统每次使用引用值时都会进行检查,因此解引用 null 引用会立即在发生该行被捕获。这与 C++ 等语言形成对比,在 C++ 中解引用 null 的值可能直到后来才会导致程序崩溃。这让程序员能够确切知道问题发生的位置,从而大大提高生产力。

  2. 较慢。由于该语言负责在运行时实现大量引用机制,并执行大量额外的运行时检查,Java 代码的运行速度慢于 C 和 C++ 等其他语言。但对于许多应用程序而言,提高程序员效率并减少错误所带来的吸引力,使得这种速度上的牺牲是值得的。

4.4. 机器中是如何实现引用的?

引用是如何实现的?简短的解释是,机器中的每个内存区域都有一个数字地址,如 1000 或 20452。你可以将内存想象成一个大数组,内存中的每个位置都有一个索引,即其内存地址。对内存区域的引用实际上只是一个存储该内存区域地址的整数。

在上图中,我们假设 Java 决定将新的 Employee 对象放置在内存地址 2000 处。因此,引用变量仅存储值 2000。如果我们仔细查看计算机的内存,就能确切地看到 Employee 对象在内存字节中(从 2000 开始)是如何布局的。但我们在此图中并未展示这一点,因为 Employee 对象的内部实现细节比我们目前需要关心的内容更为复杂。(但如果您真的想了解,不妨去查阅一下,您会学到很多。)

解引用操作会查看引用变量的地址,并前往该内存位置以检索其中存储的被引用对象。将一个引用变量赋值给另一个引用变量只是复制了数字形式的内存地址。这与在两个 int 变量之间进行赋值时发生的情况完全相同:值只是从一个复制到另一个。 null 的值始终是一个特殊值,Java 绝不会将其用作任何合法被引用对象的位置。坏引用实际上就是包含 null 值的引用。Java 的运行时环境会持续监视对具有 null 值的引用变量进行的解引用操作,以便在发生时立即捕获它。

   «  3. 基本引用 部分 2   ::   目录   ::   5. 局部内存  »

关闭窗口