• 【JavaSE】抽象类和接口


    前言:

    抽象类和接口都用于抽象化具体对象的,都不能直接实例化,但是两者的侧重点不同抽象类只要用来抽象类型表示这个对象是什么接口主要用来抽象功能表示这个对象能做什么

    接口是一系列方法的声明,是一些方法特征的集合。一个接口只有方法的声明没有方法的实现。


    目录

    前言:

    1、抽象类

    1.1、抽象类的概念

    1.2、抽象类语法

    1.3、抽象类的特性

    1.4、抽象类和普通类的区别

    1.5、抽象类的作用

    2、接口

    2.1、接口的概念

    2.1.1、浅显一点的理解

    2.1.2、更加深入的了解

    2.2、语法规则

    2.3、接口使用 

    2.4、接口的特性

    2.5、实现多个接口

     2.6、接口之间的继承(可以认为是接口的拓展)


    1、抽象类

    1.1、抽象类的概念

    📖在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描    绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类.

    在上述的例子中,我们发现,父类Animal中的bark()方法好像并没有什么实际工作,主要的叫声打印是由Animal的各种子类的bark()方法来完成的.像这种没有实际工作的方法,我们可以把它设计成一个抽象方法(abstract  method),包含抽象方法的类我们称为抽象类(abstract class)


    1.2、抽象类语法

    📕在Java中,一个类如果被abstract修饰称为抽象类,抽象类中被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

    1. // 抽象类:被abstract修饰的类
    2. public abstract class Shape {
    3. // 抽象方法:被abstract修饰的方法,没有方法体
    4. abstract public void draw();
    5. abstract void calcArea();
    6. // 抽象类也是类,也可以增加普通方法和属性
    7. public double getArea(){
    8. return area;
    9. }
    10. protected double area; // 面积
    11. }

    ❗❗❗注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法


    1.3、抽象类的特性

    1️⃣、抽象类使用abstract修饰类

    2️⃣、 抽象类不能被实例化

    3️⃣ 、在抽象类当中,可以有抽象方法,也可以有非抽象方法!!!

     

     4️⃣、什么是抽象方法,一个方法abstract修饰,没有具体的实现只要包含抽象方法,这个类必须时抽象类!!!否则编译器就会报错。

    📑一个抽象类,不一定包含抽象方法;是一个抽象方法必须在抽象类中。

    5️⃣、当一个普通类继承了抽象类,必须重写抽象类当中的抽象方法!!!

    1. abstract class Shape{
    2. //抽象方法
    3. public abstract void draw();
    4. }
    5. class Circle extends Shape {
    6. //重写抽象方法
    7. @Override
    8. public void draw(){
    9. System.out.println("⚪");
    10. }
    11. }

    6️⃣、抽象类存在最大的意义,就是被继承!!!

    7️⃣、抽象方法不能被private,final,static修饰!!!(换句话说抽象方法要满足重写的规则)

    📜抽象方法不能被private修饰

    1. abstract private void draw()//因为它的范围只能在自己类的内部使用,外面引用不到,也就
    2. //等于这个方法不存在。)

    📜抽象方法不能被final和static修饰,因为抽象方法要被子类重写

    1. abstract class Shape{
    2. //抽象方法
    3. final public abstract void draw();//被final修饰抽象方法
    4. //final修饰的方法是不能被改变的,就像被final修饰的变量,会成为常量,常量不能被修改
    5. }
    1. abstract class Shape{
    2. //抽象方法
    3. abstract public static void draw();//被static修饰的方法是静态方法,是类方法,是
    4. //属于Shape (父类) 的,子类重写不了。
    5. }

    8️⃣、当一个子类没有重写抽象的父类的方法,可以把当前子类变为abstract修饰。

    🧨情景一:在子类中重写父类抽象方法

    1. abstract class Shape{
    2. //抽象方法
    3. public abstract void draw();
    4. }
    5. class Cycle extends Shape {
    6. //重写抽象方法
    7. @Override
    8. public void draw() {
    9. System.out.println("⚪");
    10. }
    11. }

    🧨情景二:在子类中没有重写抽象方法,那子类有abstract修饰,成为抽象类

    1. abstract class Shape{
    2. //抽象方法
    3. public abstract void draw();
    4. }
    5. abstract class Cycle extends Shape {
    6. }

    9️⃣、抽象类必须被继承、并且继承之后子类要重写父类中的抽象方法。

    在第8条中,虽然(Cycle)中没有重写父类(Shape)的抽象方法(draw),但是在Cycle类的子类(A)中还得重写draw方法。

    1. abstract class Shape{
    2. //抽象方法
    3. public abstract void draw();
    4. }
    5. abstract class Cycle extends Shape {
    6. }
    7. class A extends Cycle {
    8. @Override
    9. public void draw() {
    10. System.out.println("A!");
    11. }
    12. }

    🔟、抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

    ⏸、抽象类中可以有构造方法,供子类创建对象,初始化父类的成员变量

    1. abstract class B {//抽象类
    2. public int a;
    3. public int b;
    4. public B(){//父类无参构造方法
    5. }
    6. public B(int a, int b) {//父类有参构造方法
    7. this.a = a;
    8. this.b = b;
    9. }
    10. public void func(){
    11. }
    12. }
    13. class C extends B{
    14. public C(){//子类无参构造方法
    15. super(1,2);
    16. }
    17. }

    1.4、抽象类和普通类的区别

                                 抽象类                              普通类
                        抽象类不能实例化                   普通类可以实例化
       抽象类中可以包含非抽象方法和抽象方法                   只能包含非抽象方法

    1.5、抽象类的作用

    🔜抽象类本身不能被实例化,想要使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。

    那么你是否会有这样的疑问:普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

     确实如此. 但是使用抽象类相当于多了一重编译器的校验.

    🔜使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.

    很多语法存在的意义都是为了 "预防出错", 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不 就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.

    充分利用编译器的校验, 在实际开发中是非常有意义的.


    2、接口

    2.1、接口的概念

    2.1.1、浅显一点的理解

    📖在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等。

     电脑的USB口上,可以插:U盘、鼠标、键盘...所有符合USB协议的设备

    电源插座插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备

    🔜通过上述例子可以看出:接口就是公共的行为给范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

    2.1.2、更加深入的了解

    • 在Java中接口是一个全部由抽象方法组成的集合,接口需要用interface定义,里面只能有抽象的方法和常量。
    • 接口体现的是事务扩展的功能,在Java中,类定义了一个实体,包阔实体的属性,实体的行为。而接口定义了一个实体可能发生的动作,只有一个声明没有具体的行为
    • 当一个方法在很多类中有不同的体现的时候,就可以将这个方法抽象出来做成一个接口
    • 接口内只能有不可修改的全局常量,只能有抽象的方法,接口没有构造方法。

    2.2、语法规则

    📕接口定义格式与定义的格式基本相同,将class关键字换成interface关键字就定义了一个接口。

    1. public interface 接口名称{
    2. public abstract void method1();//public abstract 是固定搭配,可以不写
    3. public void method2();
    4. abstract void method3();
    5. void method4();
    6. //注意:因为在接口中public abstract是固定搭配,可以不写,所以在接口中上述写法都是抽象方
    7. //法,更推荐方式4,代码更简洁。
    8. }

    ❗❗❗提示

    1、创建接口时,接口的命名一般以大小字母 I 开头

    2、接口的命名一般使用 "形容词" 词性的单词。

    3、阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性。 


    2.3、接口使用 

    📕接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。

    1. public class 类名称 implements 接口名称{
    2. // ...
    3. }

    ❗❗❗注意:

    • 子类父类之间是extends 继承关系,
    • 接口之间是 implements 实现关系。

    📕、请实现笔记本电脑使用USB鼠标,USB鼠标的例子

    1、USB接口:包含打开设备,关闭设备功能

    2、笔记本类:包含卡机功能、关机功能、使用USB设备功能

    3、鼠标类:实现USB接口,比具备点击功能

    4、键盘类:实现USB接口、并具备输入功能

     1、USB接口

    1. public interface IUSB {//实现USB接口
    2. void openDevice();//打开USB接口服务
    3. void closeDevice();//关闭USB接口服务
    4. }

    2、鼠标类,实现USB接口

    1. public class Mouse implements IUSB{
    2. @Override
    3. public void openDevice() {
    4. System.out.println("打开鼠标服务!");
    5. }
    6. @Override
    7. public void closeDevice() {
    8. System.out.println("关闭鼠标服务!");
    9. }
    10. public void click(){
    11. System.out.println("鼠标点击服务!");
    12. }
    13. }

    3、 键盘类,实现USB接口

    1. public class KeyBoard implements IUSB{
    2. @Override
    3. public void openDevice() {
    4. System.out.println("打开键盘服务!");
    5. }
    6. @Override
    7. public void closeDevice() {
    8. System.out.println("关闭鼠标服务!");
    9. }
    10. public void inPut(){
    11. System.out.println("键盘输入服务!");
    12. }
    13. }

    4、 笔记本类:使用USB设备

    1. public class Computer {
    2. public void open(){
    3. System.out.println("打开笔记本电脑");
    4. }
    5. public void close(){
    6. System.out.println("关闭笔记本电脑");
    7. }
    8. //所有的USB设备 在电脑上是不是可以使用呢?结果是只要满足这个接口的规范就行
    9. //这里用接口的引用做参数,这里接收的是那个类的对象,usb这个参数不知道
    10. //这里发生向上转型,将传过来的参数,用接口的引用来接收,类型范围扩大,发生向上转型
    11. //传过来的参数为对象mouse和keyBoard,所以方法useDevice的参数usb可以代表的是mouse和keyBoard
    12. public void useDevice(IUSB usb){
    13. usb.openDevice();//打开传过来的对象的USB服务接口
    14. if(usb instanceof Mouse){//用instanceof关键字判断usb引用接收的是那个类的对象
    15. Mouse mouse = (Mouse)usb;//向下转型,将usb接口转型成为Mouse类,适合鼠标使用的接口
    16. mouse.click();//用鼠标的引用调用鼠标类的方法
    17. }else if(usb instanceof KeyBoard ){
    18. KeyBoard keyBoard = (KeyBoard)usb;//向下转型,将usb接口转型为KeyBoard类,适合键盘使用的接口
    19. keyBoard.inPut();
    20. }
    21. usb.closeDevice();//关闭传过来的对象USB服务接口
    22. }
    23. }

    提示:USB接口的功能是由电脑来实现,在computer类中判断使用那个接口服务。 

    5、测试类

    1. public class Test {
    2. public static void main(String[] args) {
    3. //创建对象
    4. Computer computer =new Computer();
    5. Mouse mouse = new Mouse();
    6. KeyBoard keyBoard =new KeyBoard();
    7. //调用接口方法
    8. //使用computer调用Computer类中的useDevice方法
    9. //给useDevice方法传递实际参数mouse和keyBoard
    10. computer.useDevice(mouse);
    11. System.out.println("==========");
    12. computer.useDevice(keyBoard);
    13. }
    14. }

    🍂🍂🍂解释一下代码的运行过程:

    1️⃣、在测试类中

    • 创建并且实例化三个类的对象
    • 通过computer(引用)调用Computer类中的useDevice(使用接口)的方法,将mouse和keyBoard(对象)传给useDevice方法.

    2️⃣、在computer类中

    • useDevice方法接收传过来的参数mouse和keyBoard,方法useDevice 的参数usb代表的是mouse 和keyBoard。
    • 依次用来判断打开的是谁的usb接口服务(openDevice).
    • 在通过if语句判断是mouse还是keyBoard,用来调用相应的功能方法,以保证在打开mouse或者keyBoard的usb接口服务后,可以正常使用mouse和keyBoard的功能。

    3️⃣、Mouse和KeyBoard类中

    • 接口中的方法在Mouse和KeyBoard类中都得重写,以保证动态绑定的发生,来调用相应的对象mouse和keyBoard的方法。

    2.4、接口的特性

    1️⃣、使用关键字 interface 来定义接口

    1. interface IShape {
    2. public abstract void draw();//抽象方法
    3. }

    2️⃣、接口类型是一种引用类型,但是不能直接new接口的对象

     3️⃣、接口当中的成员默认是public static final

    ❗❗❗ 注意:在接口中定义成员变量,都要进行初始化,不然编译器会报错。

     4️⃣、接口当中每一方法都是 public抽象方法,即接口中的方法会被隐式的指定 public abstract(只能是 public abstract,其他修饰符都会报错

     5️⃣、接口当中的方法不能有具体的实现,但是从JDK8开始,可以写一个default修饰的方法

    1. interface IShape{
    2. public void func(){//error,编译器报错,在接口中方法不能有具体的实现
    3. System.out.println("fafaaf");
    4. }
    5. }
    1. interface IShape{
    2. default public void func(){//用default修饰的方法可以在接口中实现
    3. System.out.println("fafaaf");
    4. }
    5. }

       ❗❗❗提示:在接口中被default修饰的方法可以在子类中不用重写。 

    6️⃣、接口中不能有静态代码块和构造方法

    ✨接口中不能有构造方法:构造方法的主要作用是在创建对象的时候,对类中的属性进行初始化;但是在接口中,成员变量被public static final 修饰是静态常量,在编写的时候已经给赋值。

    7️⃣、接口需要被类实现,使用关键字 implements

    1. interface IShape{// 使用关键字 interface 类定义接口
    2. void draw();//接口当中的方法默认为 public abstract 修饰
    3. }
    4. class Rect implements IShape{//使用 implement 实现类与接口的关系
    5. @Override
    6. public void draw() {
    7. System.out.println("矩形");
    8. }
    9. }

    8️⃣、接口当中可以有 static 修饰的方法

     9️⃣、重写接口中的方法时,不能使用默认的访问权限(default)

    1. public interface USB {
    2. //在接口中的方法默认是被public abstract修饰
    3. void openDevice();
    4. void closeDevice();
    5. }
    6. //在类中实现接口
    7. public class Mouse implements USB {
    8. //在Mouse类中方法不写public,类中的方法是被包权限(default)修饰的
    9. //但是重写的规则是,子类的方法的访问修饰权限 >= 父类的访问修饰权限
    10. //所以不能使用包访问权限(default)
    11. @Override
    12. void openDevice() {
    13. System.out.println("打开鼠标");
    14. }
    15. // ...
    16. }
    17. // 编译报错,重写USB中openDevice方法时,不能使用默认修饰符(default)
    18. // 正在尝试分配更低的访问权限; 以前为public

    🔟、接口虽然不是类,但是接口编译完成之后字节码文件的后缀格式也是 .class

    ⏸、如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类 

    🎉🎉🎉上述总结可以归类为:


    2.5、实现多个接口

    📕在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承但是一个类可以实现多个接口。

    下面通过类来表示一组动物。

    1. abstract class Animal{//建立抽象类
    2. public String name;
    3. public Animal(String name){//构造方法
    4. this.name = name;
    5. }
    6. }

    例外我们在提供一组接口,分别表示"会飞的","会跑的","会游泳的"。

    1. interface IRunning{
    2. void run();
    3. }
    4. interface ISwimming{
    5. void swim();
    6. }
    7. interface IFly{
    8. void fly();
    9. }

    接下来创建几个具体的动物

    狗:是会跑的

    1. class Dog extends Animal implements IRunning{
    2. @Override
    3. public void run() {
    4. System.out.println(name+" 正在四条狗腿跑!");
    5. }
    6. public Dog(String name){
    7. super(name);
    8. }
    9. }

    鱼:是会游泳的

    1. class Fish extends Animal implements ISwimming{
    2. @Override
    3. public void swim() {
    4. System.out.println(name+" 正在游泳!");
    5. }
    6. public Fish(String name){
    7. super(name);
    8. }
    9. }

    鸟:是会飞的

    1. class Bird extends Animal implements IFly{
    2. @Override
    3. public void fly() {
    4. System.out.println(name+" 正在飞!");
    5. }
    6. public Bird(String name) {
    7. super(name);
    8. }
    9. }

    鸭子:是会飞,游泳,跑的

    1. class Duck extends Animal implements IRunning,ISwimming,IFly{
    2. @Override
    3. public void run() {
    4. System.out.println(name+" 正在用两条腿跑!");
    5. }
    6. @Override
    7. public void swim() {
    8. System.out.println(name+" 正在用两条腿游泳!");
    9. }
    10. @Override
    11. public void fly() {
    12. System.out.println(name +"正在用翅膀飞!");
    13. }
    14. public Duck(String name) {
    15. super(name);
    16. }
    17. }

    测试类

    1. public class Test {
    2. public static void walk(IRunning iRunning){
    3. iRunning.run();
    4. }
    5. public static void swim(ISwimming iSwimming){//向上转型
    6. iSwimming.swim();
    7. }
    8. public static void main(String[] args) {
    9. walk(new Dog("旺财"));
    10. walk(new Duck("唐老鸭"));
    11. walk(new Robot());
    12. System.out.println("=======");
    13. swim(new Fish("七秒"));
    14. swim(new Duck("唐老鸭2号"));
    15. }
    16. }

    运行结果

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

    • 继承表达的含义是 is - a 语义。
    • 接口表达的含义是 具有xxx 特性。

    狗是一种动物,具有会跑的特性。

    鱼是一种动物,具有会游泳的特性。

    鸟是一种动物,具有会飞的特性 

    ❗❗❗ 这样设计有什么好处呢?时刻牢记多态的好处,让程序员忘记类型,有了接口之后类的使用者就不必关注具体类型,而是只关注某个类是否具有某种能力。

    例如:设计一个机器人类,他不继承抽象类,只是实现一个接口

    1. //用机器人类实现IRunning这个接口,可以不用管他是不是一个动物,他只要实现IRunning这个接口规范的动作就行
    2. class Robot implements IRunning{
    3. @Override
    4. public void run() {
    5. System.out.println(" 机器人正在用机器腿跑!");
    6. }
    7. }

    ❗❗❗提示:当子类继承父类并且实现接口时,应注意

    ❌、当先实现接口,后继承,编译器会报错。


     2.6、接口之间的继承(可以认为是接口的拓展)

    相当于将多个接口合并在一起

    🎉✨在Java中,类和类之间是单继承的一个类可以实现多个接口接口与接口之间可以多继承。即:用接口可以达到多继承的目的。

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

    🧨来看这个代码:这里只是为了说明知识点,没有将接口中的方法在重写时具体实现。

    1. interface A{
    2. void funcA();
    3. }
    4. interface B{
    5. void funcB();
    6. }
    7. interface C extends A,B{
    8. void funcC();
    9. }
    10. class CC implements C{
    11. @Override
    12. public void funcA() {
    13. }
    14. @Override
    15. public void funcB() {
    16. }
    17. @Override
    18. public void funcC() {
    19. }
    20. }
    21. public class Test2 {
    22. }

    ❓❓❓看见上述的代码,我们会产生一种疑问,直接写三个接口就行,为什么还要继承? 

    • 接口的继承只是接口使用的一种方法,你可以用三个接口单独写一个规范,也可以在一个接口中写三个规范,
    • 你可以在类实现接口时,实现单独的三个接口,也可以实现一个继承的接口


  • 相关阅读:
    宝塔安装rabbitMQ实战
    环境响应性介孔二氧化硅复合微球/二氧化硅层状双氢氧化物微球的相关制备
    MMDetection3D的一些函数的介绍和说明
    新手如何备考学习PMP?
    PPT简明
    【使用 BERT 的问答系统】第 6 章 :BERT 模型应用:其他任务
    Ant Design Pro从零到一(认识AntD)
    华为od项目
    SpringBoot中使用Redisson分布式锁的应用场景-多线程、服务、节点秒杀/抢票处理
    ES7新特性深度解析:提升JavaScript开发效率的利器
  • 原文地址:https://blog.csdn.net/m0_73067372/article/details/128111094