1

对象

典型的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);
    }
}

该程序创建、操作和显示有关各种对象的信息,这是输出:

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,否则,你将收到编译器错误。

此状态中的变量(当前不引用任何对象)可以如下所示(变量名称,originOne,以及指向无的引用):

objects-null.gif

实例化一个类

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)声明,以下语句提供2394作为这些参数的值:

Point originOne = new Point(23, 94);

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

objects-oneRef.gif

这是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的一个构造初始化原点originOne,此外,构造函数将width设置为100,将height设置为200,现在有两个对同一个Point对象的引用 — 一个对象可以有多个对它的引用,如下图所示:

objects-multipleRefs.gif

以下代码行调用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对象并立即获得其高度,实质上,该语句计算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运行时环境具有垃圾收集器,可以定期释放不再引用的对象使用的内存,垃圾收集器在确定时间正确时自动完成其工作。


上一篇:类
下一篇:类的更多方面

博弈
2.5k 声望1.5k 粉丝

态度决定一切