创建和使用对象
了解什么是对象
一个典型的 Java 程序会创建许多对象,众所周知,这些对象通过调用方法来交互。通过这些对象交互,程序可以执行各种任务,例如实现 GUI、运行动画或通过网络发送和接收信息。一旦对象完成了其创建目的的工作,其资源就会被回收供其他对象使用。
这里有一个名为 CreateObjectDemo
的小程序,它创建了三个对象:一个 Point
对象和两个 Rectangle
对象。编译此程序需要所有三个源文件。
public class CreateObjectDemo {
public static void main(String[] args) {
// Declare and create a point object and two rectangle objects.
Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);
// display rectOne's width, height, and area
System.out.println("Width of rectOne: " + rectOne.width);
System.out.println("Height of rectOne: " + rectOne.height);
System.out.println("Area of rectOne: " + rectOne.getArea());
// set rectTwo's position
rectTwo.origin = originOne;
// display rectTwo's position
System.out.println("X Position of rectTwo: " + rectTwo.origin.x);
System.out.println("Y Position of rectTwo: " + rectTwo.origin.y);
// move rectTwo and display its new position
rectTwo.move(40, 72);
System.out.println("X Position of rectTwo: " + rectTwo.origin.x);
System.out.println("Y Position of rectTwo: " + rectTwo.origin.y);
}
}
这是 Point
类
public class Point {
public int x = 0;
public int y = 0;
// a constructor!
public Point(int a, int b) {
x = a;
y = b;
}
}
以及 Rectangle
类
public class Rectangle {
public int width = 0;
public int height = 0;
public Point origin;
// four constructors
public Rectangle() {
origin = new Point(0, 0);
}
public Rectangle(Point p) {
origin = p;
}
public Rectangle(int w, int h) {
origin = new Point(0, 0);
width = w;
height = h;
}
public Rectangle(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
// a method for moving the rectangle
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
// a method for computing the area of the rectangle
public int getArea() {
return width * height;
}
}
此程序创建、操作和显示有关各种对象的信息。以下是输出
Width of rectOne: 100
Height of rectOne: 200
Area of rectOne: 20000
X Position of rectTwo: 23
Y Position of rectTwo: 94
X Position of rectTwo: 40
Y Position of rectTwo: 72
以下三个部分使用上述示例来描述对象在程序中的生命周期。从这些部分,您将学习如何编写代码来创建和使用您自己程序中的对象。您还将学习系统如何在对象的生命周期结束后清理对象。
创建对象
如您所知,类为对象提供了蓝图;您可以从类中创建对象。以下每个从 CreateObjectDemo
程序中获取的语句都会创建一个对象并将其分配给一个变量
Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);
第一行创建了一个 Point
类的对象,第二行和第三行分别创建了一个 Rectangle
类的对象。
每个语句都有三个部分(将在下面详细讨论)
- 声明:以粗体显示的代码都是变量声明,将变量名与对象类型关联起来。
- 实例化:
new
关键字是 Java 运算符,用于创建对象。 - 初始化:
new
运算符后跟对构造函数的调用,该构造函数初始化新对象。
声明一个变量来引用对象
之前您已经了解到,要声明一个变量,您需要编写
type name;
这会通知编译器您将使用 name 来引用类型为 type 的数据。对于原始变量,此声明还会为变量保留适当的内存量。
您也可以在单独的行上声明一个引用变量。例如
Point originOne;
如果您像这样声明 originOne
,它的值将不确定,直到实际创建对象并将其分配给它。仅仅声明一个引用变量并不会创建对象。为此,您需要使用 new
运算符,如下一节所述。您必须在代码中使用 originOne
之前将对象分配给它。否则,您将收到编译器错误。
处于这种状态的变量目前没有引用任何对象。
实例化类
new
运算符通过为新对象分配内存并返回对该内存的引用来实例化类。new
运算符还会调用对象构造函数。
注意:短语“实例化类”与“创建对象”含义相同。当您创建对象时,您是在创建类的“实例”,因此是“实例化”类。
new
运算符需要一个后缀参数:对构造函数的调用。构造函数的名称提供了要实例化的类的名称。
new
运算符返回对它创建的对象的引用。此引用通常分配给适当类型的变量,例如
Point originOne = new Point(23, 94);
new
运算符返回的引用不必分配给变量。它也可以直接在表达式中使用。例如
int height = new Rectangle().height;
此语句将在下一节中讨论。
初始化对象
以下是 Point
类的代码
public class Point {
public int x = 0;
public int y = 0;
//constructor
public Point(int a, int b) {
x = a;
y = b;
}
}
此类包含一个构造函数。您可以识别构造函数,因为它的声明使用与类相同的名称,并且没有返回值类型。Point
类中的构造函数接受两个整型参数,如代码 (int a, int b)
所声明。以下语句为这些参数提供 23 和 94 作为值
Point originOne = new Point(23, 94);
执行此语句的结果可以在下一张图中说明
以下是 Rectangle
类的代码,它包含四个构造函数
public class Rectangle {
public int width = 0;
public int height = 0;
public Point origin;
// four constructors
public Rectangle() {
origin = new Point(0, 0);
}
public Rectangle(Point p) {
origin = p;
}
public Rectangle(int w, int h) {
origin = new Point(0, 0);
width = w;
height = h;
}
public Rectangle(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
// a method for moving the rectangle
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
// a method for computing the area of the rectangle
public int getArea() {
return width * height;
}
}
每个构造函数都允许您使用原始类型和引用类型为矩形的 origin
、width
和 height
提供初始值。如果类具有多个构造函数,则它们必须具有不同的签名。Java 编译器根据参数的数量和类型来区分构造函数。当 Java 编译器遇到以下代码时,它知道要调用 Rectangle
类中需要一个 Point
参数,后跟两个整型参数的构造函数
Rectangle rectOne = new Rectangle(originOne, 100, 200);
这会调用 Rectangle
的一个构造函数,该构造函数将 origin 初始化为 originOne
。此外,构造函数将 width 设置为 100,将 height 设置为 200。现在有两个对同一个 Point
对象的引用——一个对象可以有多个对它的引用,如下一张图所示
以下代码行调用了 Rectangle
构造函数,该构造函数需要两个整型参数,它们为 width
和 height
提供初始值。如果您检查构造函数中的代码,您会看到它会创建一个新的 Point
对象,其 x
和 y
值初始化为 0
Rectangle rectTwo = new Rectangle(50, 100);
以下语句中使用的 Rectangle
构造函数不接受任何参数,因此被称为无参数构造函数
Rectangle rect = new Rectangle();
所有类至少有一个构造函数。如果类没有显式声明任何构造函数,Java 编译器会自动提供一个无参数构造函数,称为默认构造函数。此默认构造函数会调用类父类的无参数构造函数,或者如果类没有其他父类,则调用 Object
构造函数。如果父类没有构造函数(Object
有一个),编译器会拒绝该程序。
使用对象
创建对象后,您可能希望将其用于某些目的。您可能需要使用其某个字段的值、更改其某个字段,或者调用其某个方法来执行操作。
引用对象的字段
对象字段通过其名称进行访问。您必须使用一个明确的名称。
您可以在其自身类中为字段使用一个简单的名称。例如,我们可以在 Rectangle
类中添加一个语句来打印 width
和 height
System.out.println("Width and height are: " + width + ", " + height);
在这种情况下,width
和 height
是简单的名称。
位于对象类之外的代码必须使用对象引用或表达式,后跟点 (.
) 运算符,后跟一个简单的字段名称,例如
objectReference.fieldName
例如,CreateObjectDemo
类中的代码位于 Rectangle
类代码之外。因此,要引用名为 rectOne
的 Rectangle
对象中的 origin
、width
和 height
字段,CreateObjectDemo
类必须分别使用名称 rectOne.origin
、rectOne.width
和 rectOne.height
。该程序使用其中两个名称来显示 rectOne
的 width
和 height
System.out.println("Width of rectOne: " + rectOne.width);
System.out.println("Height of rectOne: " + rectOne.height);
尝试从 CreateObjectDemo
类中的代码使用简单的名称 width
和 height
没有意义——这些字段只存在于对象中——会导致编译器错误。
稍后,该程序使用类似的代码来显示有关 rectTwo
的信息。相同类型的对象具有相同实例字段的副本。因此,每个 Rectangle
对象都有名为 origin
、width
和 height
的字段。当您通过对象引用访问实例字段时,您会引用该特定对象的字段。CreateObjectDemo
程序中的两个对象 rectOne
和 rectTwo
具有不同的 origin
、width
和 height
字段。
要访问字段,可以使用对对象的命名引用(如前面的示例所示),也可以使用任何返回对象引用的表达式。回想一下,new
运算符返回对对象的引用。因此,您可以使用从 new 返回的值来访问新对象的字段
int height = new Rectangle().height;
此语句创建一个新的 Rectangle
对象并立即获取其 height
。本质上,该语句计算 Rectangle
的默认高度。请注意,在执行此语句后,程序不再拥有对创建的 Rectangle
的引用,因为程序从未将引用存储在任何地方。该对象没有引用,其资源可以由 Java 虚拟机回收。
调用对象的方法
您还可以使用对象引用来调用对象的方法。您将方法的简单名称附加到对象引用,并在两者之间使用点运算符 (.
)。此外,您还将在封闭括号内提供方法的任何参数。如果方法不需要任何参数,请使用空括号。
objectReference.methodName(argumentList);
或
objectReference.methodName();
Rectangle
类有两个方法:getArea()
用于计算矩形的面积,move()
用于更改矩形的原点。以下是调用这两个方法的 CreateObjectDemo
代码
System.out.println("Area of rectOne: " + rectOne.getArea());
...
rectTwo.move(40, 72);
第一条语句调用 rectOne
的 getArea()
方法并显示结果。第二行移动 rectTwo
,因为 move()
方法将新值分配给对象的 origin.x
和 origin.y
。
与实例字段一样,objectReference
必须是对对象的引用。您可以使用变量名,但也可以使用任何返回对象引用的表达式。new
运算符返回对象引用,因此您可以使用从 new
返回的值来调用新对象的方法
new Rectangle(100, 50).getArea()
表达式 new Rectangle(100, 50)
返回一个对象引用,该引用引用一个 Rectangle
对象。如所示,您可以使用点表示法来调用新的 Rectangle
的 getArea()
方法来计算新矩形的面积。
某些方法(例如 getArea()
)会返回值。对于返回值的方法,您可以在表达式中使用方法调用。您可以将返回值分配给变量,使用它来进行决策或控制循环。此代码将 getArea()
返回的值分配给变量 areaOfRectangle
int areaOfRectangle = new Rectangle(100, 50).getArea();
在这种情况下,getArea()
被调用的对象是构造函数返回的矩形。
垃圾收集器
一些面向对象的语言要求您跟踪创建的所有对象,并在不再需要它们时显式销毁它们。显式管理内存既乏味又容易出错。Java 平台允许您创建任意数量的对象(当然,受系统处理能力的限制),并且您不必担心销毁它们。Java 运行时环境在确定对象不再使用时会删除它们。此过程称为垃圾回收。
当不再有对该对象的引用时,该对象有资格进行垃圾回收。当变量超出范围时,通常会删除保存在变量中的引用。或者,您可以通过将变量设置为特殊值 null
来显式删除对象引用。请记住,程序可以对同一对象有多个引用;必须删除对对象的所有引用,才能使该对象有资格进行垃圾回收。
Java 运行时环境有一个垃圾收集器,它会定期释放不再引用的对象使用的内存。垃圾收集器在确定时机合适时会自动执行其工作。
上次更新: 2024 年 1 月 5 日