抽象类和接口都用于抽象化具体对象的,都不能直接实例化,但是两者的侧重点不同:抽象类只要用来抽象类型,表示这个对象是什么;接口主要用来抽象功能,表示这个对象能做什么。
接口是一系列方法的声明,是一些方法特征的集合。一个接口只有方法的声明没有方法的实现。
目录
📖在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描 绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类.

在上述的例子中,我们发现,父类Animal中的bark()方法好像并没有什么实际工作,主要的叫声打印是由Animal的各种子类的bark()方法来完成的.像这种没有实际工作的方法,我们可以把它设计成一个抽象方法(abstract method),包含抽象方法的类我们称为抽象类(abstract class).
📕在Java中,一个类如果被abstract修饰称为抽象类,抽象类中被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
- // 抽象类:被abstract修饰的类
- public abstract class Shape {
- // 抽象方法:被abstract修饰的方法,没有方法体
- abstract public void draw();
- abstract void calcArea();
-
- // 抽象类也是类,也可以增加普通方法和属性
- public double getArea(){
- return area;
- }
- protected double area; // 面积
- }
❗❗❗注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
1️⃣、抽象类使用abstract修饰类

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

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

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

📑一个抽象类,不一定包含抽象方法;是一个抽象方法必须在抽象类中。
5️⃣、当一个普通类继承了抽象类,必须重写抽象类当中的抽象方法!!!
- abstract class Shape{
- //抽象方法
- public abstract void draw();
- }
- class Circle extends Shape {
- //重写抽象方法
- @Override
- public void draw(){
- System.out.println("⚪");
- }
- }
6️⃣、抽象类存在的最大的意义,就是被继承!!!
7️⃣、抽象方法不能被private,final,static修饰!!!(换句话说抽象方法要满足重写的规则)
📜抽象方法不能被private修饰
- abstract private void draw()//因为它的范围只能在自己类的内部使用,外面引用不到,也就
- //等于这个方法不存在。)
📜抽象方法不能被final和static修饰,因为抽象方法要被子类重写
- abstract class Shape{
- //抽象方法
- final public abstract void draw();//被final修饰抽象方法
- //final修饰的方法是不能被改变的,就像被final修饰的变量,会成为常量,常量不能被修改
- }
- abstract class Shape{
- //抽象方法
- abstract public static void draw();//被static修饰的方法是静态方法,是类方法,是
- //属于Shape (父类) 的,子类重写不了。
-
- }
8️⃣、当一个子类没有重写抽象的父类的方法,可以把当前子类变为abstract修饰。
🧨情景一:在子类中重写父类抽象方法
- abstract class Shape{
- //抽象方法
- public abstract void draw();
- }
- class Cycle extends Shape {
- //重写抽象方法
- @Override
- public void draw() {
- System.out.println("⚪");
- }
- }
🧨情景二:在子类中没有重写抽象方法,那子类有abstract修饰,成为抽象类
- abstract class Shape{
- //抽象方法
- public abstract void draw();
- }
- abstract class Cycle extends Shape {
-
- }
9️⃣、抽象类必须被继承、并且继承之后子类要重写父类中的抽象方法。
在第8条中,虽然(Cycle)中没有重写父类(Shape)的抽象方法(draw),但是在Cycle类的子类(A)中还得重写draw方法。
- abstract class Shape{
- //抽象方法
- public abstract void draw();
- }
- abstract class Cycle extends Shape {
-
- }
- class A extends Cycle {
- @Override
- public void draw() {
- System.out.println("A!");
- }
- }
🔟、抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
⏸、抽象类中可以有构造方法,供子类创建对象,初始化父类的成员变量
- abstract class B {//抽象类
- public int a;
- public int b;
- public B(){//父类无参构造方法
-
- }
- public B(int a, int b) {//父类有参构造方法
- this.a = a;
- this.b = b;
- }
-
- public void func(){
-
- }
- }
- class C extends B{
- public C(){//子类无参构造方法
- super(1,2);
- }
- }
-
| 抽象类 | 普通类 |
| 抽象类不能实例化 | 普通类可以实例化 |
| 抽象类中可以包含非抽象方法和抽象方法 | 只能包含非抽象方法 |
🔜抽象类本身不能被实例化,想要使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。
那么你是否会有这样的疑问:普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
🔜使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
很多语法存在的意义都是为了 "预防出错", 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不 就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.
充分利用编译器的校验, 在实际开发中是非常有意义的.
📖在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等。


电脑的USB口上,可以插:U盘、鼠标、键盘...所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备
🔜通过上述例子可以看出:接口就是公共的行为给范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
- 在Java中接口是一个全部由抽象方法组成的集合,接口需要用interface定义,里面只能有抽象的方法和常量。
- 接口体现的是事务扩展的功能,在Java中,类定义了一个实体,包阔实体的属性,实体的行为。而接口定义了一个实体可能发生的动作,只有一个声明,没有具体的行为。
- 当一个方法在很多类中有不同的体现的时候,就可以将这个方法抽象出来做成一个接口。
- 接口内只能有不可修改的全局常量,只能有抽象的方法,接口没有构造方法。
📕接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字就定义了一个接口。
- public interface 接口名称{
- public abstract void method1();//public abstract 是固定搭配,可以不写
- public void method2();
- abstract void method3();
- void method4();
- //注意:因为在接口中public abstract是固定搭配,可以不写,所以在接口中上述写法都是抽象方
- //法,更推荐方式4,代码更简洁。
- }
❗❗❗提示:
1、创建接口时,接口的命名一般以大小字母 I 开头。
2、接口的命名一般使用 "形容词" 词性的单词。
3、阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性。
📕接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
- public class 类名称 implements 接口名称{
- // ...
- }
❗❗❗注意:
- 子类和父类之间是extends 继承关系,
- 类与接口之间是 implements 实现关系。
📕、请实现笔记本电脑使用USB鼠标,USB鼠标的例子
1、USB接口:包含打开设备,关闭设备功能
2、笔记本类:包含卡机功能、关机功能、使用USB设备功能
3、鼠标类:实现USB接口,比具备点击功能
4、键盘类:实现USB接口、并具备输入功能
1、USB接口
- public interface IUSB {//实现USB接口
- void openDevice();//打开USB接口服务
- void closeDevice();//关闭USB接口服务
- }
2、鼠标类,实现USB接口
- public class Mouse implements IUSB{
- @Override
- public void openDevice() {
- System.out.println("打开鼠标服务!");
- }
-
- @Override
- public void closeDevice() {
- System.out.println("关闭鼠标服务!");
- }
- public void click(){
- System.out.println("鼠标点击服务!");
- }
- }
3、 键盘类,实现USB接口
- public class KeyBoard implements IUSB{
- @Override
- public void openDevice() {
- System.out.println("打开键盘服务!");
- }
-
- @Override
- public void closeDevice() {
- System.out.println("关闭鼠标服务!");
- }
- public void inPut(){
- System.out.println("键盘输入服务!");
- }
- }
4、 笔记本类:使用USB设备
- public class Computer {
- public void open(){
- System.out.println("打开笔记本电脑");
- }
- public void close(){
- System.out.println("关闭笔记本电脑");
- }
- //所有的USB设备 在电脑上是不是可以使用呢?结果是只要满足这个接口的规范就行
- //这里用接口的引用做参数,这里接收的是那个类的对象,usb这个参数不知道
- //这里发生向上转型,将传过来的参数,用接口的引用来接收,类型范围扩大,发生向上转型
- //传过来的参数为对象mouse和keyBoard,所以方法useDevice的参数usb可以代表的是mouse和keyBoard
- public void useDevice(IUSB usb){
- usb.openDevice();//打开传过来的对象的USB服务接口
- if(usb instanceof Mouse){//用instanceof关键字判断usb引用接收的是那个类的对象
- Mouse mouse = (Mouse)usb;//向下转型,将usb接口转型成为Mouse类,适合鼠标使用的接口
- mouse.click();//用鼠标的引用调用鼠标类的方法
- }else if(usb instanceof KeyBoard ){
- KeyBoard keyBoard = (KeyBoard)usb;//向下转型,将usb接口转型为KeyBoard类,适合键盘使用的接口
- keyBoard.inPut();
- }
- usb.closeDevice();//关闭传过来的对象USB服务接口
- }
- }
提示:USB接口的功能是由电脑来实现,在computer类中判断使用那个接口服务。
5、测试类
- public class Test {
- public static void main(String[] args) {
- //创建对象
- Computer computer =new Computer();
- Mouse mouse = new Mouse();
- KeyBoard keyBoard =new KeyBoard();
- //调用接口方法
- //使用computer调用Computer类中的useDevice方法
- //给useDevice方法传递实际参数mouse和keyBoard
- computer.useDevice(mouse);
- System.out.println("==========");
- computer.useDevice(keyBoard);
- }
- }
🍂🍂🍂解释一下代码的运行过程:
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的方法。
1️⃣、使用关键字 interface 来定义接口
- interface IShape {
- public abstract void draw();//抽象方法
- }
2️⃣、接口类型是一种引用类型,但是不能直接new接口的对象

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

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

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

5️⃣、接口当中的方法不能有具体的实现,但是从JDK8开始,可以写一个default修饰的方法
- interface IShape{
- public void func(){//error,编译器报错,在接口中方法不能有具体的实现
- System.out.println("fafaaf");
- }
- }
- interface IShape{
- default public void func(){//用default修饰的方法可以在接口中实现
- System.out.println("fafaaf");
- }
- }
❗❗❗提示:在接口中被default修饰的方法可以在子类中不用重写。
6️⃣、接口中不能有静态代码块和构造方法
✨接口中不能有构造方法:构造方法的主要作用是在创建对象的时候,对类中的属性进行初始化;但是在接口中,成员变量被public static final 修饰是静态常量,在编写的时候已经给赋值。
7️⃣、接口需要被类实现,使用关键字 implements
- interface IShape{// 使用关键字 interface 类定义接口
- void draw();//接口当中的方法默认为 public abstract 修饰
- }
- class Rect implements IShape{//使用 implement 实现类与接口的关系
- @Override
- public void draw() {
- System.out.println("矩形");
- }
- }
8️⃣、接口当中可以有 static 修饰的方法

9️⃣、重写接口中的方法时,不能使用默认的访问权限(default)
- public interface USB {
- //在接口中的方法默认是被public abstract修饰
- void openDevice();
- void closeDevice();
- }
- //在类中实现接口
- public class Mouse implements USB {
- //在Mouse类中方法不写public,类中的方法是被包权限(default)修饰的
- //但是重写的规则是,子类的方法的访问修饰权限 >= 父类的访问修饰权限
- //所以不能使用包访问权限(default)
- @Override
- void openDevice() {
- System.out.println("打开鼠标");
- }
- // ...
- }
- // 编译报错,重写USB中openDevice方法时,不能使用默认修饰符(default)
- // 正在尝试分配更低的访问权限; 以前为public
🔟、接口虽然不是类,但是接口编译完成之后字节码文件的后缀格式也是 .class
⏸、如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
🎉🎉🎉上述总结可以归类为:

📕在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。
下面通过类来表示一组动物。
- abstract class Animal{//建立抽象类
- public String name;
- public Animal(String name){//构造方法
- this.name = name;
- }
- }
例外我们在提供一组接口,分别表示"会飞的","会跑的","会游泳的"。
- interface IRunning{
- void run();
- }
- interface ISwimming{
- void swim();
- }
- interface IFly{
- void fly();
- }
接下来创建几个具体的动物
狗:是会跑的
- class Dog extends Animal implements IRunning{
- @Override
- public void run() {
- System.out.println(name+" 正在四条狗腿跑!");
- }
-
- public Dog(String name){
- super(name);
- }
- }
鱼:是会游泳的
- class Fish extends Animal implements ISwimming{
- @Override
- public void swim() {
- System.out.println(name+" 正在游泳!");
- }
- public Fish(String name){
- super(name);
- }
- }
鸟:是会飞的
- class Bird extends Animal implements IFly{
- @Override
- public void fly() {
- System.out.println(name+" 正在飞!");
- }
-
- public Bird(String name) {
- super(name);
- }
- }
鸭子:是会飞,游泳,跑的
-
- class Duck extends Animal implements IRunning,ISwimming,IFly{
- @Override
- public void run() {
- System.out.println(name+" 正在用两条腿跑!");
- }
-
- @Override
- public void swim() {
- System.out.println(name+" 正在用两条腿游泳!");
- }
-
- @Override
- public void fly() {
- System.out.println(name +"正在用翅膀飞!");
- }
-
- public Duck(String name) {
- super(name);
- }
- }
测试类
- public class Test {
- public static void walk(IRunning iRunning){
- iRunning.run();
- }
- public static void swim(ISwimming iSwimming){//向上转型
- iSwimming.swim();
- }
- public static void main(String[] args) {
- walk(new Dog("旺财"));
- walk(new Duck("唐老鸭"));
- walk(new Robot());
- System.out.println("=======");
- swim(new Fish("七秒"));
- swim(new Duck("唐老鸭2号"));
- }
- }
运行结果

📚、 上面的代码展示了Java面向对象编程中最常见的用法:一个类继承一个父类,同时实现多个接口。
狗是一种动物,具有会跑的特性。
鱼是一种动物,具有会游泳的特性。
鸟是一种动物,具有会飞的特性
❗❗❗ 这样设计有什么好处呢?时刻牢记多态的好处,让程序员忘记类型,有了接口之后,类的使用者就不必关注具体类型,而是只关注某个类是否具有某种能力。
例如:设计一个机器人类,他不继承抽象类,只是实现一个接口
- //用机器人类实现IRunning这个接口,可以不用管他是不是一个动物,他只要实现IRunning这个接口规范的动作就行
- class Robot implements IRunning{
- @Override
- public void run() {
- System.out.println(" 机器人正在用机器腿跑!");
- }
- }
❗❗❗提示:当子类继承父类并且实现接口时,应注意
❌、当先实现接口,后继承,编译器会报错。
相当于将多个接口合并在一起
🎉✨在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口,达到复用的效果,使用 extends关键字。
🧨来看这个代码:这里只是为了说明知识点,没有将接口中的方法在重写时具体实现。
- interface A{
- void funcA();
- }
- interface B{
- void funcB();
- }
- interface C extends A,B{
- void funcC();
- }
- class CC implements C{
- @Override
- public void funcA() {
-
- }
-
- @Override
- public void funcB() {
-
- }
-
- @Override
- public void funcC() {
-
- }
- }
- public class Test2 {
-
- }
❓❓❓看见上述的代码,我们会产生一种疑问,直接写三个接口就行,为什么还要继承?
- 接口的继承只是接口使用的一种方法,你可以用三个接口单独写一个规范,也可以在一个接口中写三个规范,
- 你可以在类实现接口时,实现单独的三个接口,也可以实现一个继承的接口