再谈抽象类(感觉理解的更深了)

抽象类其实一直都是一个比较困扰我的问题,自己上网看资料看书,最后总是感觉理解的不是很深,直到今天听了王克晶老师讲抽象类,感觉豁然开朗,不得不承认讲的真的太清楚了。

首先,我们需要知道为啥要有抽象类,学了这10几天Java,我已经明显的感觉到,Java中的各种语法、结构很多都是为了简化代码的重复性,而抽象类真的大大大大大地提升了这种复用性,废话不多说,赶紧把我今天听的东西记录一下。

下面我循序渐进地把抽象类引出来。

首先,要先说抽象方法,抽象方法是用abstrat修饰的、没有方法体的方法,抽象方法存在的意义一定是要把它写在父类里,一定要用子类去实现,这个不仅仅是语法规定(子类不去实现父类的抽象方法会报错的,除非子类也是抽象类),更重要的抽象类的意义!!!抽象类最重要的作用就是:为所有派生类提供统一的入口,派生类的实现可以不同,但是其入口是一致的!!关于这个加粗红色的部分,后面会慢慢解释。

class FlyingObject{
     int x;//飞行物横坐标
     int y;//飞行物纵坐标
 }
 class Airplane extends FlyingObject{
     void step() {
         y++;//小飞机纵向走
     }
 }
 class BigPlane extends FlyingObject{
     void step() {
         x++;//大飞机横向走
     }
 }
 class Bee extends FlyingObject{
     void step() {
         x++;//蜜蜂横向纵向都走
         y++;
     }
 }

看上面这个代码,是飞机大战的几个类,第一个是飞行物,是超类,是整个天空所有飞行物的父类,后面三个一次是小飞机、大飞机和小蜜蜂,都是飞行物的子类。在这三个子类里都有step()这个方法,三个方法都是使得对象在天空的坐标发生变化的方法,但是三个方法的方法体不同,这样我们想要三个对象调用step()方法,我们就必须在主方法里这样写。

public class AbstractDemo {
     public static void main(String[] args) {    
         Airplane airplane=new Airplane();
         airplane.step();
         BigPlane bigPlane=new BigPlane();
         bigPlane.step();
         Bee bee=new Bee();
         bee.step();
     }
 }

要先去一个一个new三个类的对象,然后再去调用。那么问题来了,对于我们的主角英雄机而言,小飞机、大飞机和小蜜蜂都是敌人,而且这三个类都是飞行物的子类,我们为什么不可以通过向上造型创建一个FlyingObject的数组去把这些敌人都装进去呢,这样之后对敌人都可以进行统一操作了!答案当然是可以的,我们可以这样做。

public class AbstractDemo {
     public static void main(String[] args) {    
         FlyingObject[] enemy=new FlyingObject[3];
         enemy[0]=new Airplane();
         enemy[1]=new BigPlane();
         enemy[2]=new Bee();
     }
 }

这样一写,通过向上造型,创建了一个飞行物的数组,里面有小飞机、大飞机、小蜜蜂各种敌人,最关键的,放进数组后可以进行统一操作了。

那么问题来了,这个时候,想让他们动起来怎么办呢?

如果写:

for(int i=0;i<enemy.length;i++)
  enemy[i].step();

这样是会报错的!提示step()这个方法未定义!

对啊!因为enemy是超类的引用数组,而step()这个方法是子类的,当然不能通过enemy[i]去调用step()方法了。

那怎么办呢?

这个时候,抽象类就应运而生了!!

在超类FlyingObject里这样写:

abstract class FlyingObject{
     int x;//飞行物横坐标
     int y;//飞行物纵坐标
     abstract void step();
 }

在这个类里,加入step()方法,但是并没有方法体,因为很显然啊,我们这个时候并不想在父类里实现step()方法,step()方法对每个子类都不同啊,真正要去调用它的时候我们都是根据不同的子类去调用各自不同的step()方法啊,所以我们在超类里不能也不需要去写它的方法体,我们仅仅是想在超类里为各个子类提供一个step()方法的接口,这样就可以通过enemy[i]这个超类对象去调用step()方法了。

因为没有方法体,所以这个方法不能属于普通成员方法,Java为我们提供了abstract关键字,将它视为抽象方法,就可以了。但是抽象方法所在的类必须为抽象类,因此在类名前也需要加入abstract关键字。

那么问题来了,

public class AbstractDemo {
     public static void main(String[] args) {    
         FlyingObject[] enemy=new FlyingObject[3];
         enemy[0]=new Airplane();
         enemy[1]=new BigPlane();
         enemy[2]=new Bee();
         for(int i=0;i<enemy.length;i++)
             enemy[i].step();
     }
 }

第8行的这个enemy[i].step()如何判断它调用哪个子类的step()呢?

这个问题问的非常好!

很简单!当然是看enemy[i]里装着的具体是哪个对象,这是向上造型的知识。向上造型,引用能不能.出来方法,要看这个父类里有没有这个方法,.出来这个方法,具体去调用哪个方法,要看这个引用具体指向哪个对象,而我们用抽象类,都是用子类去实现父类的方法,其实也相当于把父类的抽象方法重写(覆盖)了,我们要去调用的应该是重写后的方法!

总结一点,向上造型,能不能.出来方法是看引用的类型,.出来之后具体调用哪个类的方法要看对象!

这样我们通过抽象类和抽象方法,就将所有的敌人都装进了enemy这个数组中,并且可以直接调用子类的方法,还可以对enemy进行统一操作!代码简化很多!复用性的魅力体现的淋漓尽致有木有!!

再回来看开头抽象类的意义:为所有派生类提供统一的入口,派生类的实现可以不同,但是其入口是一致的!!

现在应该就很明白了!

这下大家就明白抽象类的作用了吧,这种知其然还知其所以然的感觉真的很好!