Java – 多态,接口


6.13、多态

多态的基本介绍

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的使用前提

  1. 多态必须发生在子父类关系中。(继承关系)
  2. 使用多态一般要使用方法的重写,如果不使用方法的重写,那么多态就没有意义了。

好处:优化代码设计,比较强调代码的设计思想。

多态的使用

方法的多态

重写和重载就体现多态

对象的多态 (核心,困难,重点)

(1) 一个对象的编译类型和运行类型可以不一致

(2) 编译类型在定义对象时,就确定了,不能改变

(3) 运行类型是可以变化的

(4) 编译类型看定义时 =号的左边,运行类型看 =号 的右边

多态的向上转型

本质:父类的引用指向了子类的对象

语法:父类类型 引用名 = new 子类类型()

特点:

  1. 编译类型看左边,运行类型看右边
  2. 可以调用父类中的所有成员(需遵守访问权限)
  3. 不能调用子类中特有成员
  4. 最终运行效果看子类的具体实现!
多态的向下转型

语法:子类类型 引用名 = (子类类型) 父类引用

前提:发生了向上转型

只能强转父类的引用,不能强转父类的对象

要求父类的引用必须指向的是当前目标类型的对象

当向下转型后,可以调用子类类型中所有的成员

如何解决类型转换异常

向下转型,可能会发生java.lang.ClassCastException: 类型转换异常(运行期异常)

instanceof关键字

instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

语法:

//如果父类的引用是子类的类型:返回true
//如果不是:返回false
if(父类的引用 instanceof 子类类型){
    //转换
}else{
    System.out.println("类型转换异常!");
}

多态案例

父类:宠物类 属性:名字,种类 抽象方法:eat,call

子类:鸟,狗,猫 属性:名字,种类 重写:eat、call 自己独有的方法

manager类:feed(Pet pet)方法,findName(Pet[] pets,String name)方法,Pet findType(int num)方法,doSomething(Pet pet)方法

public abstract class Pet {
    private String name;
    private String variety;
    public Pet(){};
    public Pet(String name, String variety) {
        this.name = name;
        this.variety = variety;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVariety() {
        return variety;
    }

    public void setVariety(String variety) {
        this.variety = variety;
    }

    public abstract void eat();
    public abstract void call();

}

public class Bird extends Pet{
    public Bird(){};
    public Bird(String name, String variety) {
        super(name, variety);
    }

    @Override
    public void eat() {
        System.out.println("鸟吃五谷");
    }

    @Override
    public void call() {
        System.out.println("啾啾~~~");
    }

    public void fly(){
    System.out.println("俺能飞~~~");
    }
}

public class Dog extends Pet{
    public Dog(){};
    public Dog(String name, String variety) {
        super(name, variety);
    }

    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

    @Override
    public void call() {
        System.out.println("汪汪~~~");
    }

    public void destoryHome(){
        System.out.println("拆家~~~");
    }
}

public class Cat extends Pet{
    public Cat(){};
    public Cat(String name, String variety) {
        super(name, variety);
    }

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void call() {
        System.out.println("喵喵~~~");
    }

    public void CatchMouse(){
        System.out.println("抓老鼠");
    }
}

public class Manger {
    public String name;
/*    public void feed(Dog dog){
        dog.eat();
        dog.call();
    }
    public void feed(Cat cat){
        cat.eat();
        cat.call();
    }*/


    //此处喂食方法形参是宠物类的对象,但在调用该方法时
    // pet 的形态会发生变化,有 dog、cat、bird三种宠物,这就是多态
    public void feed(Pet pet){
        pet.eat();
        pet.call();
    }

    //新建Pet[]保存3只宠物,写一个方法查找名称为xxx的宠物,然后调用eat()方法。
    public void findName(Pet[] pets,String name){
        for (Pet pet : pets) {
            if (pet.getName().equals(name)){
                pet.eat();
                return;
            }
        }
        System.out.println("没有找到该宠物");
    }

    //根据宠物的类型来进行不同的调用 : 1 : 返回狗 2:返回猫 3:返回鸟
    public Pet findType(int num) {
        Pet pet = null;
        if (num == 1) {
            pet = new Dog();
        }else if (num == 2) {
            pet = new Cat();
        }else if (num == 3) {
            pet = new Bird();
        }else {
            System.out.println("类型错误");
        }
        return pet;
    }

    public void doSomeThing(Pet pet){
        pet.eat();
        pet.call();
        if (pet instanceof Dog){
            Dog dog = (Dog) pet;
            dog.destoryHome();
        } else if (pet instanceof Cat) {
            ((Cat)pet).CatchMouse();
        } else if (pet instanceof Bird) {
            ((Bird) pet).fly();
        }else {
            System.out.println("类型转换异常");
        }
    }
}

public class Test1 {
    public static void main(String[] args) {
        Bird bird = new Bird("花花", "画眉鸟");
        Dog dog = new Dog("大黄", "泰迪");
        Cat cat = new Cat("小妖", "加菲猫");
        Manger manger = new Manger();
        manger.name = "黄某";
        //调用方法时,向上转型
        // dog -> pet , cat -> pet , bird -> pet
        manger.feed(dog);
        manger.feed(cat);
        manger.feed(bird);
        System.out.println("===============");

        //应用2:在创建对象的时候,可以是多态的方式
        // 父类类型 父类的引用 = new 子类类型();
        //父类的引用指向子类对象
        Pet pet1 = new Bird("多多", "大鸟");
        //看起来pet是父类,本质上是子类
        //编译看左边,运行看右边,调用的是父类中的方法,运行的是子类中重写后的方法
        // 父类的引用不能调用子类独有的方法!!
        Pet pet2 = new Dog("小黄", "泰迪");
        Pet pet3 = new Cat("小花", "加菲猫");
        manger.feed(pet1);
        manger.feed(pet2);
        manger.feed(pet3);

        Pet[] pets ={new Bird("花花", "画眉鸟"),
                new Dog("大黄", "泰迪"),
                new Cat("小妖", "加菲猫")};
        manger.findName(pets,"大黄");

        //根据序号返回具体的动物类型
        Pet pet = manger.findType(2);
        if (pet != null){
            pet.eat();
        }
        System.out.println("===============");


        // 向上转型后,发现不能再调用子类独有的方法了
        // 这时需要向下转型 -> 强制转换
        Bird bird1 = (Bird) pet1;
        bird1.fly();
        //但是有时你并不知道,他向上转型之前是什么类型,需要用 instanceof 判断
        //instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
        if (pet2 instanceof Bird){
            Bird bird2 = (Bird) pet2;
            bird2.fly();
        }else {
            System.out.println("类型转换异常");
        }

        if (pet2 instanceof Dog){
            Dog dog2 = (Dog) pet2;
            dog2.destoryHome();
        }else {
            System.out.println("类型转换异常");
        }

        System.out.println("===============");
        manger.doSomeThing(cat);
    }
}

6.14、接口

接口的概念

接口是更加抽象的抽象的类

接口:在java中,接口也是一个类,但是这个类不能写 class ,只能写 interface

在接口中,一般只能定义抽象方法,只能定义静态常量,接口起到了定义规则和规范的作用,体现了代码优化的思想、程序设计的多态和高内聚低耦合的设计思想,表现出的是一种能力。Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。

接口的特点

  1. 接口中的方法一般都是 抽象方法 :public abstract(隐式存在/可以不写)

  2. 接口中的变量都是 静态常量 :public static final(隐式存在/可以不写)

  3. 接口是一个特殊的类,所以不能写class修饰,接口中的方法默认是public的

  4. 接口不能实例化!!!

  5. 接口中不能存在构造方法!

  6. 一个类可以 实现多个接口 ,所以接口是对java单继承的扩展,提高代码的扩展性。

  7. 一个类可以继承父类,同时可以实现多个接口。语法: public class 类名 extends 父类 implements 接口1, 接口2,接口3…

  8. 实现类必须实现接口中的全部方法,如果一个实现类只想实现接口的部分方法,那么必须要把该类定义成抽象类。

    public abstract class InterImpl2 implements Inter{ 
    }
    
  9. 接口是可以继承接口的,接口可以实现 多继承 ,一个接口可以继承多个接口!!!

  10. 接口继承其他接口时,如果其他接口的抽象方法名重复,那么会默认使用第一个有此方法的接口,因为抽象方法没有具体实现,所以重写那个抽象方法都是一样的!!!

  11. 类只能继承类,类只能实现接口。

  12. 实现类一旦实现了接口中的抽象方法,就不能修改方法的任何定义!!!

如何实现接口

语法:

public interface 接口名{
    //静态常量
    //抽象方法
    //普通方法(jdk新特性)
}

案例:

案例:实现防盗门功能

实线:继承 ; 虚线:接口

TheftproofDoor:防盗门 ; Door:门 ; Lock: 锁

防盗门 is a 门 ; 防盗门 has a 锁 功能 —> 能力

TheftproofDoor extends Door implements Lock

image

public abstract class Door {
    public abstract void doorUp();
    public abstract void doorDown();
}

public interface Doorbell {
    void takePhoto();
}

public interface Lock {
    void lockUp();
    void lockDown();
}

public class SafeDoor extends Door implements Lock, Doorbell{
    @Override
    public void doorUp() {
        System.out.println("用力推,门打开了。");
    }

    @Override
    public void doorDown() {
        System.out.println("轻轻拉门,门关上了。");
    }

    @Override
    public void takePhoto() {
        System.out.println("铃......咔嚓......照片已存储");
    }

    @Override
    public void lockUp() {
        System.out.println("插进钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙。");
    }

    @Override
    public void lockDown() {
        System.out.println("插进钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙。");
    }
}

public class Test01 {
    public static void main(String[] args) {
        SafeDoor safeDoor = new SafeDoor();
        safeDoor.doorDown();
        safeDoor.lockDown();
        safeDoor.takePhoto();
        safeDoor.lockUp();
        safeDoor.doorUp();
    }
}

抽象类和接口有哪些区别

  1. 定义上来说:

抽象类: public abstract class 类名{}

接口: public interface 接口名{} 接口使用比较多

  1. 具体使用上来说:

抽象类:可以定义普通方法,可以有构造方法,可以定义成员变量。

接口:一般定义成抽象方法,不能有构造方法,变量都是静态常量

抽象类和接口都不能实例化。

  1. 实际使用来说:

抽象类:is…a

接口:has…a

实现接口 与 继承 的区别

1)接口和继承解决的问题不同

继承的价值主要在于:解决代码的复用性和可维护性。

接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即 更加的灵活

2)接口比继承更加灵活

接口比继承更加灵活,继承是满足 is … a 的关系,而接口是 is … a 的关系

3)接口在一定程度上实现代码解耦[即:接口规范性 + 动态绑定机制]

使用多态的写法实例化对象

Pet pet = new Dog();

1)父类类型是普通类型

语法: 父类类型 父类引用 = new 子类类型();

2)父类类型是抽象类

语法: 抽象类 父类引用 = new 子类类型();

3)父类类型是接口

语法: 接口 接口引用 = new 实现类();

抽象类和接口在实际开发中的选择

  1. 抽象类侧重的是子类全部 共有的属性和行为 。然后用子类去继承。
  2. 接口侧重的是 部分子类具有的一些行为和能力 。然后用实现类去实现接口。

接口作为形参使用=多态

案例:学生管理

分析:

学生管理:StudentManager类

Student 父类 – study()

Code 接口

JAVA学生: coding();

WEB学生: coding();

UI学生:

public interface Code {
    //接口中的抽象方法
    void coding();
}

public abstract class Student {
    //抽象类的抽象方法
    public abstract void study ();
}

// JavaStudent 继承了 Student ,实现了 Code
public class JavaStudent extends Student implements Code{

    //重写接口的 coding 方法
    @Override
    public void coding() {
        System.out.println("Java学生敲代码");
    }

    // 重写抽象类的 study 方法
    @Override
    public void study() {
        System.out.println("Java学生在学习……");
    }
}

// JavaStudent 继承了 Student ,实现了 Code
public class WebStudent extends Student implements Code{

    //重写接口的 coding 方法
    @Override
    public void coding() {
        System.out.println("Web学生敲代码");
    }

    // 重写抽象类的 study 方法
    @Override
    public void study() {
        System.out.println("Web学生在学习……");
    }
}

// UIStudent 只继承了 Student
public class UIStudent extends Student{

    // 重写 抽象类的 study 方法
    @Override
    public void study() {
        System.out.println("UI学生在学习……");
    }
}

public class StudentManger {

    //把抽象类对象作为形参
    public void study(Student student){
        student.study();
    }

    //把接口对象作为形参
    public void code(Code code){
        code.coding();
    }

    //把接口作为方法的返回值类型,返回值必须要实现了接口的对象
    public Code reIndex(){
        return new JavaStudent();
    }
}

public class Test4 {
    public static void main(String[] args) {

        // UIStudent 向上转型 -> Student(抽象类)
        Student student = new UIStudent();
        student.study();

        // JavaStudent 向上转型 -> Code(接口)
        Code code = new JavaStudent();
        code.coding();

        //创建一个学生管理者对象,调用学生管理者的方法
        StudentManger studentManger = new StudentManger();
        // study 方法传递的参数必须是 Student抽象类的子类
        studentManger.study(new UIStudent());

        // code 方法传递的参数必须是 Code接口的实现类
        studentManger.code(new JavaStudent());

        // reIndex 方法的返回值类型是 Code。
        studentManger.reIndex().coding();
    }
}

接口作为类的属性使用=多态

案例:

USB接口:USBService 服务/功能

​ void connect();

USB风扇:USBFan

USB鼠标:USBMouse

U盘:USBDisk

扩展USB接口,实现电脑接入USB设备进行工作。组装一台计算机。

public interface USBService {
    void connect();
}

public class USBDisk implements USBService{
    @Override
    public void connect() {
        System.out.println("U盘已连接到电脑");
    }
}

public class USBFan implements USBService{
    @Override
    public void connect() {
        System.out.println("USB风扇已连接到电脑");
    }
}

public class USBMouse implements USBService{
    @Override
    public void connect() {
        System.out.println("USB鼠标连接到电脑");
    }
}

public class Computer {
    //接口作为类的属性使用
    USBService usb;
    //USBService usb = new USBDisk(); //也可以之间给这个属性创建对象

    public void work(){
        usb.connect();
    }
}

public class Test3 {
    public static void main(String[] args) {
        new USBFan().connect();
        new USBMouse().connect();
        new USBDisk().connect();

        Computer computer = new Computer();
        //为类的属性创建对象
        computer.usb = new USBDisk();
        computer.work();
    }
}

原创文章,作者:306829225,如若转载,请注明出处:https://blog.ytso.com/tech/java/275778.html

(0)
上一篇 2022年7月21日 09:35
下一篇 2022年7月21日 09:40

相关推荐

发表回复

登录后才能评论