目录
实验四 继承和多态
实验目的
掌握继承和多态的概念与实现方法。
掌握如何从已有的类中派生子类并继承父类。
掌握方法的覆盖和重载。
实验内容
实现Student类、Sort类,满足以下要求:
- Student类包括学生学号sid、学生姓名name、学生电话phone三个私有数据域,并有其get方法。
- 有三个数据域的构造方法。
- 重写Student类的toString()方法,使输出形式为“sid:201835132290, name:Hailey, phone=243-555-2837”。
- Student继承Comparable实现多态性,重写compareTo()方法;编写Sort类的selectionSort(Comparable<T>[] list)方法,共同实现Student对象对比时按照学号从小到大排序。
- 测试程序给出student对象的数组,调用selectionSort(Comparable<T>[] list)方法对其进行排序,最后得到按照学号排序的学生信息的输出。如:
sid:201934562346,name:Sarah,phone=457-555-7862
sid:202012434629,name:Marsha,phone=587-555-1225
实验设计的思路和考量
父类和子类
我们可以从现有的类定义新的类,这称为类的继承。新类称为次类、子类和继承类;现有的类称为超类、父类或基类。
语法:
public class Circle extends GeometricObject
extends是继承的关键字,GeometricObject为父类Superclass,而Circle就是子类Subclass。通过继承,Circle类拥有GeometricObject类的所有可以访问的数据域和方法。
父类中的私有数据域在该类之外是不可访问的。因此,不能在子类中直接使用父类的private数据域。但是,如果父类中定义了公共的访问器或修改器,那么可以通过这些公共的访问器和修改器来访问/修改他们。
Super关键字的使用
Super关键字指代父类,可以用于调用父类中的普通方法和构造方法。
注:父类的构造方法不会被子类所继承,他们只能用关键字从子类的构造方法中调用。
比如:
public class GeometricObject {
public GeometricObject(){}
}
class Circle extends GeometricObject{
public Circle() {
super();//如果父类有有参构造方法,可以用super(parameters)
}
}
Super调用父类的普通方法时,语法有所不同:super.方法名(参数)
比如父类中有getDateCreated()方法,Circle类中可以有printCircle()方法调用它:
public void printCircle(){
System.out.println("The circle is created"+super.getDateCreated()+"and the radius is"+ radius);
}
方法重写/覆盖
即子类从父类继承方法时,有时需要修改父类中定义的方法的实现,那就需要在子类中使用和父类一样的签名以及一样的返回值类型来对该方法进行定义。
以下有两点需要注意:
- 仅当实例的方法是可访问时,它才能被覆盖。因为私有的方法在它的类本身以外是不能访问的,所以它不能被覆盖。
- 与实例方法一样,静态的方法也能被继承。但是,静态的方法不能被覆盖。如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏。可以使用语法:父类名.静态方法名,调用隐藏的静态方法。
方法的重载
重载意味着使用同样的名字但是不同的签名来定义多个方法。
因为重载在之前已经讲过,所以此处用一个例子来显示重写和重载的不同。
public class Test {
public static void main(String[] args) {
A a = new A();
a.p(10);
a.p(10.0);
}
}
class B {
public void p(double i) {
System.out.println("B:" + i);
}
}
class A extends B {
@Override
public void p(double i) {
System.out.println("A:" + i);
}
}
public class Test {
public static void main(String[] args) {
A a = new A();
a.p(10);
a.p(10.0);
}
}
class B {
public void p(double i) {
System.out.println("B:" + i);
}
}
class A extends B {
public void p(int i) {
System.out.println("A:" + i);
}
}
运行第一段程序中的Test类时,a.p(10)和a.p(10.0)调用的都是定义在类A中的p(double i) 方法,所以程序都显示10.0。运行第二段中的Test类时,a.p(10)调用类A中定义的p(int i)方法,显示输出为10,而a.p(10.0)调用定义在类B中的 p(double i)方法,显示输出为10.0。
多态和动态绑定
多态意味着父类的变量可以指向子类的对象,常在动态绑定中体现。动态绑定就是方法可以沿着继承链中的多个类中实现,JVM决定运行时调用哪个方法。
例子如下:
public class DynamicBindingDemo {
public static void main(String[] args) {
m(new GraduateStudent());
m(new Student());
m(new Person());
m(new Object());
}
public static void m(Object x) {
System.out.println(x.toString());
}
}
class GraduateStudent extends Student {
}
class Student extends Person {
@Override
public String toString() {
return "Student";
}
}
class Person extends Object {
@Override
public String toString() {
return "Person";
}
}
运行结果:
由例子中可知,类GraduateStudent、Student、Person、Object都有它们自己对toString()方法的实现,使用哪个实现取决于运行时x的实际类型。
Object类的equals方法
如同toString()方法,equals(Object)方法是定义在Object类中的另外一个有用的方法,它测试两个对象是否相等。
它的签名是:public boolean equals(Object obj)
调用它的语法是:object1.equals(object2)
Object类中这个方法的默认实现是:
public boolean equals(Object obj) {
return (this==obj);
}
这个实现使用==运算符检测两个引用变量是否指向同一个对象。
实验示例代码
public class Student implements Comparable<Student>{
private String sid;
private String name;
private String phone;
public String getSid() {
return sid;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public Student( String sid,String name, String phone) {
this.sid = sid;
this.name = name;
this.phone = phone;
}
@Override
public String toString() {
return "sid:"+sid+",name:"+name+",phone="+phone;
}
@Override
public int compareTo(Student o) {
int result=sid.compareTo(o.getSid());
return result;
}
}
class Sort<T>{
public void selectionSort(Comparable<T>[] list){
int min;
Comparable<T> temp;
for(int index=0;index<list.length-1;index++){
min=index;
for(int scan=index+1;scan<list.length;scan++){
if (list[scan].compareTo((T)list[min])<0){
min=scan;
}
}
temp=list[min];
list[min]=list[index];
list[index]=temp;
}
}
}