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