【JAVASE】抽象类 + 接口

晚霞与玫瑰共绘浪漫 你是我藏在心底的爱意泛滥

在这里插入图片描述
大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍JAVASE中的 抽象类 + 接口,干货满满哟。(以下结果均在IDEA中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🥇🥈🥉

废话不多说,直接进入我们的文章。



一.🥇 抽象类

在上一篇博客我们讲多态的时候,在打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为抽象类

1.1🥈 语法规则

abstract class Shape { 
 abstract public void draw(); 
}

在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.

注意事项

1. 抽象类不能直接实例化.

Shape shape = new Shape(); 
// 编译出错
//Error:(30, 23) java: Shape是抽象的; 无法实例化

2. 抽象方法不能是 private 的

abstract class Shape { 
 abstract private void draw(); 
} 
// 编译出错
//Error:(4, 27) java: 非法的修饰符组合: abstract和private

3. 抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用

abstract class Shape { 
 abstract public void draw(); 
 void func() { 
 System.out.println("func"); 
 } 
} 
class Rect extends Shape { 
     @Override
    public void draw() {
        System.out.println("♦");
    }
} 
public class Test { 
 public static void main(String[] args) { 
 Shape shape = new Rect(); 
 shape.func(); 
 } 
} 
// 执行结果
//func

4.因为不能被实例化,所以,这个抽象类,其实只能被继承

abstract class Shape {
    public int a;
    public void func() {
        System.out.println("测试普通方法");
    }
    public abstract void draw();//抽象方法
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

5.一个普通类,继承了一个抽象类,那么这个普通类当中,需要重写这个抽象类中所有的抽象方法
6.抽象类最大的作用就是被继承

7.一个抽象类A如果继承了另一个抽象类B,那么A可以不实现抽象父类B的继承方法

bstract class Shape {
    public int a;
    public void func() {
        System.out.println("测试普通方法");
    }
    public abstract void draw();//抽象方法
}

abstract class A extends Shape{
    public abstract void funcA();//未实现父类的方法,但不报错
}

但我们要记住一句话,出来混迟早是要还的😂😂😂

8.结合第7点,当A类再次被一个普通类继承后,那么A和B两个抽象类当中的抽象方法,必须被重写

abstract class Shape {
    public int a;
    public void func() {
        System.out.println("测试普通方法");
    }
    public abstract void draw();//抽象方法
}

abstract class A extends Shape{
    public abstract void funcA();
}
class B extends A {
    @Override
    public void funcA(){

    }
    @Override
    public void draw(){

    }
}

如果未写全则报错,也就是我们上面说的出来混,迟早是要还的

9.抽象类不能被final所修饰,抽象方法也不可以被final修饰

1.2🥈 抽象类中的非抽象成员

既然我们上面说到抽象类无法被实例化,那么它里边的其它成员变量不是毫无意义吗——当然不是的,它虽然无法被实例化,但我们依然可以通过构造方法使用里边的成员变量,这也为我们具体实现一些抽象类的场景提供了便利

abstract class User {
    private String name;

    public User(String name) {
        this.name = name;
    }
    public abstract void func();

    public String getName() {

        return this.name;
    }
}

class Admin extends User{

    public Admin(String name){
        super(name);
    }

    @Override
    public void func(){

    }
}
public class Test2 {
    public static void main(String[] args) {
        User user = new Admin("bit");
        System.out.println(user.getName());
    }
}

1.3🥈 抽象类的作用

抽象类存在的最大意义就是为了被继承 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法。

那么我们可能会想, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

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

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

二.🥇 接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量

2.1🥈 语法规则

在刚才的打印图形的示例中, 我们的父类 Shape 并没有包含别的非抽象方法, 也可以设计成一个接口

interface IShape { 
 void draw(); 
} 
class Cycle implements IShape { 
 @Override 
 public void draw() { 
 System.out.println("○"); 
 } 
} 
public class Test { 
 public static void main(String[] args) { 
 IShape shape = new Rect(); 
 shape.draw(); 
 } 
}

定义

  1. 使用 interface 定义一个接口
  2. 接口中的方法一定是抽象方法, 因此可以省略 abstract
  3. 接口中的方法一定是 public, 因此可以省略 public
  4. Cycle 使用 implements 继承接口. 此时表达的含义不再是 “扩展”, 而是 “实现”
  5. 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.
  6. 接口不能单独被实例化.

扩展(extends) vs 实现(implements)
扩展指的是当前已经有一定的功能了, 进一步扩充功能.
实现指的是当前啥都没有, 需要从头构造出来

此外,我们还需要注意以下几点:

1. 接口当中的普通方法,不能有具体实现,非要实现,只能通过关键字default实现

interface IShape {
    public abstract void draw();//不加关键字也默认public abstract
    /*public void func(){//报错

    }*/
    default public void func(){
        System.out.println("默认的方法");
    }
}

2.接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static)

interface IShape { 
 void draw(); 
 public static final int num = 10; 
}

其中的 public, static, final 的关键字都可以省略. 省略后的 num 仍然表示 public 的静态常量.

提示:基本规则
1. 我们创建接口的时候, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 “形容词” 词性的单词.
3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性

在这里插入图片描述
3.当一个类实现了一个接口,就必须要重写接口当中的抽象方法

interface IShape {
    public abstract void draw();//不加关键字也默认public abstract
}
class Rect implements IShape {
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

4.当一个类实现一个接口,重写这个方法的时候,必须加上public

interface IShape { 
 abstract void draw() ; // 即便不写public,也是public 
} 
class Rect implements IShape { 
 void draw() { 
 System.out.println("□") ; //权限更加严格了,所以无法覆写。
 } 
}

上述用例中调用接口的类中覆写的方法默认为包访问权限,而接口中默认是public,故这段代码会报错

5.对于同一个子类只能继承一个父类,但是可以实现多个接口,接口间用逗号链接

interface IA {
     int A = 10;
     void funcA();//public abstract
}
interface IB {
    void funcB();
}
abstract class BClass {

}
class AClass extends BClass implements IA,IB{
    @Override
    public void funcA() {
        System.out.println("A::funcA()");
        System.out.println(A);
    }

    @Override
    public void funcB() {
        System.out.println("A::funcB()");
    }
}

6.接口和接口之间可以使用extends来操作它们的关系,此时意味:拓展

interface IA1{
    void funcA();
}

interface IB1 extends IA1{
    void funcB();
}

7.一个接口通过extends来拓展另一个接口的功能。此时当一个类D 通过implements来实现这个接口B的时候,不仅仅是B的抽象方法,它还要从接口A继承的接口A中的方法

interface IA1{
    void funcA();
}

interface IB1 extends IA1{
    void funcB();
}
class C implements IB1{
    @Override
    public void funcB() {
        System.out.println("接口B");
    }

    @Override
    public void funcA() {
        System.out.println("接口A");
    }
}

2.2🥈 实现多个接口

有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果.现在我们通过类来表示一组动物.😎😎😎

class Animal { 
 protected String name; 
 
 public Animal(String name) { 
 this.name = name; 
 } 
}

另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”.

interface IFlying { 
 void fly(); 
} 
interface IRunning { 
 void run(); 
} 
interface ISwimming { 
 void swim(); 
}

接下来我们创建几个具体的动物,猫, 是会跑的.

class Cat extends Animal implements IRunning { 
 public Cat(String name) { 
 super(name); 
 } 
 @Override 
 public void run() { 
 System.out.println(this.name + "正在用四条腿跑"); 
 } 
}

鱼, 是会游的.

class Fish extends Animal implements ISwimming { 
 public Fish(String name) { 
 super(name); 
 } 
 @Override 
 public void swim() { 
 System.out.println(this.name + "正在用尾巴游泳"); 
 } 
}

青蛙, 既能跑, 又能游(两栖动物)

class Frog extends Animal implements IRunning, ISwimming { 
 public Frog(String name) { 
 super(name); 
 } 
 @Override 
 public void run() { 
 System.out.println(this.name + "正在往前跳"); 
 } 
 @Override 
 public void swim() { 
 System.out.println(this.name + "正在蹬腿游泳"); 
 } 
}

提示, IDEA 中使用 ctrl + i 快速实现接口
还有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”

class Duck extends Animal implements IRunning, ISwimming, IFlying { 
 public Duck(String name) { 
 super(name); 
 } 
 @Override 
 public void fly() { 
 System.out.println(this.name + "正在用翅膀飞"); 
 } 
 @Override 
 public void run() { 
 System.out.println(this.name + "正在用两条腿跑"); 
 } 
 @Override 
 public void swim() { 
 System.out.println(this.name + "正在漂在水上"); 
 } 
}

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性

猫是一种动物, 具有会跑的特性.
青蛙也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞

这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力.😎😎😎
例如, 现在实现一个方法, 叫 “散步”

 public static void runFunc(IRunning iRunning) {
        iRunning.run();
    }

在这个 walk 方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行

public static void main(String[] args) {
        runFunc(new Duck("鸭子"));
        runFunc(new Frog("青蛙"));
    }
// 执行结果
//我带着伙伴去散步
//小猫正在用四条腿跑
//我带着伙伴去散步
//小青蛙正在往前跳

甚至参数可以不是 “动物”, 只要会跑就OK

class Robot implements IRunning { 
 private String name; 
 public Robot(String name) { 
 this.name = name; 
 } 
 @Override 
 public void run() { 
 System.out.println(this.name + "正在用轮子跑"); 
 } 
}
 public static void main(String[] args) {
        runFunc(new Roobot());
    }
//机器人正在用轮子跑    

想对大家说的话

家人们,学到这里我们已经肝完了JAVA 抽象类 + 接口了,相信大家对抽象类和接口更加了解了🥳🥳🥳后续新一会持续更JAVA的有关内容,学习永无止境,技术宅,拯救世界!
在这里插入图片描述

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Corwttaml

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值