前言
可能是长期从事“研究物质空间位置转移的科研项目”(ban zhuan)的原因,个人一直没有感觉出抽象类的作用。觉得既然抽象类无法实例化,里面的方法又只有子类才能调用或实现,那我写它干啥,直接写在子类里不就好了吗?但是Java肯定不可能设计一个毫无作用的东西给我们,今天就研究一下抽象类的使用以及它的意义在哪儿。
抽象类使用
定义抽象类及子类实现
首先我们定义一个抽象类名字叫AbstractAnimal,里面提供了一个抽象方法run和一个公共方法sleep;然后我们再实现这个抽象类的子类AbstractRabbit,并重写run方法。
public abstract class AbstractAnimal {
AbstractAnimal() {
System.out.println("AbstractAnimal constructor");
}
public abstract void run();
// 没有声明权限时,则默认设为package-private,它只对在自己的包内的所有类可见。
void sleep() {
System.out.println("this is AbstractAnimal sleep()");
}
}
public class AbstractRabbit extends AbstractAnimal {
AbstractRabbit() {
System.out.println("AbstractRabbit constructor");
}
@Override
public void run() {
System.out.println("this is AbstractRabbit run()");
}
}
调用方式
调用时由于抽象类无法实例化,故我们实例化时是new的子类AbstractRabbit,然后用这个子类对象去调用相应的方法。
AbstractAnimal testAbstract = new AbstractRabbit();
testAbstract.run();
testAbstract.sleep();
输出结果:
AbstractAnimal constructor
AbstractRabbit constructor
this is AbstractRabbit run()
this is AbstractAnimal sleep()
为什么要这样实现?
首先我们知道会有许许多多的动物种类,如老虎、狮子、狗熊、猫、兔子等等,它们都会跑也都要睡觉。我们通常的思路是给每一种动物都实现一个类,然后再实现相应的方法。这样就会带来一个很明显的弊端:我们需要实现非常非常多实现类的同时,还要写很多很多实现方法,但明明有些方法是可以共用的,比如sleep、run、eat等等。这种时候抽象类的作用就体现出来了,我们可以把这些公用变量和方法都放到抽象类里,需要的时候直接调用即可。不仅如此,我们还可以在抽象类中规定一些抽象方法,而抽象方法子类继承之后必须要实现,这样可以保证子类的完整性与规范性。
举个栗子:你想想是不是不管什么动物都要吃东西,不eat的那还是动物吗?那我干脆就强制要求所有的子类都要实现这个方法,不然你就不是动物。
现在再思考思考抽象类的意义是不是就有点感觉了?
抽象类的使用规范及意义
使用规范
- 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,不会给出方法的具体实现。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。同时父类的构造方法会在子类实例化时自动调用。
- 子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
意义
- 更好的将设计与实现分离(俗称官话,需要代码写多了才能慢慢体会/手动滑稽)。
- 减少冗余代码,降低重复工作量,使代码更简洁凝练。
- 保证子类的规范性与完整性。
PS:配合下一篇文章“Java接口”食用效果更佳:https://www.jianshu.com/p/c5d00a4b06ca