Java 指针引论

Chapter 0 Introduction to Pointers in Java

| 关于   «  1. 指针章简介   ::   目录   ::   3. 基本引用 部分 2  »

2. 基础参考资料 第 1 部分

2.1. 指针与引用

2.1.1. 什么是指针?

有很多漂亮、整洁的代码可以在不了解 指针 的情况下编写。但一旦你学会使用指针的力量,就再也无法回头。有太多事情只能通过指针来完成。然而,能力越大,责任也越大。指针会引发新的、更糟糕类型的错误,而指针错误可能以随机方式导致程序崩溃,这使得调试更加困难。尽管如此,即使存在这些问题,指针仍然是一种令人难以抗拒的强大编程构造。

指针解决了两个常见的软件问题。首先,指针允许代码的不同部分轻松共享信息。你可以通过来回复制信息达到相同的效果,但指针能更好地解决这个问题。其次,指针支持复杂的链接数据结构,如 链表 和 二叉树 。

2.1.2. 什么是指针?

Java 实际上使用了指针概念的一个受限版本,称为 引用 。虽然它们的意思大致相同,但术语“指针”往往用于不特定于任何语言或实现的讨论中。“指针”一词暗示了 C/C++ 中将指针实现为 地址 或内存位置的常见方式。引用仅“指向”一个对象。这意味着程序员通过引用获得的访问权限更为有限。虽然这限制了他们的操作,但 Java 的理念是,代码更有可能正确运行,从而完全弥补了这一限制。Java 程序员只能为引用赋值并比较两个引用是否相等。引用的其他用法都是隐式完成的,程序员无法控制。这些限制降低了出现错误的可能性。

2.2. Java 中的数据类型

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

(顺便提一下,对于被指针—指向的对象这一概念,并没有通用的术语;“被指针指向的对象”只是我们在这些解释中使用的词。)

回到像 int 和 float 变量这样简单的事物,它们只是将值存储在盒子中:在 Java 中,这些被称为 原始数据类型 。在 Java 中,对象和数组是非原始数据类型,并且总是通过引用进行访问。Java 会自动在幕后为这类复杂类型使用引用,而且没有特定于引用的语法(不像 C/C++ 那样)。你只需要意识到,如果 a 和 b 是数组或对象,那么像 a = b 这样的赋值操作将自动通过引用来实现, 这与 a 和 b 是像 int 这样的原始对象时的行为不同 。带有数组和对象的赋值与参数本质上是浅层或共享的—,这将在下面的“浅拷贝与深拷贝”部分讨论。

2.3. 引用与解引用

解引用 表示跟随引用以获取其指向对象的值。在上图中对 empRef 进行解引用会返回其指向对象,即 Employee 对象。因此,“解引用”仅意味着访问指向对象的值。从视觉上看,解引用的结果就是箭头所指向的对象。在 Java 中,这通常通过“点”运算符来完成,以访问对象的字段或方法。例如:

String myName = empRef.name()

这将解引用 empRef 以调用该对象的 name 方法。

关键限制是引用必须指向一个目标才能访问。引用代码中的许多错误都源于违反这一限制,从而导致常见的 NullPointerException 。在进行解引用操作之前,必须先为引用分配一个目标。

常量 null 是一个特殊的引用值,它编码了“不指向任何对象”的概念。拥有一个定义明确的引用值来表示引用没有目标对象是很方便的。尝试获取 null 引用的目标对象会导致运行时错误。在图示中,值 null 通常被画成连接引用变量方框对角的斜线。

2.4. changePointeeDataDirect

2.5. Employee 类

我们将大量使用 Employee 对象作为示例,因此现在正式介绍一下它。认识一下 Employee 类。

/**
 * Employee is a class used in this tutorial to explain various pointer concepts
 */
class Employee {
  String name;
  int salary;

  /**
   * class constructor to initialize name and salary fields
   * @param name: employee name
   * @param salary: employee salary
   */
  public Employee(String name, int salary)
  {
    this.name = name;
    this.salary = salary;
  }

  /**
   * getter method for the name field
   * @return the value of name field
   */
  public String getName()
  {
    return name;
  }

  /**
   * setter method for the name field
   * @param newName the value to be assigned to name field
   */
  public void setName(String newName)
  {
    name = newName;
  }

  /**
   * getter method for the salary field
   * @return the value of salary field
   */
  public int getSalary()
  {
    return salary;
  }

  /**
   * setter method for the salary field
   * @param newSalary the value to be assigned to salary field
   */
  public void setSalary(int newSalary)
  {
    salary = newSalary;
  }
}

2.6. 引用赋值

将一个引用赋值( = )给另一个引用会使它们指向同一个被引用对象。这是一条适用于潜在复杂情况的简单规则,因此值得重申:将一个引用赋值给另一个引用会使它们指向同一事物。下面的示例添加了第二个名为 second 的引用,并通过以下语句进行赋值:

second = empRef;

结果是 second 指向与 empRef 相同的指针。在图中,这意味着 second 和 empRef 框都包含指向 Employee 对象的箭头。引用之间的赋值不会改变甚至触及指针。它只改变引用所指向的指针。

赋值后,测试 (second == empRef) 将返回 true。

赋值操作也适用于 null 值。使用 null 引用进行的赋值操作会将 null 值从一个引用复制到另一个引用。

内存图示是思考引用代码的关键。当你查看代码时,请思考它在运行时将如何使用内存,然后绘制简图来梳理你的思路。本教程使用大量图示来展示引用的工作原理。你也应该这样做。

2.7. circularList1

   «  1. 指针章简介   ::   目录   ::   3. 基本引用 部分 2  »

关闭窗口