Java-多态,接口详解

文章目录

  • 一、多态
    • 1、向上转型
      • 1.1、发生的时机:
        • 1.1.1、直接赋值
        • 1.1.2、方法传参
        • 1.1.3、方法返回
    • 2、动态绑定
    • 3、向下转型
  • 二、super关键字
  • 三、抽象类
    • 3.1、语法规则
    • 3.2、注意
  • 四、接口
    • 4.1、语法规则
    • 4.2、实现多个接口
    • 4.3、接口间的继承

一、多态

面向对象编程的三大特性之一:多态。
1.继承的存在(继承是多态的基础,没有继承就没有多态).
2.子类重写父类的方法(多态下调用子类重写的方法).(子类不一定需要将方法进行重写)
3.父类引用变量指向子类对象(子类到父类的类型转换).

从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。

多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用:消除类型之间的耦合关系。

1、向上转型

例如:

Bird bird = new Bird("圆圆"); 

这个代码也可以写成

Bird bird = new Bird("圆圆"); 
Animal bird2 = bird; 
// 或者写成下面的方式
Animal bird2 = new Bird("圆圆"); 

此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为向上转型。

1.1、发生的时机:

1.1.1、直接赋值

例如:

Animal bird2 = bird; 

这就是直接赋值的一种表现,此时bird的类型是Bird,而bird2的类型是Animal,将bird赋值给bird2时,bird就会发生向上转型,成为Anmal类型。和基本数据类型中的自动提升相似。

1.1.2、方法传参

作用:
当我们调用一个方法时,会有参数的传入。利用向上转型,就可以在设置方法传入参数的类型时,设置成父类类型,这样子类的对象都可以作为这个方法的参数传入。
例如:
Java-多态,接口详解_第1张图片

1.1.3、方法返回

当一个方法需要返回一个类型时,我们也可以将这个类型设置成父类类型,这样想返回其子类也可以,不用重新去写一个方法。
例如:
Java-多态,接口详解_第2张图片

2、动态绑定

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

// Animal.java
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

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

// Bird.java 
public class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }

    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}

// Test.java 
public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Animal("圆圆");
        animal1.eat("谷子");
        Animal animal2 = new Bird("扁扁");
        animal2.eat("谷子");
    }
} 
// 执行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
扁扁正在吃谷子

此时

· animal1 和 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的实例, animal2 指向Bird 类型的实例.
**·**针对 animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而
animal2.eat() 实际调用了子类的方法.

因此, 在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为 动态绑定。

3、向下转型

向下转型就相当于基本类型中的大类型转换成小类型,需要使用强转。

Bird bird = (Bird)animal;

注意animal原本的原本的类型,如果他原来不是Bird类型就会出现异常

Animal animal = new Cat("小猫"); 
Bird bird = (Bird)animal; 
bird.fly(); 
// 执行结果, 抛出异常
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Bird 
 at Test.main(Test.java:35)

当我们需要使用子类独有的方法时,就需要使用向下转型才能使用。
向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见,但是也有一定的用途.

instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全了.

Animal animal = new Cat("小猫"); 
if (animal instanceof Bird) { 
 Bird bird = (Bird)animal; 
 bird.fly(); 
}

二、super关键字

在子类中调用父类的属性和方法,就需要使用super关键字。

  1. 使用了 super 来调用父类的构造器
public Bird(String name) { 
 super(name); 
}
  1. 使用 super 来调用父类的普通方法
public class Bird extends Animal { 
 public Bird(String name) { 
 super(name); 
 } 
 @Override 
 public void eat(String food) { 
 // 修改代码, 让子调用父类的接口. 
 super.eat(food); 
 System.out.println("我是一只小鸟"); 
 System.out.println(this.name + "正在吃" + food); 
 } 
}

注意:
在构造方法中调用重写的方法

class B {
    public B() {
        // do nothing
        func();
    }

    public void func() {
        System.out.println("B.func()");
    }
}

class D extends B {
    private int num = 1;

    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}

public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
} 
// 执行结果
D.func() 0

此时会触发动态绑定,调用子类中重写的方法,不会调用父类的方法。

三、抽象类

这个父类本身没有太大的意义,然后其子类有共同的行为,但是各自的表现形式都不相同。此时我们就可以将这个父类定义成抽象类。当一个类被定义成抽象类,这个类就无法产生对象。

3.1、语法规则

我们用abstract来表示一个类是抽象类。

3.2、注意

1.abstract不能和private同时修饰一个类
2.抽象类里面没有方法体的方法称为抽象方法
3.抽象类是普通类的一个超集,普通类有的属性和方法抽象类都有。
4.抽象类可以没有抽象方法,有抽象方法的类一定是抽象类
5.抽象类的子类必须完成抽象类里面的抽象方法,不然也要定义成抽象类。
6.当一个子类多层继承了多个抽象类,此时如果子类不想定义为抽象类,就需要完成所有抽象方法。

四、接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.
接口表示一种规则,一种规定。就像生活中的电脑的插口,或者插排上面的插口,想使用三角口,就必须将插头设计成三角形,不然无法使用。

4.1、语法规则

我们使用interface来修饰接口
例如:

interface USB{
//常量字段
//抽象方法
}

当我们需要去使用这个USB接口时,我们就必须按照他的规定,就是纪要满足他的常量片段,还要完成他给的抽象方法。
我们使用implements表示一类实现了一个接口,即满足了这个规定。
实现的这个类继承了接口中所有的常量字段。

class  Keybord implements USB{
    
}

4.2、实现多个接口

现在我们通过类来表示一组动物

class Animal { 
 protected String name; 
 
 public Animal(String name) { 
 this.name = name; 
 } 
} 

另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”

interface IFlying { 
 void fly(); 
} 
interface IRunning { 
 void run(); 
} 
interface ISwimming { 
 void swim(); 
}

接下来我们创建几个具体的动物
猫, 是会跑的.

class Cat extends Animal implements IRunning { 
 public Cat(String name) { 
 super(name); 
 } 
 @Override 
 public void run() { 
 System.out.println(this.name + "正在用四条腿跑"); 
 } 
}

鱼, 是会游的

class Fish extends Animal implements ISwimming {
    public Fish(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(this.name + "正在用尾巴游泳");
    }
}

青蛙, 既能跑, 又能游(两栖动物)

class Frog extends Animal implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name + "正在往前跳");
    }

    @Override
    public void swim() {
        System.out.println(this.name + "正在蹬腿游泳");
    }
} 

还有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }

    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }

    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
} 

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.
先继承后实现。

4.3、接口间的继承

接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.

interface IRunning { 
 void run(); 
} 
interface ISwimming { 
 void swim(); 
} 
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming { 
} 
class Frog implements IAmphibious { 
 ... 
} 

通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法,也需要实现 swim 方法.
接口间的继承相当于把多个接口合并在一起

你可能感兴趣的:(java,开发语言)