「JavaSE」抽象类&接口1:基本概念

🎇个人主页:Ice_Sugar_7

🎇所属专栏:快来卷Java啦

🎇欢迎点赞收藏加关注哦!

抽象类&接口1

  • 🍉抽象类
    • 🍌特性
    • 🍌应用
    • 🍉接口
      • 🍌接口的特性
      • 🍌使用接口

        🍉抽象类

        在面向对象的概念中,所有的对象都是通过类来描绘的,

        但是不是所有的类都是用来描绘对象的。如果一个类没有包含足够的信息来描绘一个具体的对象,那它就叫作抽象类

        比如说,Animal是动物类,每种动物都有不同的叫声,但由于Animal并不是具体的动物,所以其内部的bark方法没办法实现

        而对于Dog类和Cat类,首先猫和狗都属于动物,那么它们和Animal类就是继承关系,而且它们都是具体的动物,所以它们的bark方法可以实现

        上一篇文章打印图形的例子中,我们发现,父类 Shape 中的 draw 方法其实啥都没做,绘制图形的工作都是由Shape的子类的draw方法来完成的

        public class Shape { public Shape shape;
            public void draw() { System.out.println("画一个图形");
            }
        }
        

        像这种没有实际工作的方法,我们可以把它设计成一个抽象方法(abstract method),包含抽象方法的类我们称为抽象类

        abstract class Shape { int a;
            int b = 10;
            private double area;
            
            //抽象方法
            abstract public void draw();
            
            //可以在抽象类中实现普通方法
            public double getArea() { //得到绘制的图形的面积
                return this.area;
            }
        }
        

        在Java中,一个类如果被 abstract 修饰,那就称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体(即没有方法体

        抽象类作为一种类,里面自然也可以添加普通方法和成员变量

        🍌特性

        • 不能实例化

          刚才我们说抽象类无法描绘一个具体的对象,所以它不能实例化对象

        • 抽象方法不能用private修饰

          要被子类重写,肯定不能设为私有

        • 抽象方法不能被final和static修饰

          因为被final或static修饰的方法不能被重写,但是抽象方法在继承时要求子类重写该方法,所以final、static和abstract是天敌

        • 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰

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

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

          🍌应用

          因为抽象类不能实例化,所以要想使用的话,就要创建该抽象类的子类,然后让子类重写抽象类中的抽象方法

          不过你可能会说:普通的类也可以被继承呀,普通的方法也可以被重写呀,为啥非得用抽象类和抽象方法呢?

          因为使用抽象类相当于多了一重编译器的校验

          比如上面画图形的例子,如果使用普通类进行继承,那不小心忘记重写子类的draw方法,那此时编译器是不会报错的

          反之,如果是抽象类,编译器就会“逼你”重写

          很多语法存在的意义就是为了“预防出错”

          比如我们前面学过的 final 就是这样,创建的变量不希望被用户修改,所以加上 final ,这样就能够在不小心被修改的时候,让编译器及时提醒我们

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


          🍉接口

          如果将抽象类再进一步抽象化,就成了接口

          抽象类中可以包含成员变量,但是接口不可以(不过仍然可以包含静态变量)

          抽象类中可以实现非抽象方法,但是接口只能包含方法的声明。如果要实现方法,那只能实现静态方法

          下面来看怎么实现一个接口

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

          public abstract编译器会默认给你加上的,所以不用写

          关于接口的命名,需要注意:

          1. 创建接口时,接口的命名一般以大写字母 I 开头
          2. 接口的命名一般使用“形容词”词性的单词
          3. 接口中的方法和属性不要加任何修饰符号,保持代码的简洁性

          接口有点类似C语言的头文件,因为头文件里面包含的就是函数声明,要具体实现这个函数的话,就得在其他源文件中实现

          🍌接口的特性

          1. 接口是一种引用类型,但是不能直接new接口的对象
          2. 接口中每一个方法都是public的抽象方法,也就是说接口中每个方法默认都是public abstract修饰的。不能改为其他,否则会报错
          3. 接口中的方法不能在接口中实现的,只能由实现接口的类来实现
          4. 重写接口中方法时,不能使用默认的访问权限(default),一定要指定用public
          5. 接口中可以有变量,它们默认会被 public static final 修饰
          public interface test2 { int a = 2;
              final public static double b = 3.0;
          }
          
          1. 接口中不能有静态代码块和构造方法
          2. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
          3. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

          🍌使用接口

          接口不能直接使用,必须要有一个“实现类”来实现该接口中所有抽象方法,实现接口需要用到关键字implements

          接口是一种类型,它可以引用实现该接口的具体的类型

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

          举个例子:

          实现笔记本电脑使用USB鼠标和键盘

          1. USB接口:包含打开设备、关闭设备功能
          2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
          3. 鼠标类:实现USB接口,并具备点击功能
          4. 键盘类:实现USB接口,并具备输入功能

          这里的USB接口就是一个接口,它提供两种功能:连接设备、断开连接

          public interface USB { void openDevice();
              void closeDevice();
          }
          

          笔记本类需要根据接入接口的类(鼠标、键盘类),来调用相应的类的功能

          public class Computer{ public void powerOn() { System.out.println("电脑开机");
              }
              
              //因为鼠标类和键盘类都实现了USB接口,所以它们都能向上转型传参给usb
              public void useService(USB usb) { usb.openDevice();
                  if(usb instanceof Mouse) { Mouse mouse = (Mouse) usb;  //向下转型,使用鼠标类特有的方法
                      mouse.click();
                  } else if (usb instanceof Keyboard) { Keyboard keyboard = (Keyboard) usb;
                      keyboard.input();
                  }
                  usb.closeDevice();
              }
              
              public void powerOff() { System.out.println("电脑关机");
              }
          }
          

          鼠标和键盘实现基本的功能:

          //鼠标类实现USB接口
          public class Mouse extends Computer implements USB{ @Override
              public void openDevice() { System.out.println("连接鼠标");
              }
              public void click() { System.out.println("使用鼠标点击");
              }
              @Override
              public void closeDevice() { System.out.println("鼠标已断开连接");
              }
          }
          //键盘类实现USB接口
          public class Keyboard extends Computer implements USB{ @Override
              public void openDevice() { System.out.println("连接键盘");
              }
              public void input() { System.out.println("使用键盘输入");
              }
              @Override
              public void closeDevice() { System.out.println("键盘断开连接");
              }
          }
          

          接口解决了Java不支持多继承的问题

          以动物类为例,我们想写一个Dog类来继承动物类,狗会跑,也会游泳。但不是所有动物都会这两种行为,而Java不支持多继承,但是支持一个类实现多个接口,所以我们可以把动物的行为封装成一个个接口

          public class Animal { public String name;
              public int age;
          }
          //接口
          public interface IFly { void Fly();
          }
          public interface ISwimming { void swimming();
          }
          public class Bird extends Animal implements IFly{ public String color;
              @Override
              public void Fly() { System.out.println(this.name + "正在振动翅膀飞翔");
              }
          }
          //同时实现两个接口
          public class Dog extends Animal implements IRun,ISwimming{ public String color;
              @Override
              public void run() { System.out.println(this.name + "正在跑");
              }
              @Override
              public void swimming() { System.out.println(this.name + "正在游泳");
              }
          }
          

          小结:

          • 接口表达的是某个类具有某种特性。有了接口,我们在使用类的时候就不用关注具体类型,只需关注某个类是否具备某种能力
          • 方法的参数以接口类型作为形参,就可以接收所有实现这个接口的类类型(因为它们传参时会向上转型为接口类型)

            比如前面USB接口的例子,鼠标、键盘类都可以向上转型为USB类,然后实现多态