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代表的就是父类。事实上,我们可以使用这个关键字,显式调用父类的所有方法