抽象类的由来
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。例如,前面我们讲到Animal动物类,Cat,Dog都继承于Animal类
public class Animal {
private String name;
private String color;
public void eat(){
System.out.println("eat");
}
public void run(){
System.out.println("run");
}
}
public class Cat extends Animal {
public void run(){
System.out.println("run");
}
}
public class Dog extends Animal {
public void run(){
System.out.println("run");
}
}
我们会发现
第一:Animal到底什么是动物,它没有具体的行为和属性。new出来有什么意义,对于Animal类,是,所有的动物都有吃喝的行为,定义eat方法可以描述动物“吃”这一行为,但是每种动物吃的都不一样,因此一个eat方法并不能准确描述吃什么,怎么吃。这时Animal给出的信息就不足够描述一个对象,我们就不能去实例化Animal类,再比如有一个People人类,Student,Teacher是人类的子类,那什么是人类没有意义。
第二:我们看到Animal类有eat的方法,吃是每个动物必须有的,但是Dog和Cat确没有实现
如果说下面的子类不是你写的,而是别人写的,别人在写子类的时候,忘记重写了 work()
方法,或者就是故意不重写,你拿他有办法吗?那怎么办呢?抽象类就是专门治它们而存在的。
如果一个方法抽取到父类中,不确定方法体,就可以使用 abstract
(抽象关键字) 去修饰,这个方法就叫做抽象方法。
抽象方法子类必须强制重写,否则子类的代码直接报错。
而抽象方法所在类,就是 抽象类
,这就是 抽象类
的由来。
抽象类的定义及规则
在Java中,我们通过在类前添加关键字abstract(抽象的)来定义抽象类。如下所示 :
抽象类的定义格式:直接给类名加上 abstract(抽象关键字) 就行了。
public abstract class 类名{}
抽象方法的定义格式:不写方法体,直接分号结束。
前面再加一个 abstract(抽象关键字)public abstract 返回值类型 方法名(参数列表);
必须使用Abstract关键字修饰,含有0个或多个用abstract修饰的方法(抽象方法),抽象方法没有方法体,注意不能new
abstract class Animal {
private String name;
private String color;
public abstract void eat();//如果一个方法已经确定是抽象方法,那么它绝不能再有方法体,即不能出现大括号,而是只需要在()后面添加一个分号即可
public void run(){
System.out.println("run");
}
}
public static void main(String[] args) {
new Animal();//不能new
}
一个类继承了抽象类,必须重写完抽象类的所有抽象方法,否则该类只能定义为抽象类。用abstract关键字修饰 ,
抽象类中不一定包含抽象方法,但拥有抽象方法的类必须定义成抽象类。 如果某个类继承了一个抽象类,那么这个类有两个选择——要么实现父类所有的抽象方法,要么子类本身也定义成抽象类。
abstract class Dog extends Animal{
public void eat() {
}
}
抽象类体现的是模板思想,部分实现,部分抽象。
当父类需要定义一个方法,却不能明确该方法的具体实现细节时,可以将该方法定义为abstract,具体实现细节延迟到子类,
抽象方法不能被final private,static修饰,因为抽象方法需要在子类中进行重写。
再来看一个例子
假设左边的 Cat 是第一个程序员开发的,这个程序员在开发的时候他觉得:吃的方法叫什么名字,我想不起来了,方法名就使用 abc 代替。然后猫吃东西不能每次都吃一样的吧?所以方法需要加上一个形参,表示是猫吃的东西。完整代码如下图的左边。
假设右边的 Dog 是第二个程序员开发的,这个程序员在开发的时候他觉得:狗吃完了需要排除,因此这个程序员给这个方法加上了一个返回值。完整代码如下图的右边。
乍一看这个程序员写的代码,一点问题也没有。
但是两个程序员的代码合在一起,问题就出现了:假设第三个程序员,想要调用 吃东西 的方法,那就变的麻烦起来了。
如果他想要调用 猫 吃东西的方法,它就需要到 Cat类 中看一下吃东西的方法是哪个;
如果他想要调用 狗 吃东西的方法,它就需要到 Dog类 中看一下吃东西的方法是哪个;
如果这个Javabean的体系越来越大,每次第三个程序员调用eat方法的时候,到要到对应的子类去看一下你的 eat 方法是怎么写的,他才能去调用。
因此这种方式会导致代码不统一,以后在调用的时候非常的痛苦,有一种浑身蛋疼的感觉。
2)解决方案
假如我将共性的内容放到父类,其中方法体不一样的,我把它写成抽象的。那么子类在继承动物的时候,它就需要强制去重写这个方法。
以后别人在调用的时候,就不需要去看子类了,而是只需要直接去看父类就可以了。知道父类中的 eat() 方法是怎么定义的,就可以去调用所有子类中的 eat() 方法了。
同时这种方式,它还有一个隐藏的含义:强制子类必须按照这种格式进行重写,因此这种方式,它相当于就是有一种 将代码格式统一 的味道在里面。
抽象类的作用是什么?
当我们在多个子类中抽取共性时,如果在父类中无法确定方法体,就可以把这个方法定义为抽象的。
强制让子类按照这种格式进行重写。
抽象方法所在的类,必须写成抽象类。
例子
例子2
游戏管理器的改进,假设每个游戏情况不同,而且肯定是不同的,如果checkedNetWork这些方法写在父类里,出现什么问题,子类不是强制实现的。怎么解决使用抽象方法来解决
通过抽象类的使用,我们可以把需要子类必须定义的自身独有的方法,定义为抽象类,把共有的方法放在抽象类里去实现。