Object类是Java类库中的一个特殊的类,它是类库中所有类的父类,也是用户自定义的所有类的直接或者间接父类。也就是说,用户定义的所有类在默认情况下都是Object类的子类,无论用户是否显示声明Object类作为自己定义的类的父类,这是系统自动完成的。因此,通常把Object类称为通用超类。
Object类中的成员恰好都是方法,其中有7 个public 类型的,两个protected 类型的。用户自定义的所有类都将继承这些方法。Object 类中的7 个public 类型的方法分别是toString( )方法、equals( )方法、getClass( )方法、hashCode( )方法、notify( )方法、notifyAll( )方法和wait( )方法,两个protected 类型的方法是clone( )方法和finalize( )方法。
在Object类中,getClass( )方法用于返回一个Class类型的对象,以识别出当前对象的类;hashCode( )方法用于计算一个对象的散列编码值并把结果作为int 类型值返回;clone( )方法用于对象的克隆;finalize( )方法用于销毁对象时的清理工作;而notify( )方法、notifyAll( )方法和wait( )方法是用于多线程程序中当前对象的处理。有关多线程的应用,第9章将详细阐述。
另外,值得注意的是:getClass( )方法、notify( )方法、notifyAll( )方法和wait( )方法在Object类中已经定义成为final 类型,即这4 个方法都是最终方法,所以在用户自己定义的类中不能重写这4个方法。
Object 类的toString( )方法返回描述当前对象的文本化String数据。默认情况下,该对象的表示形式是:类名后面跟一个“@”和该对象Hash码的十六进制表示。当然,用户可以在自己的类中重写该方法。toString( )方法会在使用“+”号把一个对象与String类型的变量连接的时候自动调用。
【例4-9】toString( )方法示例。
//*********** TesttoString.java**********
//测试Object类的toString()方法
import java.io.*;
class Father
{
protected String name;
Father( ) {
name = "Noname";
}
Father(String s){
name = s;
}
public void setName(String s){
name = s;
}
public String getName( ){
return name;
}
}// Father类结束
class Child extends Father
{
Child( ){
super( ); //调用Father类的无参构造方法
name = "Noname"; //有super( )的调用,这行代码可以去掉
}
Child(String s) {
super(s); //调用Father类的有参构造方法
name = s; //有super(s)的调用,这行代码可以去掉
}
public String toString( )
{
return "This is child,and his name is " + name;
}
}// Child类结束
//主类
public class TesttoString
{
public static void main(String args[ ])
{
Father f = new Father("John");
System.out.println(f);
//System.out.println(f.toString( ));
Child c = new Child("Joe");
System.out.println(c);
//System.out.println(c.toString( ));同上一行的执行效果一样
System.out.println(" ");
}
}// TesttoString类结束
本例中定义了3个类:Father 类、Child 类和TesttoString 类。其中,Child类是Father类的子类,TesttoString类是程序的主类。TesttoString 类中创建了Father类的一个对象f和Child类的一个对象c。因为用户定义的所有类都是系统类Object的子类,所以Father类也是Object类的子类。因为Father 类中没有对Object类的toString( )方法进行重写,所以输出的结果为Father 类的类名跟一个“@”和该类对象Hash 码的十六进制表示,即图4-5中的Father@1f68ee3。而Child 类中对Object类的toString()方法进行了重新定义,所以输出了“This is child,and his name is Joe”的结果。因为输出某个对象时系统会自动调用该对象的toString( )方法,第43行System.out.println(f)的输出结果就是执行了f对象的toString( )方法,所以第43行和第44行的执行效果是一样的。
图4-5 to String( )方法示例的程序运行结果
Object 类的equals( )方法用于比较以参数传递过来的对象和当前对象,如果它们是相同的对象(不仅是相等,而且必须是同一个对象),则返回true;如果它们是不同的对象,即使两者的数据成员相等,也将返回false。
例4-10测试了Object类中的equals( )方法。
【例4-10】equals( )方法示例。
//测试Object类的equals()方法
import java.io.*;
class Cylinder
{
// 常量
public static final double PI = 3.14;
// 成员变量
private double height; //圆柱体的高
private double radius; //圆柱体的半径
Cylinder( )//无参数构造方法
{
height = 0.0;
radius = 0.0;
}
Cylinder(double r,double h) //有参数构造方法
{
height = r;
radius = h;
}
public void setRadius(double r) //设置圆柱体的半径
{
radius = r;
}
public void setHeight(double h) //设置圆柱体的高
{
height = h;
}
public double getRadius( ) //获取圆柱体的半径
{
return radius;
}
public double getHeight( ) //获取圆柱体的高
{
return height;
}
public double surfaceArea( ) //计算表面积
{
return 2*PI*radius*height+2*PI*radius*radius;
}
public double volume( ) //计算体积
{
return PI*radius*radius*height;
}
}// Cylinder类结束
//主类
public class TestEqual
{
public static void main(String args[ ])
{
//创建Cylinder类的对象c1,c2,c3,c4,c5
Cylinder c1 = new Cylinder( );
Cylinder c2 = new Cylinder( );
Cylinder c3 = new Cylinder( 5.0,6.0);
Cylinder c4 = new Cylinder( 5.0,6.0);
Cylinder c5 =null;
c5 = c4;
// 测试equals()方法
if (c1.equals(c2))
System.out.println("c1和c2是相同的对象");
else
System.out.println("c1和c2是不同的对象");
System.out.println(" ");
// 测试equals()方法
if (c3.equals(c4))
System.out.println("c3和c4是相同的对象");
else
System.out.println("c3和c4是不同的对象");
System.out.println(" ");
// 测试equals()方法
if (c5.equals(c4))
System.out.println("c5和c4是相同的对象");
else
System.out.println("c5和c4是不同的对象");
System.out.println(" ");
}
}// TestEqual类结束
本例的执行结果如图4-6所示。该例中定义了一个圆柱体(Cylinder)类和一个TestEqual 类。在主类中创建了Cylinder类的5个对象:c1、c2、c3、c4和c5。虽然Cylinder类的对象c1和c2是使用同样的创建方法产生的,它们有相等的成员变量,但是c1和c2仍然是不同的对象,因为它们引用的是两块不同的内存。同理,c3和c4也是不同的对象。但c4和c5是同一个对象,因为执行语句“c5 = c4;”后,c4和c5引用的是同一块内存。
图4-6 equals( )方法示例的程序运行结果
本例中Cylinder类没有自己定义equals( )方法,所以Cylinder类的equals( )方法是继承Object类的equals( )方法。用户可以根据程序的需要在自己定义的类中重新定义equals( )方法,以取代Object类的equals( )方法。
String 类是系统定义的字符串类,该类已经重写了Object类的equals( )方法。下面的例4-11说明了String类的equals( )方法和运算符“==”的区别。
【例4-11】判断字符串是否相等。
//**********TestStringEqual.java**********
//比较String类的equals( )方法和运算符“==”的区别
import java.awt.Graphics;
import java.applet.Applet;
public class TestStringEqual extends Applet
{
//String类的对象
String s1,s2,s3,s4;
public void init()//初始化
{
s1 = new String("hello");
s2 = new String("hello");
s3 = "hello";
s4 = "hello";
}
public void paint(Graphics g)//重绘
{
g.drawString("s1 = " +s1, 25, 30);
g.drawString("s2 = " +s2, 25, 50);
g.drawString("s3 = " +s3, 25, 70);
g.drawString("s4 = " +s4, 25, 90);
// 测试equals()方法
If (s1.equals("hello"))
g.drawString("s1 equals \"hello\"", 25 , 140);
else
g.drawString("s1 does not equals \"hello\"", 25 , 140);
// 测试equals()方法
if (s1.equals(s2))
g.drawString("s1 equals s2", 25 , 160);
else
g.drawString("s1 does not equals s2", 25 , 160);
// 测试==
if (s1 == "hello")
g.drawString("s1 == \"hello\"", 25 , 180);
else
g.drawString("s1 != \"hello\"", 25 , 180);
// 测试==
if (s1 == s2)
g.drawString("s1 == s2 ,s1 and s2 point to the same string",25 , 200);
else
g.drawString("s1 != s2 ,s1 and s2 do not point to the same string",
25 , 200);
// 测试==
if (s1 == s3)
g.drawString("s1 == s3 ,s1 and s3 point to the same string", 25 ,
220);
else
g.drawString("s1 != s3 ,s1 and s3 do not point to the same string",
25 , 220);
// 测试==
if (s3 == s4)
g.drawString("s3 == s4 ,s3 and s4 point to the same string", 25 ,
240);
else
g.drawString("s3 != s4 ,s3 and s4 do not point to the same string",
25 , 240);
}
}// TestStringEqual类结束
程序的执行结果如图4-7所示。
图4-7 判断字符串是否相等的程序运行结果
本例程序是一个Java Applet 程序,它定义了一个TestStringEqual类。因为系统String类的equals( )方法完全改写了Object 类的equals( )方法,所以String类的两个对象s1和s2在用equals( )方法进行是否相等的判断时的意义就完全不同了,String类的equals( )方法是用于比较两个对象的内容是否相等的。所以,本例的结果是:s1 equals s2。当然,s1和匿名对象"hello"的比较结果也是:s1 equals"hello"。
与String 类的equals( )方法不同,运算符“==”在对象的比较上不是比较两个对象的内容是否相等,而是比较两个对象的引用是否相等,即在内存中是否引用同一块地址。值得注意的是,Java语言将内容相同的所有匿名String对象作为有许多引用的同一个匿名String对象,本例中s3和s4都是使用了匿名字符串"hello"创建的,所以判断的结果是:s3 == s4,s3 and s4 point to the same string。
存在继承关系的父类对象和子类对象之间也可以在一定条件下相互转换。父类对象和子类对象的转换需要注意如下原则。
(1)子类对象可以被视为其父类的一个对象。
(2)父类对象不能被当作其某一个子类的对象。
(3)如果一个方法的形式参数定义的是父类对象,那么调用这个方法时,可以使用子类对象作为实际参数。
(4)如果父类对象引用指向的实际是一个子类对象(在之前的某个时候根据这一点把子类对象的引用赋值给这个父类对象的引用),那么这个父类对象的引用可以用强制类型转换转换成子类对象的引用。
class superclass // 定义父类
{
int x ;
}
class subclass extends superclass //定义子类
{
int y;
char ch;
…
}
public class testclass // 使用父类与子类
{
superclass sp, sp_ref ;
superclass sb,sb_ref ;
sp = new superclass( ) ;
sb =new subclass( );
sp_ref =sb; // 父类引用可以指向子类对象
sb_ref=(subclass)sp_ref ; //父类引用转换成子类引用
}