03-面向对象的三大特征——多态


前言

接口的概述:接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
如图:
在这里插入图片描述
多态的概述:
同一个对象,在不同时刻表现出来的不同形态
在这里插入图片描述


一、接口

1.1 概述

  • 当一个类中所有方法都是抽象方法时,我们可以将这个类定义为接口
  • 接口比抽象类还要抽象
  • Java中接口存在的两个意义
    1. 用来定义规范
    2. 用来做功能的拓展
  • 接口用关键字interface修饰

    public interface 接口名 {} 
    
  • 类实现接口用implements表示

    public class 类名 implements 接口名 {}
    
  • 接口不能实例化

    ​ 我们可以创建接口的实现类对象使用

  • 接口和类之间是实现关系,类实现接口用implements关键字表示

    public class 类名 implements 接口名 {}
    
    • 注意: 在接口的实现关系中,我们可以看成接口是实现类的父类,但是严格意义上来讲接口和实现类不是父子类关系,只有通过extends连接的两个类才是真正意义上的父子类关系

在这里插入图片描述
接口的子类(实现类)

  • 要么重写接口中的所有抽象方法

  • 要么子类也是抽象类

1.2接口的成员特点

  • 成员特点

    • 成员变量

      ​ 只能是常量
      ​ 默认修饰符:public static final
      在这里插入图片描述

  • 构造方法(含有大括号—进行具体实现内容)

    ​ 没有,因为接口主要是用来扩展功能的,而没有具体存在,里边全都是抽象方法;而构造方法必须存在具体的实现内容,因此接口类中不能出现其构造方法

  • 成员方法

    ​ 只能是抽象方法

    ​ 默认修饰符:public abstract

    • 关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解

      • 在JDK1.8(JDK8)之前,接口中只能允许存在抽象方法
      • 在JDK1.8、JDK1.9之后,接口中允许存在有方法体的非抽象方法(后续)
  • 代码演示

    • 接口
    public interface Inter {
        public static final int NUM = 10;
    
        public abstract void show();
    }
    
    • 实现类
    class InterImpl implements Inter{
    
        public void method(){
            // NUM = 20;
            System.out.println(NUM);
        }
    
        public void show(){
    
        }
    }
    
    • 测试类
    public class TestInterface {
        /*
            成员变量: 只能是常量 系统会默认加入三个关键字
                        public static final
            构造方法: 没有
            成员方法: 只能是抽象方法, 系统会默认加入两个关键字
                        public abstract
         */
        public static void main(String[] args) {
            System.out.println(Inter.NUM);
        }
      
    }
    

1.3类和接口的关系

  • 类与类的关系

    ​ 继承关系,只能单继承,但是可以多层继承

  • 类与接口的关系

    ​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

    ​ 继承关系,可以单继承,也可以多继承

1.4 接口组成更新

1.4.1 接口组成更新概述

  • 常量

    public static final

  • 抽象方法

    public abstract

  • 默认方法(Java 8)

  • 静态方法(Java 8)

  • 私有方法(Java 9)

1.4.2 接口中默认方法

  • 格式(使用default关键字,并且方法体中)

    public default 返回值类型 方法名(参数列表) { }

  • 作用

    • 解决接口升级的问题
    • 应用场景概述:
      • 现存在一个接口类,里边定义了不同的抽象方法,然后它有多个实现类(按照实现类的规定,必须对接口类中的所有抽象方法进行一个强制重写)
      • 在使用中,现又需要对接口类进行新增某个功能的这样一个修改操作
      • 通常来说,新增该功能就是在我们的接口类中再编写一个抽象方法
      • 但是此时会发现该接口类的所有实现类都会报错,因为由于实现关系中对抽象方法必须进行一个强制重写,而要解决这个报错只能在每个实现类中都对新功能方法进行重写,修改起来会很麻烦
      • 此时就可以利用接口中的默认方法来完成
      • 在接口类中定义一个实现新功能的默认方法,并完成其方法体的编写,在主函数中可以用实现类的对象直接调用新方法即可
  • 范例

    public default void show3() { 
    }
    
  • 注意事项

    • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字(此时与之前的硬性条件有所不同,之前要求方法重写时方法声明必须一模一样)
      在这里插入图片描述
    • 在接口中,public可以省略,default不能省略
      在这里插入图片描述
  • 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写

  • 一个实现类可以与多个接口类存在实现关系

    • 但如果多个接口类中存在相同默认方法(即存在各自的方法体)

    • 实现类中就必须对该方法进行强制重写

    • 重写后按照就近原则,就会优先访问实现类中该方法的方法体

    • 避免出现实现类不知道应该继承出哪个接口的方法的情况

1.4.3 接口中静态方法

  • 格式

    public static 返回值类型 方法名(参数列表) { }

  • 范例

  • 应用场景:想要新增功能,并固定此功能(写死),即规定不想要实现类对该功能进行重写,此时就可以利用接口中的静态方法应用

    public static void show() {//public可以省略
    }
    
  • 注意事项

    • 静态方法只能通过接口名调用,不能通过实现类的类名或者对象名调用
    • 静态方法不能被实现类重写
    • public可以省略,static不能省略
  • 代码示例

//接口类与实现类
package com.wedu.JieKou.StudyDemo;

public interface Inter {
    void showA();

    public abstract void showB();

    //想新增一个功能,利用接口的默认方法直接定义
    public default void showC(){
        System.out.println("接口中的新增方法");
    }

    static void showD(){
        System.out.println("接口中的新增方法======D=====");
    }
}

 class InterImpL1 implements Inter{
    @Override
    public void showC(){
        System.out.println("实现类1的新功能重写");
    }

    @Override
    public void showA(){
         System.out.println("实现类1的showA方法重写");
     }

     @Override
     public void showB(){
         System.out.println("实现类1的showB方法重写");
     }
 }

class InterImpL2 implements Inter{
    @Override
    public void showA(){
        System.out.println("实现类2===的showA方法重写");
    }

    @Override
    public void showB(){
        System.out.println("实现类2====的showB方法重写");
    }
}
//测试类
package com.wedu.JieKou.StudyDemo;

public class StudyTestDemo {
    public static void main(String[] args) {
        InterImpL1 interImpL1 = new InterImpL1();
        interImpL1.showA();
        interImpL1.showB();

        InterImpL2 interImpL2 = new InterImpL2();
        interImpL2.showA();
        interImpL2.showB();

        //InterImpL1.showC();//不知道是不是因为JDK版本问题导致无法实现直接调用

        Inter.showD();
    }
}

在这里插入图片描述

1.4.4 接口中私有方法

  • 私有方法产生原因

    • Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。
    • 这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码时
    • 程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的
    • 因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
  • 定义格式

    • 格式1:私有的非静态方法

    • 场景:接口类中有多个默认方法,且当中存在共性,此时提取它们的共性并利用接口的“私有的非静态方法”应用,编写一个方法,并可以在默认方法中直接调用该“共性”的方法

    • 最后在测试类中使用接口类的类名调用默认方法即可

在这里插入图片描述

  • private 返回值类型 方法名(参数列表) { }

  • 范例1

    private void show() {  
    }
    
  • 格式2:私有的静态方法

    private static 返回值类型 方法名(参数列表) { }

  • 范例2
    在这里插入图片描述

  • 注意事项1

  • 默认方法可以调用私有的静态方法和非静态方法

  • 静态方法只能调用私有的静态方法

  • 注意事项2

    • 私有的非静态方法:外部测试类无法直接访问(不论是通过实现类的类名和对象,还是接口类的类名都无法直接访问),只能通过实现接口类中默认方法来间接调用该私有方法
    • 私有的静态方法

二、多态

2.1 多态的前提

  • 要有 继承(亲爹关系) 实现关系(干爹关系)

  • 要有方法的重写

  • 要有父类引用指向子类对象

    • 解释:
      在这里插入图片描述
      在这里插入图片描述

2.2多态中的成员访问特点

  • 成员访问特点

    • 成员变量

      编译看父类(左边),运行看父类(左边)

    • 成员方法

      编译看父类(左边),运行看子类(右边)

      • 要达到多态的目的,运行出子类中的方法,因此在多态中子类最好是重写父类的方法

2.3 多态的好处和弊端

  • 好处

    • 提高程序的扩展性

    • 定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

      • 定义方法参数时,使用父类型作为形参,在调用方法时,使用具体的子类型作为实参
      package com.wedu.DuoTai.StudyDemo;
      
      public class PolyTest {
          public static void main(String[] args) {
              Dog dog = new Dog();
              dog.show();
      
              Cat cat = new Cat();
              cat.show();
      
              Animals animals=new Cat();//编译看父类(左边),运行看子类(右边)
              animals.show();//控制台:	给猫喂食
      
              System.out.println("=============");
              Person person = new Person();
              person.feed(dog);
              System.out.println("=============");
      
              Animals animals1 = new Animals();
              animals1.show();
          }
      }
      
      class Person{
      
          //使用父类型作为形参,在调用方法时,使用具体的子类型作为实参  比如上面的person.feed(dog);语句
          public void feed(Animals animals){//相当于 Animals animals=new Dog();,即父类引用指向子类对象
              System.out.println("人给宠物喂食+++++");
              animals.show();
          }
      }
      
      class Animals{
          public void show(){
              System.out.println("给宠物喂食");
          }
      }
      class Dog extends Animals {
          @Override
          public void show(){
              System.out.println("给狗喂食");
          }
      }
      
      class Cat extends Animals{
          @Override
          public void show(){
              System.out.println("给猫喂食");
          }
      }
      
      
      • 定义方法返回值时,使用父类型作为返回值类型,具体return时,返回子类的对象
  • 弊端

    ​ 不能使用子类的特有成员

    (因为父类引用指向子类对象时,编译是看父类,先在父类中寻找有没有对应的成员,因此这个时候访问不到子类的特有成员,但是可以使用多态中的向上转型实现访问子类的特有成员)

3.4多态中的转型

  • 向上转型(类比自动类型转换)

    ​ 父类引用指向子类对象就是向上转型

    ​ 格式:父类型 对象名=子类对象;

  • 向下转型(类比强制类型转换)

    ​ 格式:子类型 对象名 = (子类型)父类引用;

在这里插入图片描述

3.5多态中转型存在的风险和解决方案

  • 风险

    如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException (类型转换异常)

  • 解决方案

  • 因为对应的实际类型与目标类型不是一种类型,不是同类型的无论如何都不能进行转换,但是我们可以在转换之前加一个判断,如果类型不一致就直接不转换,反之进行转换。来更好规避这在种风险

    • 关键字

      instanceof

    • 使用格式

      变量名 instanceof 类型

      通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值