10.3 方法复写
上一节,我们有这样的例子
public class Rectangle{
protected double length;
protected double height;
public double sqr(){
return length*height;
}
public double cc(){
return 2*(length+height);
}
}
public class Square extends Rectangle{
public Square(double length){
this.height=length;
this.length=length;
}
}
public static void main(String[]args){
Square s=new Square(4);
System.out.println(s.sqr());
}
即使Square类并没有sqr方法,对象s也能调用,因为Rectangle有这个方法,而s实际上也是一个Rectangle实例,也就是说s既是一个Square对象也是一个Rectangle对象。
子类由于在逻辑上是父类的一种,所以它能直接调用父类的所有方法。假如子类的方法,和父类名字一样,参数一样,这样的话这个方法会覆盖掉父类。例如Square现在是这样的话:
public class Square extends Rectangle{
public Square(double length){
this.height=length;
this.length=length;
}
@Override
public double sqr(){
System.out.println("调用Square的sqr方法");
return length*length;
}
public static void main(String[]args){
Square s=new Square(4);
System.out.println(s.sqr());
}
}
上面的15行代码main函数s.sqr调用的是父类Rectangle的sqr方法,还是子类Square的sqr方法呢?
答案是子类的sqr方法,如果父类和子类有同名同参数的方法,那么子类的对象使用的是子类的方法。这称为复写,可以理解成子类重新定义父类的某个方法。
我们观察到sqr方法的前面有这样的代码:@Override 。这种称为标签,用来描述方法的特征,在这里我们可以看做sqr是复写父类的。
这个Override标签不是必须的!就算不写只要条件达到,就是复写。
什么样的条件才能达到复写呢,或者说子类里什么方法前面加上@Override没有语法错误呢?子类的方法需要满足以下条件:
1.父类有同名方法。
2.该方法参数必须和父类方法的参数一致。
3.该方法的返回类型和父类方法一致。
4.子类方法不能降低父类方法的访问级别。这是指访问控制,父类方法如果是public,那么子类必须是public;父类方法如果是protected或者没有(没有意味着同一个包的类可见,不同包不可见),那么子类方法可以是protected或者public(private不能复写)。
5.静态方法不能被复写。
就算对象的声明部分是父类,仍然调用的是子类的方法。下面的例子s声明的类型是Rectangle,但是s.sqr()调用的是子类Square的sqr:
public static void main(String[]args){
Rectangle s=new Square(4);
System.out.println(s.sqr());
}
复写和重载有类似的,但实际上区别比较大:
| 重载 | 复写 | |
|---|---|---|
| 应用场景 | 同一个类 | 子类和父类 |
| 区别 | 相同方法名,不同参数 | 子类复写的方法必须和父类的方法同名、同参数、同返回类型、同访问控制级别 |
最后,就算发生了复写,子类还是可以显式调用父类的方法,子类可以在调用父类某个方法的基础上,再加上自己的代码。例如上面的矩形和正方形的面积计算sqr,实际上是相通的。我们可以这样写:
public class Square extends Rectangle{
public Square(double length){
this.height=length;
this.length=length;
}
@Override
public double sqr(){
System.out.println("调用Square的sqr方法");
return super.sqr();
}
}
这里的super代表的就是父类。事实上,我们可以使用这个关键字,显式调用父类的所有方法
练习题
-
客观题1.下面关于复写的说法,错误的是: (2分) [选择]A.
B. 子类可以复写父类任何方法
C. @Override标签不是必须的
D. 如果方法参数不一样,那么并不是复写而是重载
E. 父类方法如果是protected,那么子类方法复写可以用public
2.子类可以使用super调用父类所有protected或者public的属性和方法 (2分) [判断]3.一个子类对象,同时也是父类对象 (2分) [判断]4.假如在Square里面有一个这样的函数,Square里面也有sqr函数 (2分) [判断]test方法调用的是父类的方法sqr
(注意代码里面是两个类,必须用两个java文件保存)/** Rectangle */public class Rectangle{ protected double length; protected double height; public double sqr(){ return length*height; } public double cc(){ return 2*(length+height); }} /** Square.java */public class Square extends Rectangle{ public Square(double length){ this.height=length; this.length=length; } public double sqr(){ return length*height; } public void test(){ double s=super.sqr(); System.out.print(s); }}5.假如在Square里面有一个这样的函数,Square里面没有sqr函数 (2分) [判断]super.sqr()调用的是父类的方法sqr
(注意代码里面是两个类,必须用两个java文件保存)/** Rectangle.java */public class Rectangle{ protected double length; protected double height; public double sqr(){ return length*height; } public double cc(){ return 2*(length+height); }} /**Square.java */public class Square extends Rectangle{ public Square(double length){ this.height=length; this.length=length; } public void test(){ double s=super.sqr(); System.out.print(s); }}6.假如在Square里面有一个这样的函数test,Square里面也有sqr函数 (2分) [判断]test里面调用的sqr函数是父类的Rectangle的sqr
(注意代码里面是两个类,必须用两个java文件保存)/**Rectangle.java */public class Rectangle{ protected double length; protected double height; public double sqr(){ return length*height; } public double cc(){ return 2*(length+height); }} /**Square.java */public class Square extends Rectangle{ public Square(double length){ this.height=length; this.length=length; } public double sqr(){ return length*height; } public void test(){ double s=sqr(); System.out.print(s); }}7.关于下面的方法的调用,错误的是: (2分) [选择]A.
B. 对象调用的方法,可能是来自父类,也可能是父类的父类
C. 当调用一个方法的时候,如果类本身没有此方法,那么就往父类去寻找这个方法,如果在整个继承体系都没找到,那么会有语法错误
D. 子类可以调用父类的方法,但这个方法必须是protected
E. 子类可以复写父类的方法,所谓复写是指子类方法名字、参数和父类的方法一样
8.完成下面的填空 (4分) [编程填空]已知两个类,代码如上所示。现在有下面的代码:(注意代码里面是两个类,必须用两个java文件保存)/* Rectangel.java */public class Rectangle{ protected double length; protected double height; public double sqr(){ return length*height; } public double cc(){ return 2*(length+height); }}/* Square.java */public class Square extends Recangle{ public Square(double length){ this.height=length; this.length=length; } public static void main(String[]args){ int a=new Scanner(System.in).nextInt(); Square s=new Square(a); System.out.println("面积是:"+ +",周长是:"+ ); }}9.super关键字 [阅读]super关键字是用来指代父类对象,我们知道一个子类被创建,是在父类的基础上,也就是先创建父类对象,然后扩展这个父类对象成为子类对象。所以这个对象即时父类的对象,也是子类的对象。而这个super,就是父类对象。我们可以通过它访问父类的方法。
例如下面的代码,Square的sqr方法,调用了父类的sqr方法public class Square extends Rectangle{ public double sqr(){ return super.sqr(); }} -
编程题1.设计下面的类和测试代码 (10分) [Java多类]父类鸟类Bird,它有一个无参数方法fly,代表飞行。此方法调用后打印这样一句话“开始飞行”。还有一个方法land,代表着陆,此方法调用后打印“降落”。
子类麻雀类Sparrow。
写一个main函数来测试这两个类,要求定义一个Sparrow对象,然后调用fly和land方法。
2.给Rectangle和Square增加修改边长的功能。 (10分) [Java多类]1.首先给Rectangle的两个属性height和length增加getter和setter方法
2.在Square里复写这两个属性的setter方法,使得正方形永远能保持,具体做法就是只要设置一条边,另一条边也设置成这个值。
Square类必须包含带一个参数的构造函数,下面的main函数是测试代码
本题需要Recangle类有sqr函数以计算面积public static void main(String[]args){ Rectangle s=new Rectangle(); s.setHeight(6); s.setLength(7); System.out.println(s.sqr()); Square s1=new Square(5); s1.setHeight(6); System.out.println(s1.sqr()); s1.setLength(7); System.out.println(s1.sqr());}