public boolean equals(Student s) //子类增加,由子类提供对象比较规则
调用如下:
s1.equals(p1) s2.equals(s1)
//执行equals(Person),由父类提供对象比较规则 //执行equals(Student),由子类提供对象比较规则
由于重载是编译时多态,当父类对象引用子类实例时,运行时仍然执行父类提供的对象比较规则,而非子类对象比较规则。例如,以下运行时执行与希望不符:
Person p1 = new Student(……), p2 = new Student(……); //父类对象引用子类实例 p1.equals(p2)
//编译时多态,执行equals(Person),与希望不符
所以,Student类不需要重载以上两个equals()方法,而应声明equals(Person)覆盖父类方法,实现运行时多态。equals(Person)方法实现可约定子类对象与父类对象是否可比相等。
③ Student类声明equals(Person)方法,与父类实例可比
//子类覆盖父类方法,与父类实例可比,提供Person类型参数按Person规则比较
public boolean equals(Person p) //覆盖父类equals(Person)方法,约定子类对象比较规则 {
boolean b = super.equals(p); //调用父类equals(Person)方法,执行父类对象比较规则 if (b && p instanceof Student) //当父类成员变量对应相等且p引用子类实例时 { Student s = (Student)p; //类型强制转换,s也引用p引用的实例 return this.department.equals(s.department) && this.speciality.equals(s.speciality) && this.number.equals(s.number) && this.member==s.member; } return b; }
调用如下:
Person p1 = new Student(……), p2 = new Student(……); //父类对象引用子类实例 p1.equals(p2)
//运行时多态,执行子类对象比较规则
推而广之,每个类应该覆盖父类的equals()方法,参数是父类对象。Person类应该覆盖
Object类的equals(Object)方法,而Student类也该再覆盖父类的equals(Object)方法。所以,每个类应该覆盖equals(Object)方法,表现运行时多态性。
(3) Person和Student类声明equals(Object)方法,表现出运行时多态性。 ① Person类与本类及其子类对象可比相等,与之外的对象没有可比性。
Person类声明equals(Object)方法如下,它覆盖Object类的equals()方法,不支持与父类Object对象比较相等。
public boolean equals(Object obj) //覆盖Object类的equals()方法 {
if (this==obj) //this指代调用当前方法的对象 return true;
if (obj instanceof Person) //当obj引用实例属于Person及其子类 { Person p = (Person)obj; //类型强制转换,p也引用obj引用的实例 return this.name.equals(p.name) && this.birthday.equals(p.birthday) &&
this.sex.equals(p.sex) && this.province.equals(p.province) && this.city.equals(p.city);
- 31 -
}
return false; }
上述方法没有约定Person类与Person类及其子类以外对象的可比性,以下调用语法正确,但两个对象没有比较成员变量即返回false:
Person p1 = new Person(……), p2 = new Person(……); p1.equals(new Object()) p1.equals(p2)
//参数是Object实例,不比,返回false //参数是Person及其子类,可比
② Student类声明equals(Object)方法,与父类实例不可比。
Student类声明equals(Object)方法如下,没有提供与父类Person实例的比较规则。
public boolean equals(Object obj) //覆盖Person类中方法 {
if (this==obj) //this指代调用当前方法的对象 return true;
if (obj instanceof Student) //当obj引用实例属于Student及其子类 { Student s = (Student)obj; //类型强制转换,s也引用obj引用的实例 return this.department.equals(s.department) && this.speciality.equals(s.speciality) && this.number.equals(s.number) && this.member==s.member; }
return false; }
调用语句如下:
Person p1 = new Person(……), p2 = new Student(……), p3 = new Student(……);
p1.equals(p2) //编译时多态,执行Person的equals(Object),参数是Person的子类对象 p2.equals(p1) //运行时多态,false 执行Student的equals(Object),参数是Person类对象,不比,p2.equals(p3) //运行时多态,执行Student的equals(Object),参数是Student类对象,可比
③ Student类声明equals(Object)方法,与父类实例可比。
Student类声明equals(Object)方法,根据需要也可实现如下,与父类实例可比。
public boolean equals(Object obj) //覆盖父类方法,约定子类对象比较规则,与父类实例可比 {
boolean b = super.equals(p); //调用Person类equals(Object)方法,执行父类对象比较规则 if (b && p instanceof Student) //当父类成员变量对应相等且p引用子类实例时 { Student s = (Student)p;
//类型强制转换,s也引用p引用的实例
return this.department.equals(s.department) && this.speciality.equals(s.speciality) && this.number.equals(s.number) && this.member==s.member; } return b; }
调用语句如下:
Person p1 = new Person(\李小明\Person p2 = new Student(p1, ……);
- 32 -
p2.equals(p1) //子类对象与父类对象可比,true
【习3.6】 在对象数组中查找对象。
例3.6思考题,在对象数组中查找对象的search()方法声明如下:
//顺序查找对象数组中首次出现的与key相等元素,若查找成功返回元素,否则返回null public static Object search(Object value[], Object key) {
if (value!=null && key!=null) for(int i=0; i if (key.equals(value[i])) //对象采用equals()方法比较是否相等,运行时多态 return value[i]; return null; //查找不成功 } 【问题】参数key已经引用一个实例,为什么还要找它?key引用的实例与最终找到的实例有什么不同? 【答】search()方法的查找结果取决于对象的equals(Object)方法比较相等结果。例如,前述Person和Student类的equals(Object)方法实现为比较所有成员变量的取值,则search()方法查找的结果对象与key参数引用实例取值相等,即没有必要执行search()方法。 但是,如果Person类声明equals()方法如下,只比较姓名作为对象相等依据,并且Student类继承它: public boolean equals(Object obj) //只比较姓名作为对象相等依据 { return this==obj || obj!=null && obj instanceof Person && this.name.equals(((Person)obj).name); } 则以下两个Person对象相等: new Person(\张小莉\ new Student(\张小莉\电子信息\ 因此,search(Object value[], Object key)方法用于在value对象数组中查找key,通常key只提供作为查找依据的关键字数据项,且由key的equals(Object)方法提供对象比较相等的规则。 3.5 类的抽象性 1. 抽象类 3-12 什么是抽象类?在什么情况下需要设计抽象类?抽象类中是否必须有抽象方法? 【答】使用关键字abstract声明的类称为抽象类,抽象类通常包含抽象方法,抽象方法是只有方法声明而没有方法体的成员方法,抽象类不能被实例化。 3-13 以下声明有什么错误? public abstract class ClosedFigure { protected abstract ClosedFigure() - 33 - } 【答】编译错,构造方法不能被声明为抽象方法。 3-14 以下Shape类声明有什么错误? public abstract class ClosedFigure { public ClosedFigure() { } public void print() { } public void finalize() { } } 【答】没有错误,抽象类也可以不包含抽象方法。 3-15 设ClosedFigure是抽象类,以下声明有什么错误? ClosedFigure fig = new ClosedFigure(); 【答】编译错,不能创建抽象类的实例。 3-16 如果ClosedFigure类中的area()方法声明如下,会对ClosedFigure的子类有何影响? public class ClosedFigure { public double area() { return 0; } } 【答】此处ClosedFigure类的area()方法没有意义。因为ClosedFigure的子类不是必须覆盖area()方法,如果没有覆盖,无论是什么对象,则图形面积总为0.0。 将area()方法声明为抽象方法,在编译时Java会自动检查子类是否覆盖了抽象方法。如果一个方法需要被子类覆盖,则必须声明为抽象方法。 2. 最终类 3-17 什么是最终类?在什么情况下需要设计最终类?最终类中是否必须有最终方法? 【答】使用关键字final声明的类称为最终类,最终类不能被继承。 实验3 类的封装、继承和多态 增加实验题如下。 3-18 声明复数类如下,复数语法图见教材图3.15所示。 public class Complex { public double real,imag; //实部,虚部 public Complex(double real, double imag) //构造方法 public Complex(double real) //构造方法重载 - 34 -
相关推荐: