建议阅读:章节9.5和9.10

关于对象变量的更多信息

我在前面提到过,Java有两大类数据类型:基本类型和对象类型。在今天的课上,我想指出这两种变量在行为上的一些重要区别。

让我们首先看一些涉及基本数据类型的简单代码示例。

在第一个例子中,我将编写一些涉及将一个变量赋值给另一个变量的代码。

Int a = 3;Int b = a;b + +;system . out。Println ("a = " + a);system . out。Println ("b = " + b);

这段代码中需要注意的关键语句是赋值语句

Int b = a;

这条语句取a的当前值3,并将该值赋给另一个变量b。显然,这导致b的值也为3。如果我们随后增加b,这将只影响b的值,而不会影响a的值。然后print语句将报告a的值为3,b的值为4。

下面是另一个相关的例子。在这个例子中,我们将一个值传递给一个方法。

假设我们已经编写了一个方法

公共静态void incrementAndPrint(int x) {x++;system . out。Println ("x = " + x);}

下面是一些使用这个方法的代码:

Int a = 3;incrementAndPrint(一个);system . out。Println ("a = " + a);

当您将原始类型的变量作为参数传递给方法时,该变量的值将被复制到方法参数中。该方法随后修改参数所做的任何事情都将使用原始数据的副本。这不会影响传递给方法的变量的值。

在这个例子中,代码首先打印出x的值是4,然后打印出a的值是3。

下一个示例涉及到一个简单类的使用。下面是该类的代码。

公共类计数器{私有int值;public Counter(int startingValue) {value = startingValue;}公共无效增量(){value++;}公共无效打印(){系统。println("My value is " + value);}}

下面是一些利用这个类的代码片段。

Counter a = new Counter(3);计数器b = a;b.increment ();b.print ();a.print ();

运行这段代码将导致程序打印消息

我的值是4我的值是4

第一条消息是我们所期望的,因为我们告诉b在打印前增加。第二条消息可能有些出乎意料,因为我们从来没有告诉a增加它的值。

这是怎么回事?要完全理解这个示例,需要更详细地理解在创建和使用对象时发生了什么。下面的示例更详细地展示了执行上面第一个语句时程序中发生的情况。

当你用new创建一个对象时,计算机在程序中创建这个对象,然后给你一些关于这个对象所在位置的信息。该位置信息被存储在对象变量中。如上图所示,对象本身是独立于变量a存在的实体。变量a存储对象所在位置的信息,如图中从变量a指向对象本身的箭头所示。

Java中的对象变量有时被称为对象引用变量,因为它们的目的是指向实际的对象。

下图说明了当我们执行设置变量b的语句时发生了什么:

将a赋值给b只是将位置信息从变量a复制到变量b。这导致我们有两个对象变量都引用同一个对象的情况。由于两个变量都引用同一个对象,如果我们使用两个变量中的任何一个来调用更新对象状态的方法,两个变量将看到相同的变化。稍后,当我们执行print方法时,两个方法将在同一个对象上被调用,这自然会两次打印相同的值。

当您将对象类型的变量传递给方法时,也会看到相同的现象。假设我们有一个方法

公共静态无效incrementAndPrint(计数器c) {c.increment();c.print ();}

我们执行这段代码:

Counter a = new Counter(3);
incrementAndPrint(a);
a.print();

这将导致程序打印

我的值是4我的值是4

下图说明了我们在方法中执行增量操作之前的情况:

将a作为参数传递给方法会导致存储在a中的位置信息被复制到参数c中。这再次导致两个变量引用同一对象的情况。如果我们随后使用c来增加对象中的值,a会看到这个变化,因为a引用的是同一个对象。

空值

既然我们理解了对象引用变量和它们引用的对象是不同的东西,我们可以设置对象引用变量不引用任何对象的情况。

最简单的方法是设置一个对象变量,然后忘记初始化它。

对抗;a.increment ();a.print ();

NetBeans将立即将此标记为错误,因为它将立即注意到对象引用变量尚未初始化。这使得无法编译和运行此代码。

下面是一个比较隐晦的例子:

计数器a = null;a.increment ();a.print ();

NetBeans不会将此标记为错误,因为这次我们实际上是在初始化变量。我们用来初始化变量的空值设置了a不引用任何内容的情况。

如果我们继续运行这些语句,当我们尝试执行第二行代码时,代码将抛出空指针异常。当您尝试使用对象引用变量调用方法时,如果该变量实际上并不指向对象,就会发生此异常。

使用对象引用将对象链接在一起

在这些笔记的下一部分中,我将把一个类的扩展示例放在一起。这门课对我们周五的一个更大的例子很有用。此外,这个示例类将使我有机会说明对象引用变量的另一个赢博体育。

在计算机科学中,堆栈是一种特殊类型的列表,用于存储垂直排列的项目集合。栈支持两种主要操作:push操作(将新项放到列表顶部)和pop操作(移除列表顶部的项)。

这张图展示了我们将值2、3和4压入初始空堆栈的顶部后堆栈的样子。

为了实现这个结构,我将设置一种安排,其中堆栈上的每个数据项都存储在一个单独的对象中。这些对象将使用对象引用变量系统连接在一起。

第一步是定义一个简单的类,它可以在堆栈中存储单个数据项。

公共类StackItem{公共int值;public StackItem next;public StackItem(int x) {value = x;Next = null;}}

使用这个辅助类,我们可以设置堆栈类:

公共类栈{私有StackItem顶部;public Stack() {top = null;} public void push(int x) {StackItem newItem = new StackItem(x);newItem。Next = top;top = newItem;}公共布尔空(){返回顶部== null;}公共int peek(){返回top.value;}公共void pop() {top = top.next;}}

如果我们现在运行下面的代码

Stack S = new Stack();
S.push(2);
S.push(3);
S.push(4);

我们最终会得到一个像这样的对象集合:

变量S引用Stack对象。该对象的顶部成员变量引用堆栈顶部的stackknode。最前面的两个StackNode对象使用它们的next成员指向堆栈中它们下面的StackNode对象。堆栈底部的stackknode保留null值,表示在它下面的堆栈中没有任何东西。