什么是抽象类?抽象类的作用_Java之面向抽象编程——抽象类与接口

文章前言

对于刚学完Java面向对象的三大特性的人来说,还是会比较习惯用封装和继承两个特征来面向具体编程,而在我们后期的开发中,其实还是主要面向抽象编程,而多态,便是我们面向抽象编程的最要体现之一了!今天,我们就在多态的基础上,继续学习面向抽象编程的两个重要知识——抽象类和接口。

抽象类

在讲抽象类之前,我们先回忆下我们之前的面向对象是怎么面向的:

edf6c808e3eafadd9880ee79e772a901.png

我们把自然界的猫、狗、鸟等这些具体的对象提取共同特征,形成一个动物类。把自然界的树、草、花等这些具体的对象提取共同特征,形成一个植物类。

那对于以上的两个动物和植物,我们能不能继续提取共同特征呢?可以的,这里我们就可以引出抽象类了:

31b6f5acbc65a8b804e1984a4ddc0eb4.png

这时候,我们可以把动物类和植物类抽象成一个类或者抽象类。类的话我们之前说过,就不再讲了,如果抽象成抽象类的话我们应该要怎么抽象呢?答案是用修饰符abstract来修饰这个类。

0 1抽象类的概念

什么是抽象类呢?抽象类就是把类与类之间的共同特征抽象出来形成的一个类。而就是因为抽象出来的东西不是具体的,所以抽象类是不能实例化的,也就是不能创建对象。你如果用抽象类创建对象,那么编译器是会报错的。

0 2抽象类的定义

我们用以下格式定义一个抽象类(和之前的类的定义差一个abstract而已,所以也是引用数据类型):

访问权限修饰符 abstract class 类名 {}

而抽象类的类体中格式和普通的类体差不多,类体有的三大方法和三大变量它都能有,与类体的区别是有抽象方法,而抽象方法的定义为:

访问权限修饰符 abstract 返回值类型 方法名;

对于抽象方法需要知道的是抽象方法没有方法体,以分号结尾。而且抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。

0 3抽象类的作用

到这里有的读者可能就会有点疑惑了,我们定义一个类不就是为了用来创建对象嘛?抽象类不能创建对象我们为什么要有抽象类呢?抽象类的抽象方法有什么用呢?抽象类的存在就是为了面向抽象的,抽象类的主要作用就是用来被继承的。抽象类的子类可以是抽象类或者非抽象类。而对于继承了抽象类的非抽象子类,必须实现(可以理解为方法覆盖/重写)抽象类的所有抽象方法!对于继承了抽象类的抽象子类,没有要求要实现抽象类的所有抽象方法。

理论讲完来看一个简单的代码实例:

public abstract class Biology {  public abstract void move();}class Cat extends Biology {// 实现抽象类的方法  public void move() {    System.out.println("Cat move!");  } }

上面的Biology是一个抽象类,我们在里面定义了一个抽象方法move。因为Cat类不是抽象类所以继承了抽象类后必须实现抽象类中的抽象方法。对于抽象类,我们也是可以用多态的,这里不再演示。

那么,我们什么时候用抽象类呢?一般是存在继承关系,或有共同方法且实现功能一样时,使用抽象类

接口

讲完了抽象类后读者是不是感觉对于面向抽象编程来说,还是有点含糊的,其实正常,因为要用抽象类来面向抽象编程的话其实是很少的,一般都是用接口来实现,因为接口比抽象类更加抽象了!因此我们重点讲解接口:

0 1接口的概念

什么是接口呢?接口其实可以说是抽象类的子集,因为接口是在抽象类的基础上进行改进使之可以完全面向抽象编程,因此,抽象编程也叫做接口编程。对于接口,拿我们经常使用的电脑为例,一台电脑少不了USB接口,耳机接口等等,这些就可以类比成我们Java的接口了。

0 2接口的定义及实现

定义一个接口的格式为(和之前定义的类差别就是class改成interface而已,接口也是引用数据类型):

访问权限修饰符 interface 接口名 {}

而接口的类体就和抽象类差很多了!在JDK8之前,接口的类体只能存放常量和抽象方法这两种而已,JDK8之后支持在接口里面放static或default修饰的方法。不过这种新特性作者感觉使接口完全抽象的性质就被破坏了,我们来看一个JDK8之前接口的定义例子:

44b35573806744813cfffd8e4be15633.png

而实现一个接口我们不像抽象类那样用extends来被继承,而是用implements来实现!

7d38601ad2d3d9708d23c4c05e8c201f.png

如果不这样写,那么编译器就会报错。而且一个类可以实现多个接口,这在一定程度上弥补了Java单继承的缺陷。如果一个非抽象类实现多个接口,那么需要把多个接口中的抽象方法加以实现,否则编译器会报错。

接下来还有需要我们知道的点就是接口和抽象类类似,抽象类是天生用来被继承的,接口天生用来被实现的,而两者的抽象方法都要被实现(覆盖/重写),因此方法的修饰符都写public。而常量的static final和抽象方法的abstract都是固定的,所以在接口中,我们可以把以上说到的关键词都省略,因此以上的A接口可以简写成:

9481f7cfd67ecc44f2ea4aa1c72dc70c.png

一般我们都写简写形式,而在JDK8之后,我们可以在接口中加这样的两种方法:

4a69e115e2d37ab82477531c237cd3ed.png

default修饰的就相当于实例方法,static修饰的相当于静态方法。所以...(对于这个新特性作者也是很懵)。这里只解释在之前,default修饰符都是省略的,但这里default不能省略,因为省略了就和简写的抽象方法一样,所以不能省略。

0 3接口的作用

上面说过接口和抽象类相似,所以接口的作用也是主要用来被实现,当然,接口也可以被继承,但继承的类必须是接口,不仅如此,接口支持多继承!目前Java中只有接口支持多继承!

fa7528f1ce6e6e9e17e1ebf1187a9c0b.png

而且你可以重写父接口的方法。

这时候读者可能有疑惑了,那一个非抽象类能不能同时继承父类也同时实现接口呢?答案是可以的,在非抽象类中,我们可以在继承父类的同时实现一个或多个接口。

bd285df9c55706aaaf787392bd7cf1c4.png

以上是个能上天遁水的鸟类。

对于接口,最有趣的多态出现了,我们先来看一个例子:

67769a8f1fc9413fe006f061a187c993.png

我们定义了三个接口A,B,C,然后让D这个类实现A,B两个接口,然后我们用多态,A类型的引用t指向D对象,然后分别让t强转成B类型和C类型,我们会发现我们在强转成不是D类型的父类的C类型的时候编译器居然没有报错。但运行的时候报错了:

d0cd5c5fd22831b7325cb24f0a6e144b.png

报错原因是15行的类型转换出现了异常,也就是说,一个子类实现了多个父类接口时,用这些父类接口类型的引用指向子类型对象的时候,这个类型可以强转成其他父接口的类型,但不能强转成其他不是父接口的类型,如果这么做了,编译不会报错,但运行会报错!这就是接口的多态和之前的多态的简要区别了。

那么,我们什么时候用接口呢?当我们只关心功能存在性时,使用接口。很多都是形容词。比如,我们定义一个能飞的接口,以后创建能飞的类时就去实现这个接口;我们定义一个能移动的接口,以后创建能移动的类就去实现这个接口。在我们JavaSE阶段,我们对接口的选择也是只能从这方面解释的。

A总结01

抽象类和接口都是和类同一级别的,而一个Java文件只能有一个public修饰的类,所以我们在创建抽象类和接口的时候还会新建一个Java文件,然后用public来修饰抽象类和接口。(修饰符的时候会提到)

没有方法体的一定是抽象方法嘛?02

不一定,就拿Object的源代码来看:

cc5a51bd51edcd852276f1f29199d1a0.png

这个registerNatives就没有方法体,但它没有用abstract修饰,所以不是抽象方法。这个native表示调用了JVM的本地程序。

03非抽象类、抽象类、接口的比较
非抽象类抽象类接口

三大变量

+常量

三大变量

+常量

常量
三大方法

三大方法

+抽象方法

抽象方法两

种修饰符方法

能封装不能封装不能封装

单继承

单继承多继承
被单继承被单继承

被单或与其

他接口多实现

多态(没有继

承关系编译

器会报错)

多态(没有继

承关系编译

器会报错)

多态(没有继

承关系编译

器不会报错)

能实例化不能实例化不能实例化
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值