继承、多态和方法覆盖

方法覆盖

从父类当中继承的方法不满足子类的使用需求时,就要考虑使用方法覆盖了;发生方法覆盖后子类对象会调用覆盖之后的方法。

方法覆盖的条件:

  • 必须发生在具有继承关系的两个子类之间;
  • 覆盖后的方法和原来的方法具有兼容的返回值类型,相同的方法名,相同的形式参数列表(子类的返回值类型必须是相同的类型或者是该类型的子类)

注意:

  1. 私有方法不能被继承,所以无法覆盖
  2. 构造方法无法被继承,所以不能覆盖【问题二】
  3. 覆盖之后的方法不能比原方法有更低的访问控制权限(可以从protected -> public)(举例来说 不能在编译期间是公有的,然后在执行期间突然被JVM阻止存取)
  4. 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或者更少
  5. 方法覆盖只和方法有关,和属性无关
  6. 静态方法不存在覆盖(意义不大)【问题一】

示例:

class Animal{
    void move(){
        System.out.println("Animal moving");
    }
    void sing(){
        
    }
}

要求子类Cat和Bird有不同的move方法:

class Cat extends Animal{
    @Override
    void move() {
        System.out.println("Cat wondering");
    }
}
class Bird extends Animal{
    @Override
    void move() {
        System.out.println("Bird flying");
    }
    void sing(int i){
        //此处sing和父类的sing没有发生方法覆盖,因为参数列表不同;但间接形成了方法重载
    }
}

多态

多态属于面向对象三大特征之一,前提是封装形成独立体,独立体之间形成继承关系,从而产生多态机制;

多态是同一个行为具有多个不同表现形式或者形态的能力;

java中允许:

  • 向上转型 子 —> 父(自动类型转换)父类型引用指向了一个子类型的对象
  • 向下转型 父 —> 子 (强制类型转换)

多态指的是:编译阶段绑定父类中的方法,运行阶段动态绑定子类中的方法(编译时一种形态,运行时一种形态)

如果想访问子类特有方法,必须向下转型

		Animal a1 = new Animal();
        a1.move();//Animal move
        Cat c1 = new Cat();
        c1.move();//Cat wondering
        Bird b1 = new Bird();
        b1.move();//Bird flying

Animal和Cat有继承关系,Cat is a Animal,java中支持语法:父类型引用指向子类型对象,即:

Animal a = new Cat();

a就是父类型引用, new Cat();就是子类型对象

Animal a2 = new Cat();
a2.move();//Cat wondering
Animal a3 = new Bird();
a3.move();//Bird flying

分析上述代码:

  1. 编译阶段:对于编译器来说,只知道a2的类型是Animal,在运行时会去Animal.class字节码文件中查找move方法,找到了就会绑定上move方法,静态绑定成功
  2. 运行阶段:实际上在堆内存中创建的java对象是Cat,所以move的时候真正参与运行的是Cat对象,执行阶段就会动态绑定Cat对象中的move方法

但是假定要执行子类中特有的方法:

Animal a5 = new Animal();
a5.catchMouse();

编译就会报错:这是因为在静态绑定阶段无法从Animal.class字节码文件中查找到catchMouse方法,静态绑定失败,编译无法通过

也就是说:子类中特有的方法无法静态绑定

这时就必须进行向下转型:(也就是使用强制类型转换将Animal类型的a引用转换成Cat类型的c引用)

Cat cat = (Cat)a5;
cat.catchMouse();

但是向下转型也是有风险的:ClassCastException

Animal a6 = new Bird();
Cat cat1 = (Cat)a6;//这时编译器检测到a6是Animal类型,和Cat有继承关系,可以向下转型
cat1.catchMouse();//堆内存中实际创建的对象是Bird 实际运行过程中Bird转换为Cat就不行了 没有继承关系

避免类型转换异常的发生:instanceof运算符;可以在运行阶段动态绑定引用指向对象的类型 结果只能是true或false

c instance of Cat 为true: c引用指向堆内存中的对象是一个Cat

Animal a7 = new Bird();
if(a7 instanceof Cat){
    Cat z = (Cat)a7;
    z.catchMouse();//任何时候进行向下转型都需要instanceof判断
}

为什么要用instanceof运算符

public class InstanceofTest01 {
    public static void main(String[] args) {
        AnimalTest at = new AnimalTest();
        at.test(new Bird());
        at.test(new Cat());
    }
}
class AnimalTest{
    public void test(Animal a){
        if(a instanceof Cat){
            Cat c = (Cat) a;
            c.catchMouse();
        } else if (a instanceof Bird) {
            Bird b = (Bird) a;
            b.getClass();
        }

    }
}

在test中,可能传过来的是Bird 也可能传过来的是Cat 就需要进行判断


继承与多态

当定义出一组类的父型时,可以用子型的任何类来填补任何需要或者期待父型的位置

运用多态时,引用类型可以是实际对象的父类

Animal[] animals = new Animal[5];

animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Wolf();
animals[3] = new Hippo();
animals[4] = new Lion();

for(int i = 0; i < animals.length; i++){
    animals[i].eat();
    animals[i].roam();
}

编译器会寻找引用类型来决定是否可以调用该方法,但在执行期间,编译器寻找的不是引用的类型而是堆上的对象,假定此时编译器同意这个调用,则唯一能通过的方法是覆盖的方法也具有相同的参数和返回值。

并且与上例相同 参数与返回值类型也可以是多态


当要覆盖父类的方法时,就必须要“履行约定”,比如这个约定是:

boolean trunOn(){}

这就表示:没有参数且具有布尔类型的返回值


多态存在的三个条件:

  1. 继承
  2. 方法覆盖
  3. 父类型引用指向子类型对象

多态显然是离不开方法覆盖机制的,多态就是因为编译时绑定的父类中的方法,运行时实际上绑定的子类的方法,如果子类对象的方法没有重写,这个时候创建子类对象就没有意义了,只有子类将方法重写之后调用到子类对象上的方法产生不同效果时,多态就形成了。

问题一:静态方法没有方法覆盖

public class StaticMethodOverWrite {
    public static void main(String[] args) {
        Math m = new Math();
        Math s = new MathSubClass();
        m.sum();
        s.sum();
    }
}
class Math{
    public static void sum() {
        System.out.println("Math sum execute");
    }
}
class MathSubClass extends Math{
    public static void sum() {
        System.out.println("MathSubClass sum execute");
    }
}

这就说明 静态方法执行的时候跟对象无关,虽然是引用.调用静态方法,但在执行的时候会变成类名.就会执行Animal的sum方法

问题二: 私有方法不能覆盖

public class PrivateMethodOverWrite {
    private void doSome(){
        System.out.println("Private doSome execute");
    }

    public static void main(String[] args) {
        PrivateMethodOverWrite privateMethodOverWrite = new T();
        privateMethodOverWrite.doSome();//此处执行的是子类中的方法 ,说明父类中私有方法没有继承
    }
}
class T extends PrivateMethodOverWrite{
    public void doSome(){//此处重写需要注意:访问权限不能更低,只能更高或者持平
        System.out.println("SubClass doSome execute");
    }
}

兼容的返回值类型

public class CompatibleReturnValue {
    public Animal doSome(){
        return null;
    }
}
class SubClass extends CompatibleReturnValue{
    @Override
    public Cat doSome() {//返回值类型变为父类方法中声明的子类 可行
        return null;
    }
}

兼容的返回值类型:子类的返回值类型必须是相同的类型或者是该类型的子类

多态在实际开发中的作用

public class PolymorphismInActualDevelopment {
    public static void main(String[] args) {
        new Master(new Cat()).feed();
        new Master(new Bird()).feed();
        new Master(new Dog()).feed();
    }
}
class Master{
    Animal a;
    public Master(Animal a) {//Animal a = new Cat();
        this.a = a;
    }
    public void feed(){
        a.eat();
    }
}
class Dog extends Animal{
}

访问权限修饰符

  • private表示私有的 只能在本类中访问
  • public表示公开的 任何位置都能访问
  • default表示只能在本类、同package下访问
  • protected表示只能在本类,同package,子类中访问
访问控制修饰符本类同包子类任何位置(其他包中没有继承关系的类)
public可以可以可以可以
protected可以可以可以不行
default可以可以不行不行
private可以不行不行不行

从大到小排序:public > protected > default > private

访问权限修饰符可以修饰:

属性(4个都可以)

方法(4个都可以)

类(public & default)

接口(public & default)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值