抽象类和抽象方法
当我们定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的。比如,我们定义一个Animal类时,在里面定义一个say()方法用来表示动物的叫声,我们有很多动物子类继承了Animal这个父类,但是不同的动物叫声也是不同的。
因此我们无法在Animal中准确的描述,我们需要将这个say()方法定义成抽象方法,意思就是多种动物都有这种方法,我们用abstract关键字来修饰,并且在定义方法时不需要实现方法体。当一个类中包含了抽象方法,那么该类也必须使用abstract关键字修饰,这种类就叫做抽象类。抽象方法所在的类必须是抽象类,但是,抽象类中不一定要有抽象方法。
现实生活中有很多的抽象类,这些类都是概念化的,没法具体到某个实例,描述这一类对象共同的属性和行为称之为抽象,比如 人类就是一个抽象类,因为中国人,日本人,美国人都是人类,他们都是人类这个类的子类。
- Java中定义抽象类或者抽象方法使用abstract关键字。
- 抽象方法所在的类必须使用abstract关键字声明为抽象类。
- 抽象类是普通类的超集,普通类有的,抽象类都有,只是比普通类多了一些抽象方法而已。
- 抽象方法所在的类必须是抽象类,子类(普通类)若继承了抽象类,必须覆写所有父类的抽象方法。如果子类是是一个抽象类,那么不需要覆写所有的抽象方法。
- 抽象方法指的是使用abstract关键字声明,只有函数声明,没有函数实现的方法,称为抽象方法。
public abstract class Animal {
//没有方法体,子类覆写的时候实现该方法。
public abstract void say();
}
- 本地方法也没有方法体,但不是抽象方法,这个方法的具体实现是由JVM实现的。
- 若一个类使用abstract声明为抽象类,无法直接通过该类实例化对象,哪怕该类中一个抽象方法都没有。当一个类是抽象类,不管他有没有抽象方法,这个类本身就是一个抽象的概念,没法具体到某个特定的实例。只能通过子类向上转型变为抽象父类的引用。
Animal ani1 = new Dog();
- 子类继承了抽象类,就必须强制子类(普通类)覆写抽象类中的所有抽象方法,包括继承来的抽象方法,也满足单继承局限,一个子类只能继承一个抽象类。
abstract class Animal {
abstract void say();
abstract void run();
}
//Dog类是抽象类,可以选择性覆写父类的抽象方法。
abstract class Dog extends Animal {
//此时我们就没有覆写,我们是重新定义了一个抽象方法
abstract void swim();
//也可以都覆写,如果此时都覆写了,那么Dog的子类就不用再继续覆写,只需要覆写Dog类的抽像方法即可
// void say();
// void run();
}
public class ChaiQuan extends Dog {
//此时的子类就需要覆写所有父类的抽象方法
void say(){}
void swim(){}
void run() {}
}
- 抽象类虽然没办法直接实例化对象,但是也可以存在构造方法,子类仍然满足 is a关系,子类和抽象父类之间仍然满足“继承树”关系。子类在实例化对象的时候,仍然遵从继承的原则,先调用父类(抽象类)的构造方法,再调用子类的构造方法!
public class Fun extends BaseTest {
private int num = 10;
//覆写父类的抽象方法
void print() {
System.out.println("num=" + num);
}
public static void main(String[] args) {
//newFun的时候先在子类的无参构造首行默认调用父类的无参构造
new Fun();
}
}
abstract class BaseTest {
public BaseTest() {
this.print();
}
abstract void print();
}
执行结果如下:
为什么是0呢,因为在Fun继承了BaseTest,产生子类对象时调用的是子类的构造方法,此时子类构造方法的首行会默认先调用父类的无参构造,父类的无参构造中调用了print方法,此时的print方法已经被子类覆写过,所以表现出来的方法就是子类覆写后的方法,此时的num还没有被赋值,所以是0!