ObjectOrientedProgramming - 面向对象的编程(多态、抽象类、接口)- Java - 细节狂魔

  • 本文结束(还有 三个常用接口的内容,将在下篇博客中出现)

回顾上篇博客内容

=======================================================================


     继承:对共性的一个抽取,使用extends关键字来实现,

   语法 A extends B 

   A 是 子类

   B 是 父类

       A is B

    意义:为了代码的重复使用

      注意:

              1.子类继承了父类,那么子类在构造时,要先帮助父类进行构造,需要在子类的构造方法当中,

              ‘  ’使用super关键字来显示(int a =10;显示就是简单而直观)的调用父类的构造方法

              2. super 和 this 的 区别(super针对父类,this 针对当前的类)

              3. 重写 和 重载的区别 (

                   3.1重载:参数类型和个数至少有一个不同,返回值不做限制;重写:参数的类型和个数都相同,除特殊情况外,返回值相同

                   3.2 重载:针对一个类;重写:针对 子类(不限于一个类)

              4. 讲解了访问修饰限定词的访问权限范围。

                   4.1 privatae : 仅限一个类

                       4.1.1 在类中 一个成员变量 和 方法,如果被 private 所修饰,那么在该类被继承时,会不会被继承到子类当中。

                               我更偏向于不会,因为在子类中不能通过super.data 去访问它,而且编译器在 super后面加点,也点不出来。(这个更权威一些,有理有据)

                               说会的,意思是 被private修饰的成员变量 和 方法,虽然无法访问和调用,但是还是被继承下来了。

                   4.2 default(包的访问权限):限定在一个包内,就是字段/属性/成员变量,在不加任何修饰限定词的前提下,该成员变量默认时 包的访问权限

                   4.3 protected :在一个包里可以随意使用,在不同包里,必须是子类才能访问

                   4.4 public : 哪里都能用

   在了解多态之前,我需要搞懂  什么向上转型,什么是运行时绑定。

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

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

         2.1 方法名称相同

         2.2 参数的类型和个数都相同

         2.3 返回值相同 【特殊:返回值也可以是协变类型】

         注意事项:

              1。 static 方法不能重写

              2.  private 修饰的方法不能重写

              3. final 修饰的方法不能重写

              4. 子类方法的修饰限定 访问的范围 大于等于 父类方法的修饰限定(子类方法的访问权限要大于等于父类方法的访问权限)

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

     4. 向下转型: 子类引用 引用父类对象,但不安全,不建议使用。




回到本文

===================================================================

理解多态

===================================================================


     有了面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(Polymorphism) 的形式来设计程序了



案例(使用多态,打印多种形状)


代码1


class Shape{// 无论是 三角形,还是正方形等等,它们都是图形

    // 以此作为共性抽出

    public void draw(){

        System.out.println("Shape::draw()");

    }

}

// 矩形

class Rect extends Shape{

    @Override// 当我们在子类中,重写了父类中的方法。那么父类方法中的输出语句就显得毫无意义

    // 最后都是输出子类draw方法,如果你想的话,可以删掉父类的 输出语句

    public void draw(){

        System.out.println("♦");

    }

}

// 花

class Flower extends Shape{

    @Override

    public void draw() {

        System.out.println("❀");

    }



在这里插入图片描述


代码2(实现多态)


class Shape{// 无论是 三角形,还是正方形等等,它们都是图形

    // 以此作为共性抽出

    public void draw(){

        System.out.println("Shape::draw()");

    }

}

// 矩形

class Rect extends Shape{

    @Override// 当我们在子类中,重写了父类中的方法。那么父类方法中的输出语句就显得毫无意义

    // 最后都是输出子类draw方法,如果你想的话,可以删掉父类的 输出语句

    public void draw(){

        System.out.println("♦");

    }

}

// 花

class Flower extends Shape{

    @Override

    public void draw() {

        System.out.println("❀");

    }

}



public class Test {

    public static void main(String[] args) {

        Rect rect = new Rect();

        rect.draw();// 这里是单纯,new对象,访问普通成员方法



        // 使用向上转型,重写,父类当中 draw 方法。

        // 使父类的draw有了另一种实现的方法,

        // 这种情况,被称为动态绑定。

        // 在不断重写父类draw方法,呈现draw方法的多态的实现

        // 这就是我们所说的多态

        Shape shape = new Rect();

        shape.draw();

        Shape shape1 = new Flower();

        shape1.draw();

    }



}



效果图

在这里插入图片描述


代码三,通过方法和向上转型,来实现多态。让你们更加直观


代码如下


class Shape{// 无论是 三角形,还是正方形等等,它们都是图形

    // 以此作为共性抽出

    public void draw(){

        System.out.println("Shape::draw()");

    }

}

// 矩形

class Rect extends Shape{

    @Override// 当我们在子类中,重写了父类中的方法。那么父类方法中的输出语句就显得毫无意义

    // 最后都是输出子类draw方法,如果你想的话,可以删掉父类的 输出语句

    public void draw(){

        System.out.println("♦");

    }

}

// 花

class Flower extends Shape{

    @Override

    public void draw() {

        System.out.println("❀");

    }

}



public class Test {

    public static void paint(Shape shape){

        shape.draw();

    }

    public static void main(String[] args) {

        Rect rect = new Rect();

        paint(rect);

        paint(new Rect());

        System.out.println("=============");

        Flower flower = new Flower();

        paint(flower);

        paint(new Flower());

    }

 }



附图

在这里插入图片描述

从另一个方面来说:通过一个引用来调用不同的draw方法,会呈现出不同的表现形式。表现的形式取决于将来它引用那个对象。这就是动态。而且实现多态的大前提,就是一定要向上转型,且实现 父类和子类的重写方法。


总结:


在这个代码中, 上方的代码(矩形、花、继承)是 类的实现者 编写的, 下方的代码(main所在的类)是 类的调用者 编写的。

当类的调用者在编写 Paint 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现。(和 shape 对应的实例相关), 这种行为就称为 多态。

多态 顾名思义, 就是 “一个引用, 能表现出多种不同形态”。


拓展:


   多态是面向对象程序设计中比较难理解的部分. 我们会在后面的抽象类和接口中进一步体会多态的使用. 重点是多态带来的编码上的好处.

另一方面,

 如果抛开 Java, 多态其实是一个更广泛的概念, 和 "继承" 这样的语法并没有必然的联系.

  C++ 中的 "动态多态" 和 Java 的多态类似. 但是 C++ 还有一种 "静态多态"(模板), 就和继承体系没有关系了.

  Python 中的多态体现的是 "鸭子类型", 也和继承体系没有关系. 

  Go 语言中没有 "继承" 这样的概念, 同样也能表示多态.

无论是哪种编程语言, 多态的核心都是让调用者不必关注对象的具体类型. 这是降低用户使用成本的一种重要方式.




使用多态的好处是什么?

==========================================================================

1. 类调用者对类的使用成本进一步降低。

封装是让类的调用者不需要知道类的实现细节.

多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可。因此,多态可以理解成是封装的更进一步, 让类的调用者对类的使用成本进一步降低.

这也贴合了 <<代码大全>> 中关于 “管理代码复杂程度” 的初衷.

2. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else

3. 可扩展能力更强.:如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低

有第二个好处,引出问题,什么是圈复杂度?



    圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解.

 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.

    因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度". 

 如果一个方法的圈复杂度太高, 就需要考虑重构

 

     不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10



对比图(情况2)

在这里插入图片描述

情况3 附图

在这里插入图片描述


抽象类

==================================================================

语法规则


在刚才的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由

Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract

method), 包含抽象方法的类我们称为 抽象类(abstract class)


abstract class Shape { 

 abstract public void draw(); 

} 



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

对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.

在这里插入图片描述


代码如下:



abstract class Shape{

// 凡是 一个类中,包含抽象方法,就要改类前面加上 abstract 修饰,叫做抽象类。

    public abstract void draw();// 加上分号,代表没有不实现任何现象

    // 在 public 后面加上 abstract 这样它就变成了一个抽象方法

}




那么抽象类 和 普通类 又有区别?


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

在这里插入图片描述

另外 在抽象类内部 是可以定义成员变量和方法的

在这里插入图片描述

有人可能会说,又不能new,里面就算能存储方法和变量,也不能用,那我们要它干什么用,徒增脱发??


由1引出, 抽象类的用法:因为不能被实体化,所以这个抽象类,只能被继承。

在这里插入图片描述


在这里插入图片描述

另外:继承抽象类的普通类当中 需要重写所有的抽象方法,上面重写一个,只是因为 被abstract修饰的方法只有一个,


抽象类是可以 向上转型的

在这里插入图片描述


抽象类可以向上转型,就意味着能发生动态绑定、加上重写,就能形成多态。

在这里插入图片描述


而且 前面说的成员变量和方法都能继承过来,并且使用

在这里插入图片描述


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

在这里插入图片描述


俗话说的好,躲得了一时,躲不了一世,如果 继承了抽象类Shape的抽象类A,被一个普通类继承,你需要实现 抽象类A 和 B。两者的抽象方法。

在这里插入图片描述

在这里插入图片描述


抽象类 不能被 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修饰。

11.抽象方法不能是 private 的

12.抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象

方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

image.png

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

2. 什么是抽象方法,一个没有具体实现的方法,被 abstract 所修饰。

3. 抽象类是不可以被实例化(不能 new)

4. 由于抽象类不能实体化,所以,抽象类只能被继承

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

6. 一个普通类,继承了一个抽象类,那么这个普通类当中,**需要重写

所有的抽象方法。**

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

8. 一个抽象类A,如果继承了另一个抽象类B,那么抽象类A可以不实现

抽象类B的抽象方法。

9. 结合第8点,当A类 再次被一个普通类所继承后,那么 A 和 B 两个

抽象类当中的抽象方法,必须被重写。

10. 抽象类 不能被 final修饰,那么由此推论出:抽象方法也不可以被

final修饰。

11.抽象方法不能是 private 的

12.抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象

方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

[外链图片转存中…(img-9IRdCgrp-1714433179953)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值