学习目标:学习对象的创建、对象的访问、对象的引用、对象的比较和销毁。
对象的创建
类是抽象的概念集合,表示的是一个共性的产物,类中定义的是属性和方法,而对象是类的实例。
例如前面课程中的水果被归纳为类,而苹果、香蕉、葡萄为水果类的实例或对象。水果是人们赋予具有苹果、香蕉、葡萄等共同特点的名称,不单指某一事物;对象是指具体的实物或概念,如苹果、香蕉、葡萄等对象是实物,而一项政策可能就是一个概念性的对象了,在现实生活中,万事万物皆对象,面向对象编程就是模拟现实生活中的一个个对象来编程的。
类也可以看做是对象的模板,它描述一类对象的行为和状态,决定着对象的属性和方法。由对象可以抽象出类,类也可以实例化成对象,就像水果类决定了苹果、香蕉、葡萄等对象具备糖分、汁液、芳香度都基本特征,也可以通过抽取香蕉、葡萄等对象的共同特征抽象为水果类。下图给出了水果类与水果对象的关系。

由苹果、葡萄等对象抽象出水果类,水果类属性有water(汁液含量)、sugar(糖分含量)、fragrance(芳香度),这些属性是水果类所具有的共同特点。当在程序中需要使用苹果对象时,将水果类实例化苹果,同时初始化苹果对象的water(汁液含量)、sugar(糖分含量)、fragrance(芳香度)属性。
创建对象
对象是根据类来创建的。在Java语言中,可以通过new运算符、反射机制和序列化来创建对象,最常用的是通过new运算符来创建对象。本课主要讲述通过new运算符来创建对象,使用反射机制和序列化来创建对象的内容,将在Java 核心技术课程中讲述。
使用new运算符创建对象语法如下:
类名 对象名 = new 类名();
案例5:创建FruitsObj类。
在PUnit5项目unit包下创建Java类FruitsObj。代码如下:
package unit;
public class FruitsObj {
//水果名称
private String name;
//水果类汁液含量属性
private String water;
//水果类糖分含量属性
private String sugar;
//水果类芳香度属性
private String fragrance;
//水果类的构造方法
public FruitsObj(String name,String water,String sugar,String fragrance)
{
System.out.println("正在实例化" + name);
this.name = name;
this.water = water;
this.sugar = sugar;
this.fragrance = fragrance;
}
/**
* Function showFruit
* Description: 输出水果的水分含量、糖分含量、芳香度
* input: 无输入参数
* return: 无返回
*/
public void showFruit()
{
System.out.printf("----%s的属性----\n", getName());
System.out.println("水分含量:" + getWater());
System.out.println("糖分含量:" + getSugar());
System.out.println("芳香度:" + getFragrance());
}
// 水果类的开花行为
public void flower()
{
System.out.println("开花");
}
// 水果类的成熟行为
public void ripe()
{
System.out.println("成熟");
}
// 水果类的落果行为
public void drop ()
{
System.out.println("落果");
}
/**
* @return the water
*/
public String getWater() {
return water;
}
/**
* @param water the water to set
*/
public void setWater(String water) {
this.water = water;
}
/**
* @return the sugar
*/
public String getSugar() {
return sugar;
}
/**
* @param sugar the sugar to set
*/
public void setSugar(String sugar) {
this.sugar = sugar;
}
/**
* @return the fragrance
*/
public String getFragrance() {
return fragrance;
}
/**
* @param fragrance the fragrance to set
*/
public void setFragrance(String fragrance) {
this.fragrance = fragrance;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
FruitsObj类是水果类。它有四个成员变量,分别是name、water、sugar、fragrance,并提供了成员变量的get和set方法。它提供了一个显示构造方法,该构造方法用传入的参数来初始化类的成员变量。showFruit()是Fruits类的成员方法,用于输出类成员变量的信息。
在Punit5项目src目录下创建manager包,在包manager下创建Java类FruitsManager。代码如下:
package manager;
import unit.FruitsObj;
public class FruitsManager {
public static void main(String[] args) {
// 实例化Fruits类,创建一个apple对象
FruitsObj apple = new FruitsObj("苹果","80%", "60%", "30%");
// 调用apple对象的showFruit方法
apple.showFruit();
// 实例化Fruits类,创建一个peach对象
FruitsObj peach = new FruitsObj("桃","60%", "30%", "50%");
// 调用peach对象的showFruit方法
peach.showFruit();
}
}
FruitsManager类是程序的主类,在类中提供了main()方法。程序执行入口从main()方法开始。
在main()方法内部使用new运算符创建了两个Fruits类对象,一个对象是apple(苹果),一个对象是peach(桃),最后调用对象的showFruit()方法,输出对象的成员变量信息。因为FruitsManager类和Fruits类不是同一个包,因此FruitsManager类需要使用import语句导入Fruits类。
在Fruits类实例化对象的过程中,Java虚拟机会为apple和peach对象分配内存,同时也为apple和peach对象内的成员变量和成员方法分配存储空间。apple和peach对象是相互独立的,在内存中占据独立的内存地址,并且每个对象都有自己的生命周期,当一个对象的生命周期结束时,对象变成了垃圾,由Java 虚拟机自带的垃圾回收机制进行处理,该对象就不能再使用了。
访问对象的属性和方法
对象被创建后,对象可以通过“.”操作符来访问对象的成员变量和成员方法。例如:
Fruits apple = new Fruits("80%”,“60%”,”30%”);
apple. showFruit();
在上面的代码中,apple对象通过“.”操作符调用apple对象的showFruit()方法。同样的道理,apple对象也可以通过“.”操作符来访问对象的成员变量,在这种情况下,应确认对象所在的类具有访问对象成员变量的权限,否则只能调用成员变量的get和set方法来访问成员变量。
案例6:创建一个Rectangle类,定义成员变量widht(宽度)和height(高度),定义一个getArea()方法。
在PUnit5项目unit包下创建Java类Rectangle。代码如下:
package unit;
public class Rectangle {
// 成员变量width
public int width;
// 成员变量height
public int height;
// 计算面积的成员方法
public int getArea()
{
return width * height;
}
}
Rectangle类矩形类。它有两个成员变量width和height,被修饰为public权限。成员方法getArea()用于计算矩形的面积并返回计算结果。
在PUnit5项目manager包下,创建Java类RectangleTest。代码如下:
package manager;
import unit.Rectangle;
public class RectangleTest {
public static void main(String[] args) {
// 创建Rectangle对象
Rectangle rect = new Rectangle();
// rect对象的成员变量赋值
rect.width = 30;
rect.height = 20;
// 计算矩形的面积
int nArea = rect.getArea();
System.out.print("矩形的面积为:" + nArea);
}
}
在RectangleTest类的main()方法内,首先实例化Rectangle对象,实例化的对象名称为rect,然后使用“.”操作符对成员变量width和height赋值。因为成员变量width和height的访问权限为public,因此rect对象所在的类具有直接访问rect对象成员变量的权限。最后使用“.”操作符调用getArea()方法计算机圆的面积。
对象的引用
类在通过new运算符实例化对象的过程中,Java虚拟机会为对象在堆(系统存储区域,用于存储应用程序创建的对象)中分配内存,并返回存储对象的内存地址,对象的内存地址就被称为对象的引用。
例如:
public static void main(String[] args) {
// 创建Rectangle对象
Rectangle rect = new Rectangle();
// rect对象的成员变量赋值
rect.width = 30;
rect.height = 20;
// 计算矩形的面积
int nArea = rect.getArea();
System.out.print("矩形的面积为:" + nArea);
}
上面的代码使用new运算符创建了rect对象,此时rect存储的不是Rectangle类的实例化对象,而是对象在内存中的地址。
Rectangle rect = new Rectangle();
上面的语句可以分成两条语句来看:
Rectangle rect;
rect = new Rectangle();
第1条语句声明了一个Rectangle类型的变量rect,它存放在栈空间(用来存储局部变量的存储空间),此时rect不指向任何内存地址,它的值是null。
第2条语句由new运算符创建了一个Rectangle对象,并将创建的Rectangle对象放在堆中,然后将Rectangle对象在堆中的地址赋值给rect变量,此时rect变量存储的就是Rectangle对象在堆中的地址。
既然rect变量存储的Rectangle对象在堆中的地址,自然可以使用rect来访问对象的成员变量和成员方法了。
一个对象的引用可以理解为指向一个具体的、已经创建的对象,如果没有指向任何对象,在该引用为null。一个对象的引用不能同时指向多个对象,这一点也容易理解,为某一对象分配的内存只能存储一个对象,这段内存的地址也是唯一的。
多个对象引用变量可以指向同一个对象,看下面的代码:
Rectangle rect;
rect = new Rectangle();
Rectangle rect1 = rect;
rect1也是Rectangle类型的变量,rect把对象的引用赋值给rect1,此时rect和rect1指向同一个Rectangle对象。
对象的比较
在实际编程中,有时需要比较两个对象是否相等,如比较两个字符串对象。
比较两个对象是否相等,可以使用“==”运算符和对象的equals()方法。这两种方法在比较方式上是不同的。“==”运算符比较的两个对象的内存地址,equals()方法比较的是两个对象的内容。
String对象的比较在前面的课程已经讲过了,本课主要讲述自定义类的比较。
“==”运算符的比较。
“==”运算符主要是比较两个对象的内存地址是否相同。前面讲过实例化的对象变量存储的是对象的引用,使用“==”运算符可以比较两个对象变量存储的对象引用是否相同,如果这两个对象变量存储的对象引用相同,则说明它们指向同一个存储在堆中的对象,否则指向存储在堆中两个不同的对象。
使用equals()方法比较
equals()方法用于判断两个对象的内容是否相等。前面课程判断String对象的内容是否相等时,就使用了equals()方法。
equals()方法是Object类提供的方法,Object类是Java所有类(包括自定义类)的父类,所有的类都继承于Object类(关于类的继承内容将在后面的课程讲述)。String类重写了Object类equals()方法,所以可以使用equals()方法来判断两个字符串对象的内容是否相等。
要实现两个自定义类实例化对象的内容比较,就需要在自定义类中重写Object类equals()方法,在equals()方法内判断两个对象的成员变量内容是否相等,如果这两个对象的成员变量内容相等,可以认为这两个对象的内容是相等的。
例如:
public class ExtendRectangle {
// 成员变量width
public int width;
// 成员变量height
public int height;
// 构造方法
public ExtendRectangle(int width,int height)
{
this.width = width;
this.height = height;
}
// 计算面积的成员方法
public int getArea() {
return width * height;
}
@Override
public boolean equals(Object obj) {
// 比较两个对象的引用是否相等
if( this == obj )
return true;
// 判断obj是否是ExtendRectangle类的实例
if( !(obj instanceof ExtendRectangle) )
return false;
// 声明boolean变量,标记对象内容是否相等
boolean bEqual = true;
// obj强制转换为ExtendRectangle类型
ExtendRectangle rect = (ExtendRectangle)obj;
// 比较成员变量的值是否相等
if( this.width != rect.width )
bEqual = false;
if( this.height != rect.height )
bEqual = false;
return bEqual;
}
}
在equals()方法内,首先判断传入的对象引用obj和当前对象的引用是否相等,如果两个对象的引用相等,说明这两个对象是同一个对象,可以直接返回true。
instanceof是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例。如果测试对象是类的实例返回true,否则返回false。代码使用instanceof判断obj是否是ExtendRectangle的实例对象,主要是防止传入的obj不是一个合法的ExtendRectangle类的实例化对象。
传入的obj是Object类型,需要将obj强制转换为ExtendRectangle类型,才能访问ExtendRectangle类实例化对象的成员变量。
代码最后判断当前对象与obj对象的成员变量的值是否相等,如果不相等将局部变量bEqual设置为false。
对象的销毁
类在实例化的过程中,Java虚拟机会为实例化的对象分配内存,同时也为对象内的成员变量和成员方法分配存储空间。实例化的每个对象都是相互独立的,在内存中占据独立的内存地址,并且每个对象都有自己的生命周期,当一个对象的生命周期结束时,对象变成了垃圾,由Java 虚拟机自带的垃圾回收机制进行处理,该对象就不能再使用了。
Java虚拟机是如何判断对象可以被回收呢?
对象的生命周期与对象的有效范围有关。在《变量的有效范围》一课中谈到了变量分为全局变量和局部变量:类的成员变量都是全局变量;在类方法内部声明的变量是局部变量。全局变量的有效范围为整个类,局部变量的有效范围为类方法内部。
如果在类方法内部创建的实例化对象,当这个类方法执行完成后,将会被回收。因为类方法执行完成后,类方法执行时分配的栈空间(用来存储局部变量的存储空间)被清理,方法内部的实例化对象已经失去了有效范围。
例如下面的代码,当方法执行完成后,String对象s将被Java 虚拟机自动回收。
public void mthod()
{
String s = new String("这是一个方法");
System.out.println(s);
}
还有一种情况就是程序员主动将已实例化的对象设置为null,在这种情况下,虽然实例化的对象可能还在有效范围之内,Java虚拟机也会自动回收这类对象。
public void mthod()
{
String s = new String("这是一个方法");
s = null;
……
}
垃圾对象由Java 虚拟机自动回收。如果程序员希望在垃圾对象回收之前做一些对象的资源清理工作,可以重写Object类的finalize()方法,在finalize()方法内部做对象资源的清理工作。
@Override
protected void finalize() throws Throwable {
// 清理资源的代码
…………
super.finalize();
}
类重写Object类的finalize()方法后,Java 虚拟机会判断对象是否重写了finalize()方法,如果没有重写,则直接将其回收。否则,会在垃圾回收时调用该方法,并且在下一次进行垃圾回收时,再回收对象占有的内存资源。
由于Java虚拟机的垃圾自动回收不受人为控制,具体执行时间也不确定。为此,Java提供了System.gc()方法来强制启动垃圾回收器,让Java虚拟机进行对象的回收工作。