文章目录
一、提出新问题
① Animal本身就是为了保存动物的共性而存在,被猫、狗、蛇它们所继承。也就是说它本身没有什么实际含义,没人会养一个Animal,它是一个抽象的东西,但是却可以直接在程序中new一个Animal,有点不太合理。
② 如果后续新建蛇、鸟这些类,但是忘记重写它们的叫声了。此时它们只会动物叫,这显然是不合理的。
二、抽象与具体 – 抽象类(abstract)的衍生
1. 情境再现
也就是说,Animal本质来说是没人用的,它是一个抽象的东西,抽取了这些猫狗的共性,作为使用。
就像你说最近养了个动物,别人一定会继续问你养了什么动物。再比如,经常听到有人说回家撸猫撸狗,没听过有人说回家撸动物的。
抽象的目的是为了概括(解释)这些具体的事物。
以上是百度百科给动物下的定义。设想一下,你回家跟你妈说:妈,我想养一个多细胞真核生物。你妈说:啥玩意?说清楚点。你说:我就想养一个多细胞真核生物。然后不出意外你妈会觉得你可能病得不轻。
那怎么限制程序员或者用户new一个Animal(抽象类)呢?— 用abstract。
2. 举例理解
用abstract修饰Animal以后,发现不能new一个Animal了。但是猫狗这些仍然可以正常new。
三、抽象方法的使用
1. 情境再现
抽象方法 – 不能有实际意义(即 不能拥有方法体)
为什么呢?世界上那么多动物,每种动物的叫声都不一样。你怎么知道Animal是怎么叫的。你会发现,动物的叫声太多了,没有办法具体到某一个指定的叫声。就像你不能说蛇的叫声是动物叫,动物叫怎么叫?
抽象类里可以没有抽象方法,但是抽象方法一定属于抽象类。
当一个类继承一个抽象类的时候,此时,该子类必须重写父类中的所有抽象方法。
2. 举例理解
我们用abstract修饰Barking()的时候,会提示我们删除方法体。
当我们删除掉Animal类前面的abstract时,会在Barking()那里提示我们必须有一个抽象类。
我们会发现这个时候蛇这个类这里会提示我们必须重写Barking()这个抽象方法。
这个时候也不能用super继承父类的方法体内容了,必须自己重写方法体。
这个时候蛇就能正常叫了
四、接口(interface)
1. 概念
① 如果一个类里所有的方法都是抽象的,就可以把它定义成一个接口。
② 抽象类里抽象方法需要加abstract,接口里的不需要。
③ 类中的方法我们可以叫重写,但是在接口中,我们叫实现(implements)。
④ 类与类直接我们可以叫继承,类与接口之间我们叫实现
⑤ 接口与接口之间也可以有继承与被继承的关系
2. 举例理解
定义一个Human的接口,再在接口里定义一些抽象方法。
我们发现接口里的抽象方法不加abstract也是可以的,因为本来就默认接口里的方法都是抽象的。
除此之外,因为是抽象方法,所以不能有方法体。
接下来我们可以用Chinese和Westerner两个类去实现Human接口。
此时会提示我们,实现类必须重写接口里的所有抽象方法。
上面已经提过,我们是没有办法new一个抽象类的,同理,我们也没有办法new一个接口。
但是我们可以new接口的实现类实例。
接口是可以继承另一个接口的。
五、class和interface的区别
我们可以回想一下,最初我们为什么会把Animal定义成抽象类、把Barking()定义成抽象方法?
因为我们不知道Animal是什么,用户可能养一条狗、一只猫,但是不可能去养一个动物,没有人会这么说,所以我们把Animal定义成抽象类。同理,我们也不知道动物是怎么叫的,因此把Barking()定义成了抽象方法。
接口当中所有的方法也是抽象的,那为啥有的时候用接口有的时候用抽象类呢?难道不能在Animal里把所有的方法全写抽象的,为什么非要有接口?
因为抽象类是对一个具体的事物进行抽象,例如:Animal;而接口是对动作、行为进行抽象,例如:吃、跑、玩、乐。
所以刚刚把接口命名为Human是有问题的,容易误导人。与其说是Human,不如说是HumanAction。
再比如,我现在有一个Animal类和一个Animal接口。我们都知道动物有年龄、品种这些属性,它们可以在Animal类里体现;但是Animal接口里就不能出现这些东西。
虽然也可以在接口定义一个final int sss = 11;不会报错,但是通常不会这么写。
工作当中,接口的用途还是比较多的,尤其是Dao层、Service层这块。
相关问答