抽象类

下面介绍一个场景:

  一个动物类可以分为哺乳动物和非哺乳动物,而同时哺乳动物和非哺乳动物都可以繁殖后代,还有哺乳和非哺乳动物的保温方式等,而这些都是哺乳动物和非哺乳动物所共有的特征,但是,它们特点却又不一样,如哺乳动物通过胎生繁殖和恒温动物,而非哺乳动物则是卵生和冷血动物。

  如果要将上面的问题已面向对象的思想解决:则首先可以抽象成三个实体类为Animal类,Mammal类和一个NonMammal类。Animal作为MammalNonMammal的父类。

  那么问题来了,对于子类都具有的行为,但是这些行为对于不同的体系又有不同的区别,那么对于这类情况应该怎么进行处理?

  对于这种情况java就提供了一种方案来解决这个问题,这个方案就是抽象类。

 

一、抽象类的概述

  那么Java提供的抽象类是如何解决上面的问题的呢?下面一步步的分析,对于那些子类全部都共有且特点一模一样的特性,那么就可以直接写在父类中,而不需要在每个子类重写一模一样功能的方法,因为子类只要继承父类就可以得到该特性;但是对于那些,子类都有的特性且这些特性又各有特点的时候,我们就可以在父类定义一个方法而不去具体实现,子类通过继承父类的这个方法去完善这个特性的自身特点。而这个在在父类定义一个方法而不去具体的实现,那么这个方法就叫做抽象方法。

  那么对于上面场景的用抽象类解决的办法为:在Animal类中将繁殖和温度类型的两个方法抽象实现(因为这两个方法对于哺乳和非哺乳都必有,但特征又不同),而对于其他的一些方法,如睡觉和进食则是他们都共有且特点又一样的就定义为非抽象方法。因为对于这类方法,子类只需要继承父类的就可以了。见下代码示例:

 

Animal类:                                                                                      

public abstract class Animal {
  // 繁殖的抽象方法,胎生或卵生
  abstract public void breed();
  // 温度的抽象方法(恒温或冷血)
  abstract public void temperatureType();

  // 其他共用的且没有区别的特性,只需要继承就可以实现的功能
  public void eat() {
    System.out.println("不管哺乳动物还是非哺乳动物都要进食");
  }

  public void sleep() {
    System.out.println("不管哺乳动物还是非哺乳动物都要休息");
  }
}

Mammal类:                                                                                      
public class Mammal extends Animal{
  @Override
  public void breed() {
    System.out.println("哺乳动物通过胎生繁殖");
  }

  @Override
  public void temperatureType() {
    System.out.println("哺乳动物为恒温动物");
  }
}

 

NonMammal类:                                                                                      
public class NonMammal extends Animal {
  // 对于继承了抽象类的具体了,必须要实现抽象类的抽象方法。
  // 也就是对抽象类中的抽象方法进行重写
  // 对于抽象类的非抽象方法也可以重写也可以不重写
  @Override
  public void breed() {
    System.out.println("非哺乳动物通过卵生繁殖");
  }

  @Override
  public void temperatureType() {
    System.out.println("非哺乳动物为冷血动物");
  }
}

AbstractDemo类:                                                                                      
public class AbstractDemo {
  public static void main(String[] args) {
    // 抽象类不能实例化
    // Animal animal = new Animal();
    System.out.println("哺乳动物");
    Animal animal1 = new Mammal();//多态
    animal1.breed();
    animal1.temperatureType();
    animal1.sleep();
    animal1.eat();
    System.out.println("\n\n非哺乳动物");
    Animal animal2 = new NonMammal();
    animal2.breed();
    animal2.temperatureType();
    animal2.sleep();
    animal2.eat();
  }
}

运行截图:                                                                                        

 

 

抽象类的特点:

  • 从代码中,可以看出第一点,当一个类定义了一个抽象方法时,那么这个类就必须是抽象类(也就是说一个类如果包含抽象方法,那么这个类必须是抽象类)。
  • 抽象类和抽象方法用abstract关键字修饰
  • 抽象类不能实例化(这是因为抽象类是抽象的,而不是具体的)
  • 虽然抽象类不能进行实例化,但是抽象类可以有构造方法。(这是因为抽象类的构造方法用于子类访问父类的时候去初始化父类的变量)
public abstract class Animal {
  public String string = "";
  // 抽象类的构造方法
  public Animal(String string){
    this.string = string;
    System.out.println(string);
  }
  public Animal(){}
  ......  
}
public NonMammal(String string) {
  super(string);
}
  • 关于构造方法的一个特点:当父类中只有带参构造,而没有无参构造方法的时候,那么子类就必须显示的调用父类的构造方法。这是因为在创建子类对象的时候会去调用父类的构造方法,如果子类没有显示的调用父类的构造方法,那么就自动地调用父类的默认构造(也就是无参构造),但是如果当父类的无参构造又不存在的时候,那么就要显示的调用父类的构造方法,不然编译器找不到父类的构造,不能初始化父类,那么将编译报错。
  • 抽象类的子类如果是具体类的时候,那么子类就必须要实现抽象类的抽象方法,也就是重写抽象类的抽象方法。
  • 抽象类的子类如果还是抽象类的话,那么子类可以选择不实现父类的抽象方法,如下代码:鸟类也继承自抽象动物类
public abstract class BridAnimal extends Animal {
    // 可以不实现Animal的抽象方法
}
  • 抽象类没有this对象,因为抽象类不能实例化,因此抽象类是没有对象的,没有对象因此就不存在this
  • 抽象类只能引用它非抽象子类的对象,这是因为抽象类不能实例化。(不能实例化就是指不能创建这个类的对象)(多态)
  • 抽象类也可以包含非抽象方法,这些非抽象方法可以继承给它的子类。
  • 抽象类的成员变量,构造方法均和普通类一样。成员方法的抽象方法只能有方法声明,不能有方法体(也就是抽象方法不能在声明的时候进行实现)。
  • abstract关键字不可以和哪些关键字组合
    • private

  这是因为抽象方法必须在子类要重写的,也就是抽象方法必须对外可见,而private修饰的话对外不可见,子类就不能继承,也就不能重写,两者冲突。

    • final

  同样抽象方法必须要进行重写,而final修饰的方法是不能重写的,这两者存在矛盾,因此不能组合。

    • static

  static修饰的方法是可以进行类名调用的,如果将一个抽象方法声明为static,而此抽象方法没有实现,那么这种调用是毫无意义的。因此也不能组合。

 

posted @ 2017-07-12 21:14  Geore  阅读(234)  评论(0编辑  收藏  举报