系列中的上一篇
当前教程
创建和使用对象
系列中的下一篇

系列中的上一篇: 调用方法和构造函数

系列中的下一篇: 更多关于类

创建和使用对象

 

了解什么是对象

一个典型的 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 类的对象。

每个语句都有三个部分(将在下面详细讨论)

  1. 声明:以粗体显示的代码都是变量声明,将变量名与对象类型关联起来。
  2. 实例化:new 关键字是 Java 运算符,用于创建对象。
  3. 初始化: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);

执行此语句的结果可以在下一张图中说明

A Point Object

一个 Point 对象

以下是 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;
    }
}

每个构造函数都允许您使用原始类型和引用类型为矩形的 originwidthheight 提供初始值。如果类具有多个构造函数,则它们必须具有不同的签名。Java 编译器根据参数的数量和类型来区分构造函数。当 Java 编译器遇到以下代码时,它知道要调用 Rectangle 类中需要一个 Point 参数,后跟两个整型参数的构造函数

Rectangle rectOne = new Rectangle(originOne, 100, 200);

这会调用 Rectangle 的一个构造函数,该构造函数将 origin 初始化为 originOne。此外,构造函数将 width 设置为 100,将 height 设置为 200。现在有两个对同一个 Point 对象的引用——一个对象可以有多个对它的引用,如下一张图所示

A Rectangle Object

一个 Rectangle 对象

以下代码行调用了 Rectangle 构造函数,该构造函数需要两个整型参数,它们为 widthheight 提供初始值。如果您检查构造函数中的代码,您会看到它会创建一个新的 Point 对象,其 xy 值初始化为 0

Rectangle rectTwo = new Rectangle(50, 100);

以下语句中使用的 Rectangle 构造函数不接受任何参数,因此被称为无参数构造函数

Rectangle rect = new Rectangle();

所有类至少有一个构造函数。如果类没有显式声明任何构造函数,Java 编译器会自动提供一个无参数构造函数,称为默认构造函数。此默认构造函数会调用类父类的无参数构造函数,或者如果类没有其他父类,则调用 Object 构造函数。如果父类没有构造函数(Object 有一个),编译器会拒绝该程序。

 

使用对象

创建对象后,您可能希望将其用于某些目的。您可能需要使用其某个字段的值、更改其某个字段,或者调用其某个方法来执行操作。

引用对象的字段

对象字段通过其名称进行访问。您必须使用一个明确的名称。

您可以在其自身类中为字段使用一个简单的名称。例如,我们可以在 Rectangle 类中添加一个语句来打印 widthheight

System.out.println("Width and height are: " + width + ", " + height);

在这种情况下,widthheight 是简单的名称。

位于对象类之外的代码必须使用对象引用或表达式,后跟点 (.) 运算符,后跟一个简单的字段名称,例如

objectReference.fieldName

例如,CreateObjectDemo 类中的代码位于 Rectangle 类代码之外。因此,要引用名为 rectOneRectangle 对象中的 originwidthheight 字段,CreateObjectDemo 类必须分别使用名称 rectOne.originrectOne.widthrectOne.height。该程序使用其中两个名称来显示 rectOnewidthheight

System.out.println("Width of rectOne: "  + rectOne.width);
System.out.println("Height of rectOne: " + rectOne.height);

尝试从 CreateObjectDemo 类中的代码使用简单的名称 widthheight 没有意义——这些字段只存在于对象中——会导致编译器错误。

稍后,该程序使用类似的代码来显示有关 rectTwo 的信息。相同类型的对象具有相同实例字段的副本。因此,每个 Rectangle 对象都有名为 originwidthheight 的字段。当您通过对象引用访问实例字段时,您会引用该特定对象的字段。CreateObjectDemo 程序中的两个对象 rectOnerectTwo 具有不同的 originwidthheight 字段。

要访问字段,可以使用对对象的命名引用(如前面的示例所示),也可以使用任何返回对象引用的表达式。回想一下,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);

第一条语句调用 rectOnegetArea() 方法并显示结果。第二行移动 rectTwo,因为 move() 方法将新值分配给对象的 origin.xorigin.y

与实例字段一样,objectReference 必须是对对象的引用。您可以使用变量名,但也可以使用任何返回对象引用的表达式。new 运算符返回对象引用,因此您可以使用从 new 返回的值来调用新对象的​​方法

new Rectangle(100, 50).getArea()

表达式 new Rectangle(100, 50) 返回一个对象引用,该引用引用一个 Rectangle 对象。如所示,您可以使用点表示法来调用新的 RectanglegetArea() 方法来计算新矩形的面积。

某些方法(例如 getArea())会返回值。对于返回值的方法,您可以在表达式中使用方法调用。您可以将返回值分配给变量,使用它来进行决策或控制循环。此代码将 getArea() 返回的值分配给变量 areaOfRectangle

int areaOfRectangle = new Rectangle(100, 50).getArea();

在这种情况下,getArea() 被调用的对象是构造函数返回的矩形。

 

垃圾收集器

一些面向对象的语言要求您跟踪创建的所有对象,并在不再需要它们时显式销毁它们。显式管理内存既乏味又容易出错。Java 平台允许您创建任意数量的对象(当然,受系统处理能力的限制),并且您不必担心销毁它们。Java 运行时环境在确定对象不再使用时会删除它们。此过程称为垃圾回收。

当不再有对该对象的引用时,该对象有资格进行垃圾回收。当变量超出范围时,通常会删除保存在变量中的引用。或者,您可以通过将变量设置为特殊值 null 来显式删除对象引用。请记住,程序可以对同一对象有多个引用;必须删除对对象的​​所有引用,才能使该对象有资格进行垃圾回收。

Java 运行时环境有一个垃圾收集器,它会定期释放不再引用的对象使用的内存。垃圾收集器在确定时机合适时会自动执行其工作。


上次更新: 2024 年 1 月 5 日


系列中的上一篇
当前教程
创建和使用对象
系列中的下一篇

系列中的上一篇: 调用方法和构造函数

系列中的下一篇: 更多关于类