当前位置:网站首页>抽象类 or 接口

抽象类 or 接口

2022-08-09 21:35:00 牧..

抽象类 or 接口



1.回顾 前文 要点

学到这里 面向对象 的 三 大特征 我们 已经 学习过 了。

1.封装

2.继承

3.多态

下面我们 就先来复习一下。

1.啥是 封装 ?

封装 就是 将 对象 的 属性 和 实现的 细节 通过 关键字 private 给 隐藏 起来 ,对外 提供 特有的 get 和 set 方法来进行 访问,

2.啥是 继承 ?

继承 就是 通过 一个 已有 的 类的 基础上 派生 处 新类(如: 动物 类 就能 派生出 猫 类 和 狗 类 等等) 子类 继承 父类 的 特征 和 行为 ,使得 子类 对象 拥有 父类 的 实例 域 和 方法,

(简单来说 就是 对 共性 的 抽取, 使 子类 具有 父类 相同的 行为 ) , 使用 关键字 extends 来实现

如: A extends B A 就 是 子类 , B 就是 父类 属于 A is B 的 关系。

继承的 意义 : 提高 了 代码的 复用 性 。

3.啥是 多态 ?

多态是 一种 思想, 表示的 是 同一个行为具有 多个不同的表现形式 或 形态能力 。

如:黑白打印机和彩色打印机相同的打印行为却有着不同的打印效果,

但 光有 这句话 是 不能行 的 我们 还需要理解 啥是 向上转型,动态绑定 (重写)。

1.向上转型: 父类引用 引用 了 子类 对象。

2.动态绑定:通过 父类引用 调用 子类和 父类 同名 的 覆盖(重写) 方法。

注意: 在发生 动态 绑定时 一定 会 涉及到 重写 , 这个 也需要我们 重点理解.

重写特征
1.方法 名 相同
2. 参数类型 和 个数 相同,
3.返回值相同 (特例 : 返回值 构成 父子类 )。


重写 注意 事项:

1.static 修饰的 方法 不能 重写
2.final 修饰的 方法 不能 重写
3. private 修饰的 方法 不能重写
4.子类 重写 的 方法 访问 修饰 限定 需要 大于 等于 父类 访问 修饰 限定 (子类方法的访问权限要大于等于父类方法的访问权限)


动态 除了 上面 这些 比较 重要的 我们 还学习 了

静态 绑定 :通过方法的重载实现的。编译的时候,会根据你调用方法时,所给参数的类型和个数,来决定调用哪个同名方法

向下 转型 : 子类 引用父类 对象 , 因为 不太 安全 所以 不推荐 使用。

注意事项:

1.子类 继承 父类 , 子类 需要 在 构造方法中 加入 super(…) 来显示 调用 父类构造方法 , 帮助 父类 完成 构造 。

2.super 和 this 的 区别 : super 是 针对父类的引用, 而 this 针对 当前 对象的 引用 。

3.重写 和 重载 的 区别 :

重载重写
相同点:1.方法名相同1.方法名相同
不同点:2.返回值 可以不同2.返回值必须相同
特例:返回值构成父子类
不同点:3.参数列表不同
数据类型 ,顺序个数都不一样
3.参数列表相同
数据类型,顺序个数都相同
不同点:重载是没有权限要求的5.重写是有权限要求的
子类的 权限要大于等于父类
另外:父类被private修饰不能够重写


4.四种方法 权限 :

1.private被 private 修饰的 字段 或 方法,只能在 同一个类中使用,
如果 这个 类 被 继承, 被 private 修饰的 方法 或 字段 , 是 被 继承下来的 ,但无法访
2.default (包访问 权限)只要在 同一个包底下 都能够使用。
3.protected在 同一个包底下 随便使用, 但 其他包中需要是子类 才能 够 使用
4.public不管是同一个包 还是 不同 的 包 都能 够使用 。
注意上面 说的使用 是 被 这几种 修饰限定符 给 修饰的 字段 或 方法。

到此我们 就 回顾 完 了 , 下面 我们 就 来 使用 多态 来 引出 我们 接下来 学习 的 抽象类 。

抽象类

先来看 这段 代码 :

class Shape{
    
    public void drow(){
    
        System.out.println("Shape :: drow()");
    }
}
// Rect : 矩形
class Rect extends Shape{
    
    @Override
    public void drow() {
    
        System.out.println("矩形");
    }
}
// Flower : 花
class Flower extends Shape{
    
    @Override
    public void drow() {
    
        System.out.println("* 花");
    }
}
public class Test {
    

    public static void func(Shape shape){
    
        shape.drow();
    }
    public static void main(String[] args) {
    
        Rect rect = new Rect();
        func(rect);
        Flower flower = new Flower();
        func(flower);
    }
}

在这里插入图片描述

这里 我们 就 实现了 我们的 多态 , 通过 shape 这 一个 引用 , 表现出 了不同 中 状态 ,如 : 矩形 , 花 。

这里 你 有没有 想过 只要 父类 引用 了 子类 对象 , 调用 drow 方法 ,都会 发生 动态 绑定 ,原本 自己 的 drow 方法 实现的 功能 就没有 用 了 ,我们是否 可以 将 这个 方法 实现 省略 掉 呢?

这里就 可以 将 drow() 这个 方法 设置 为 抽象 方法 ,此时 包含抽象 方法的 类 我们 就称为 抽象 类 。

抽象类语法规则

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

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


此时 我们 被 abstract 修饰 的 Shape就 是 抽象类,

而 被 abstract 修饰 的 方法就 是 抽象 方法, 这个 方法就可以事项具体 类容。

下面我们 来 对比一下 抽象 类 普通类 的 区别:

1.抽象类 不能实例化

在这里插入图片描述


2.抽象类 与 普通 类 一样 可以 定义 普通 方法 和 成员变量。

在这里插入图片描述

这我们 虽然 不能 通过 new 这个 类来 创建 对象,但 抽象类 ,发明出来 就是 用来 继承的 , 我们 可以 通过 继承 , 来 调用 这些 普通 方法 或 成员变量。

如:

在这里插入图片描述

当我们尝试 继承 后 会发现 ,报红线 了 ,不要着急,这里 是因为 我们没有 重写 抽象 方法 。

注意: 当 继承 抽象类 的同时 我们 需要 重写 所有的抽象 方法。

也挺好想 :我们抽象 方法本来 什么 就没实现 ,如果 你不重写 ,别人 调用 这个 方法 有什么 用 呢? 还不如 不写。

在这里插入图片描述


重写 完 就没有 报错了 ,下面 继续 。

通过 子类 访问 ,父类 (父类 为 抽象 类 )的 普通 方法 :

abstract class Shape{
    
    abstract public void drow();
    public int a;
    public String b;
    public void func(){
    
        System.out.println("测试 抽象类 是否能 定义普通方法");
    }
}
class A extends Shape{
    
    @Override
    public void drow() {
    
        System.out.println("重写 的 抽象 方法");
    }
}
public class Test {
    

    public static void main(String[] args) {
    
        A a = new  A();
        a.func();
        a.drow();
    }
}


在这里插入图片描述

同样 的 我们 的 抽象类 ,也 是 可以 发生 向上绑定 ,和 多态的 , 这里 就 来 看一下。

1.向上 转型:

在这里插入图片描述

2.多态:

abstract class Shape{
    
    abstract public void drow();
    public int a;
    public String b;
    public void func(){
    
        System.out.println("测试 抽象类 是否能 定义普通方法");
    }
}
class A extends Shape{
    
    @Override
    public void drow() {
    
        System.out.println(" A 中 重写 的 抽象 方法");
    }
}
class B extends Shape{
    
    @Override
    public void drow() {
    
        System.out.println(" B 中 重写 的 抽象 方法");
    }
}
public class Test {
    
    
    public static void func(Shape shape){
    
        shape.drow();
    }

    public static void main(String[] args) {
    
        A a = new A();
        B b = new B();
        func(a);
        func(b);
    }
}

在这里插入图片描述


另外 : 我们 一个 抽象类 继承 一个 抽象 类 可以 不重写 抽象 方法 。


如: 抽象类 B 继承 了 抽象 类 A 此时 抽象 类 B 是 不需要 重写 抽象 A 的 抽象方法 (对比 一个 普通类 C 继承 抽象类 A ,就必须 重写抽象方法, 要不然 报错)
在这里插入图片描述


俗话 说的 父债子还 , 如果此时 一个 普通的 类 继承 了 这个抽象 类 B ,此时 这个子类 不仅 需要 重写 抽象类 A 的 抽象 方法 , 还需要 重写抽象 类B的 抽象方法。

1.只重写 了 抽象类 B 的 抽象 方法

在这里插入图片描述

2.重写了 抽象类 A 和 抽象 类 B 的 抽象 方法 。

在这里插入图片描述

如果 我们 在 拿一个 抽象类 继承 呢 ?

这样 就会 形成 套娃 ,这个 类 也 不需要 重写 父类 的 抽象 方法 ,知道 有 一个 普通类 继承 了 ,那么 就得 全部 重写 ,
在这里插入图片描述

在 来 一个 注意事项:

抽象 类是不能 被 final修饰的

之前我们 学过final,说过 其中 的 一种用法 , 被 final 修饰 的 类 是 不能 被 继承 的 ,我们的 抽象类 本 身就是 用来 被继承的 ,

final 就好比 与 火 , 而 抽象类 就好比水 , 两者 本身 就不 合 , 强行 放在 一起 肯定 会出问题。

在这里插入图片描述

既然 抽象 类 不能 被 final 修饰 ,那么 我们的抽象 方法 同理 也是 不能 被 final修饰的, 之前 还 提过 被final修饰的 方法 是不能 重写的 ,这样与 抽象方法 必须 重写 ,又有 冲突 。

在这里插入图片描述

看完上面这些 内容 你 能 总结 出什么 ?

下面 就 直接 抛出 总结。


总结 :

1、包含抽象方法的类,叫做抽象类。

2、什么是抽象类,一个没有具体实现方法,被abstract修饰

3、抽象类是不可以被实例化的。不能 new

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

5、抽象类当中,也可包含和普通类一样的成员和方法。

6、一个普通类,继承了一个抽象类,那么这个普通类当中,需要重写这个抽象类的所有的抽象方法。

7.抽象类的最大的作用,就是为了被继承

8、一个抽象类A,如果继承了一个抽象类B,那么这个抽象类A,可以不实现抽象父类B的抽象方法。

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

10、抽象类不能被final修饰,抽象方法也不可以被 final 修饰

同理 抽象 方法 不能 被 privatestatic 修饰 (导致 这个方法无法被重写)。

抽象类的 作用

抽象类 最大 的意义 就是为了 继承 ,

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

有些小伙伴 可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法
呢?

其实 使用 抽象类 相当于多了一重编译器的校验(如果你没有重写抽象方法,编译器会报错,提醒你)

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


抽象类 完了,接下来 学习 我们的 接口 。

接口


什么是 接口 ?

答: 接口是 对 公共的 行为规范 标准 (再实现 接口 时, 只要 符合 规范 标准 , 就可以 通用)。

再java 中 ,接口 可以看成 是 :多个类 的公共规范,是 一种 引用 数据类型 。

看完 上面 这些 概述 是不是 很 懵逼 下面 就通过 例子 来 解释 一下。

笔记本 上的 USB 接口 , 电源插座等 。

在这里插入图片描述

在这里插入图片描述

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

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

当满足 满足 这两个 接口 的规范 协议 ,就 能够 插入 其中使用 , 如果 你 强行 插入当我没说,

如: 有线鼠标 键盘 , 如果 他们的 线 的 插头 与 电脑 规定 的 USB接口 相同 此时 就能够 使用 ,不同那么 就不能使用。这里 就是 接口 规定了标准 范围,而我们的 鼠标 和 键盘 就 满足 这种 规范 ,就可以使用 这个 接口 。

知道 了 概念 下面就来 了解 一下 语法 规则 。

接口的 语法 规则



使用 关键字 interface 修饰 的类 就 是 接口

创建 完 了 接口,来 了解 一下我们的 接口 。



1.接口 当中 ,不能创建普通 方法

在这里插入图片描述


如果这 非要 创建 普通方法 需要 再 方法 前 加上 关键字 default ,表示 该方法 是此 接口 的 默认 方法

在这里插入图片描述


注意 : 在 JDK 1.8 开始 才 允许 接口 可以 实现 方法 , 但 必须是 被关键字 default 修饰的 方法 。

另外 : 在 接口 中 是 可以 存在 静态 方法 的 但 静态 方法 不能 被重写

在这里插入图片描述

除了 默认方法 , 静态 方法 , 接口 还 包括 抽象方法 ,

在这里插入图片描述

接口 与 抽象类 相比 之下, 接口 只能 包括 抽象 方法 或 接口 默认方法 , 字段 只能 包括 静态 常量, 而 抽象类 除了 抽象方法 , 还可以包含非抽象方法, 和字段. , 所以 说 接口 相比 抽象类 更进 一步 。

观察 上面 几张配图 ,有没有 发现 , 我们 创建 的 方法 中 的 关键字 public 都是 灰色 的 ,这里 意味 这 在 接口中 所有的 方法 ,都是默认 被 public 修饰 的。


2.在 接口当中所有 方法 默认 都是 被 public 修饰的 ,抽象方法 默认 是 被 public abstract 修饰的

在这里插入图片描述


尝试 使用 其他 访问 修饰 限定符 :

在这里插入图片描述

可以 看到 只有 public 修饰 , 或者 不写 (此时 会默认 为 是 public 修饰 )才能 够不报错 。


3.接口 同样不能实例 化对象

刚刚 说过 ,接口 是 抽象 类 的更进一步 , 抽象类 不能 实例化对象, 接口 也应该 不能 实例化对象。

下面演示 :

在这里插入图片描述


下面 就来 使用 一下我们的接口。

4.类 与 接口 之间 使用关键字 implements 来实现


模拟 场景: 皮卡丘使用 技能 A

1.通过 implements 实现 接口 ,同样需要 将 接口 中 所有的 抽象类 方法, 全部重写 ,(注意:默认方法,或 static 修饰的 方法 就 不需要 重写, 但 默认 方法 是 可以按照 自己的 意愿选着 重写 的 ,而static 修饰 的方法 无法被 重写)。

在这里插入图片描述


重写 完 drow 方法。

在这里插入图片描述

// 技能 A
interface A {
    
    void drow();
}

// 技能 B
interface B {
    
    void drow();
}

// 皮卡丘
class Pikachu implements A {
    
    @Override
    public void drow() {
    
        System.out.println("皮卡丘使用十万伏特");
    }
}

// 喷火龙
class Charizard {
    

}

public class Test {
    
    public static void main(String[] args) {
    
        Pikachu pikachu = new Pikachu();
        pikachu.drow();
    }
}


在这里插入图片描述

此时 就完成 了 我们 的 场景 模拟 。

同样我们 的接口 是可以 完成 我们 的向上 转型 的 , 既然 有 了 向上 转型 (父类 引用 引用 了 子类 对象) ,那么 子类 发生 重写 ,调用 重写 的方法 ,发生 动态绑定 ,最后 也 能够 完成 我们 的 多态 。

1.向上转型 和 动态绑定:

在这里插入图片描述

2.多态:

// 技能 A
interface A {
    
    void drow();
}

// 技能 B
interface B {
    
    void Skill();
}

// 皮卡丘
class Pikachu implements A {
    
    @Override
    public void drow() {
    
        System.out.println("皮卡丘使用十万伏特");
    }
}

// 喷火龙
class Charizard implements A,B {
    
    @Override
    public void drow() {
    
        System.out.println("喷火龙使用了 撞击 ");
    }

    @Override
    public void Skill() {
    
        System.out.println("喷火龙 使用了 吐息");
    }
}

public class Test {
    
    public static void func(A a){
    
        a.drow();
    }
    public static void main(String[] args) {
    
        Pikachu pikachu  = new Pikachu();
        Charizard charizard = new Charizard();
        func(pikachu);
        func(charizard);
    }
}

在这里插入图片描述


5.一个类 是 可以 拥有 多个 接口 的


另外 : 在 上面 代码 中我们 的喷火龙 是 实现了 两个接口 的 ,这里 我们 通过 implements 实现 接口 多个 接口时 , implements 只需要 一个 , 接口之间使用 , 逗号隔开 即可,

在这里插入图片描述

上面 一直 将的 是 方法, 在 接口 中 同样 是 包含字段 的 只不过这个 字段 必须是 静态常量 static final 修饰的 变量 。


6.接口 中 的 字段 只能 是 被 static final修饰 。

在这里插入图片描述


7. 接口 之间 是 可以 继承的


相比 于 类 继承 类 , 类 继承 抽象类, 类 虽然 继承不了我们的 接口 ,但 是 可以实现 多个 接口 , 但 我们的 接口 之间 是 可以 继承。

在这里插入图片描述


此时 我们的 接口 B 就 继承 了 接口 A , 当一个 普通类, 实现 了 这个 B 接口 就需要 重写 ,A 和 B 的 抽象方法 ,(这里 B继承 A 此时 就先当与 B接口 扩展 了 A 接口 的功能)。

interface A{
    
    int a = 10;
    void drow();
}

interface C {
    
    int c = 30;
    void func();
}
interface B extends A{
    
    int b = 20;
    void swap();

}
class Test2 implements B{
    
    @Override
    public void swap() {
    
        System.out.println("重写 B 的 方法");
    }

    @Override
    public void drow() {
    
        System.out.println("重写 A 的 方法");
    }
}


下面 我们 就来 整点 好玩的 。

以前我们 说过 一个类 只能 继承 一个 类 或 抽象 方法 。

此时 让我们的 接口去 继承 多个 接口 试试

在这里插入图片描述


总结:

接口与接口之间,可以使用 extends来 操作它们的关系,此时,extends 在这里意为 拓展。(一个接口A 通过 extends 来拓展 另一个接口B的功能,此时当一个类 通过 implements 实现 接口A,此时重写的方法,不仅仅是 A 的抽象方法,还有从B接口,拓展来的功能【方法】)


最后 我们 将 上面 所说的来一次 大总结 :

总结:
  
1.使用interface来定义一个接口,例:interface IA{},这就是一个接口
  

2.接口当中的普通方法,不能有具体的实现。非要实现,只能通过关键字 default 来修饰 这个方法。
  
3.接口当中,可以有static的方法。


4.接口 中 所有方法都是public的。

5.抽象方法,默认是 public abstract 的。

6.接口是不可以被实例化的(不可以被new的)。

7.类和接口之间的关系是通过 implements(工具)实现的。

8.当一个类 实现了 一个接口,就必须要重写接口当中的抽象方法。

9.在调用的 接口的 时候 可以创建一个接口的引用 (如 实现接口的 类 ), 对应到一个子类的实例

10.接口当中的字段/属性/成员变量,默认是public static final 修饰的。


11. 当一个类实现接口之后,重写抽象方法的时候,重写方法的前面必须加上public,因为接口中方法默认都是public的,而且重写方法的访问权限,必须要大等于父类当中方法的访问权限

补充 : 之前没演示这里 演示 一下:
在这里插入图片描述


12.一个类可以通过关键字extends继承一个抽象类或者普通类。但是只能继承一个类。同时也可以通过implements实现多个接口。接口之间使用逗号隔开。
  

13.接口与接口之间,可以使用 extends来 操作它们的关系,此时,extends 在这里意为 拓展,而不是继承,(一个接口 通过 extends 来拓展 另一个接口的功能)

本文 完 : 下文 预告 三种 常用 接口 。

原网站

版权声明
本文为[牧..]所创,转载请带上原文链接,感谢
https://blog.csdn.net/mu_tong_/article/details/126252883