JavaSE中的继承和多态

继承

背景

代码中创建的类,主要是为了抽象现实中的事物

有的时候客观事物之间就存在一些关联关系,那么用代码表示成类和对象的时候也会存在一定的联系

例如,设计一个类表示动物

public class Animal{
    public String name;
    public int age;

    public void eat(){
 
    }
    public void run(){}
}

public class Dog(){
    public String name;
    public int age;

    public void eat(){
 
    }
    public void run(){}
}

public class Bird(){
    public String name;
    public int age;

    public void eat(){
 
    }
    public void run(){}
    public void fly(){}
}

上述代码中,我们发现了大量的冗余代码

仔细分析,我们会发现Animal和Dog、Bird这两个类中存在一定的联系:

  • 这三个类都具备相同的eat和run方法,行为一致。

  • 这三个类都具备相同name属性,意义一样

  • 从逻辑上讲,Dog和Bird是一种Animal (is -a 语义)

此时,我们可以让Dog和Bird分别继承Animal类,来达到代码重用的效果。

此时,Animal这样被继承的类,我们称为父类,基类或超类,对于像Bird和Dog这样的类,我们称为子类、派生类,和现实中的继承类似,子类会继承父类的成员和方法,以达到代码重用的效果。

继承关键字 extends

继承后

public class Animal{
    public String name;
    public int age;

    public void eat(){
 
    }
    public void run(){}
}

public class Dog() extends Animal{
  
}

public class Bird() extends Animal{
  
    public void fly(){}
}

在类继承后,共同的成员属性和方法只需要在父类中编写即可。

子类中特有的成员和方法可另行编写

例如:Bird中的fly方法

语法规则

基本语法

class 子类 extends 父类{}
  • 使用extends指定父类

  • Java中一个子类只能继承一个父类

  • 子类会继承父类中所有的public的成员和方法

  • 对于父类中的private的成员和方法,子类是无法访问的

  • 子类的实例中,也包含着父类的实例,可以使用super关键字得到父类实例的引用

多态

向上转型

一般的对象实例中,我们都是这样实例化

Bird bird = new Bird();
Dog dog = new Dog();

这样正常引用,正常访问除了private修饰的成员和方法

但Java提供了向上转型的概念,即父类引用可以指向子类实例,如

Animal bird = new Bird();
Animal dog = new Dog();

向上转型发生的时机:

  • 直接复制

  • 方法传参

  • 方法返回

方法传参

class Animal{
    public String name;
    public Animal(String name){
        this.name = name;
    }
    public void eat(String food){
        System.out.println(name + "正在吃" + food);
    }
} 
class Bird extends Animal{
    public void fly(){}
}
public class test{
    public static void main(String[] args){
        Animal bird = new Bird("嘻嘻");
        bird.eat("薯条");//嘻嘻正在吃薯条
    }
}

此时,bird的引用类型是Animal类(父类),实际类型是Bird(子类)

方法返回

public class Test { 
 public static void main(String[] args) { 
     Animal animal = findMyAnimal(); 
 } 
 public static Animal findMyAnimal() { 
     Bird bird = new Bird("嘻嘻"); 
     return bird; 
 } 
}

此时,findMyAnimal()返回的是Animal类型的引用,实际上是应用到Bird的实例。

动态绑定

当子类和父类出现同名方法的时候,再去调用会发生什么情况呢?

例如:

class Animal{
    public String name;
    public int age;

    public Animal(String name){
        this.name = name;
    }
    public void eat(){
         System.out.println("我是一只小动物");
         System.out.println(name + "正在吃");
    }
    public void run(){
        System.out.println(name + "正在跑");
    }
}

class Dog() extends Animal{

    public Dog(String name){
        super(name);
    }
    @Override
    public void eat(){
         System.out.println("我是一只小狗狗");
         System.out.println(name + "正在吃");
    }
}

public class Test{
    public static void main(String[] args){
        Animal dog = new Dog("柴柴");
        Animal animal = new Animal("哈哈");
        dog.eat();
        animal.eat();
    }
}

//执行结果:
我是一只小狗狗
柴柴正在吃
我是一只小动物
哈哈正在吃

此时,我们发现

  • dog和animal都是Animal的引用类型,但dog实际指向Dog的实例,animal实际指向Animal的实例

  • 针对dog和animal调用eat方法,发现他们实际上执行了实例对象中的方法。

因此,Java中,调用某个类的方法,实际上执行的是运行类型的方法(若无重写,则向上调用)。

继承和多态的注意事项

  • 每一次子类实例的创建,实际上得创建父类对象,因此构造器中必须调用super(...),且必须放在第一行,一般会自动帮我们补上无参构造器,但父类一旦写上参数的构造器(覆盖了无参构造器),则应在子类的构造器上调用父类的构造器

  • 多态中,对于字段和方法,我们只能访问或调用引用类型(编译类型)的字段和方法,若想调用子类中特有的字段和方法,则应该向下转型。

你可能感兴趣的:(java)