抽象类和接口
抽象类
有时候父类会为它的所有子类创建一些通用方法,而这些方法表示所有子类的公共部分,但是子类可以分别重写这些方法来实现不同的功能。这个时候,我们就可以使用抽象方法了,但是这种方法是不完整的,仅有声明而没有方法体,下面就是抽象方法的语法:
/**
* 抽象类
*/
public abstract class AbstractSport {
/**
* 抽象方法
*/
abstract void play();
}
抽象方法由关键字 abstract 来修饰,并且不需要方法体,包含抽象方法的类叫做抽象类,抽象类命名以 Abstract 或者 Base 开头,如果一个类中包含一个或者多个抽象方法,该类就必须指定为抽象的,否则,编译器会报错。而且抽象类并不需要所有的方法都是抽象的,只需要将某些方法声明为抽象的即可。
由于无法直接为抽象类直接创建对象(因为编译器会报错),所以如果想创建抽象类的对象,就得先创建以抽象类为父类的子类,并且子类必须为父类中所有的抽象方法提供方法定义(即实现),然后再由子类去创建对象。如果子类没有实现父类中的抽象方法(哪怕是一个),那么子类便也是抽象类,而且必须加上 abstract 关键字。
我们已经知道抽象类和抽象方法的概念了,我们现在将上一章中的 Sport 类重新写一下:
/**
* 抽象类
* Created by FM on 2019/4/25.
*/
public abstract class AbstractSport {
/**
* 抽象方法
*/
public abstract void play();
public void what() {
System.out.println("sport");
}
public static void main(String[] args) {
//创建一个 AbstractSport 的对象集合
List<AbstractSport> sports = Arrays.asList(new Ball(), new Baseball());
//遍历结合
sports.forEach(sport -> {sport.play();sport.what();});
}
}
class Ball extends AbstractSport {
@Override
public void play() {
System.out.println("play ball");
}
public void what() {
System.out.println("ball");
}
}
class Baseball extends Ball {
public void play(){
System.out.println("play baseball");
}
public void what() {
System.out.println("baseball");
}
}
打印结果:
play ball
ball
play baseball
baseball
实际上除了父类,其他的没啥改变,抽象类和抽象方法可以使类的抽象性明确起来,并告诉用户如何去使用,而且它还能使用户可以很容易地将公共方法沿着继承层次结构向上移动。
接口
接口就是使用关键字 interface 来替代 class 关键字,它是一个极度抽象的类,将抽象的概念执行的更为彻底,它没有提供任何具体实现,它允许确定方法的方法名、参数列表和返回类型,但是没有方法体,只提供了形式,没有提供具体实现,也就是说全部都是抽象方法,而且这些抽象方法默认为 public 的。接口中也可以包含成员变量,但是这些成员变量都是隐式地指定为 static 和 final 的。任何使用接口的代码都知道可以调用该接口的那些方法,所以接口被用来建立类与类之间的协议。
但是在 Java8 中,允许在接口中定义静态 (static) 方法,使用方式和普通的静态一样,还允许在接口中定义 default 方法,这样可以为实现这个接口的所有的类增加新的功能,而不需要对这些类进行重新设计。
public interface SportInterface{
//成员(默认为 static final)
int NUM = 2;
/**
* 抽象方法(默认为 public)
*/
void play();
/**
* 静态方法
*/
static void staticTest() {
System.out.println("static test");
}
/**
* default 方法
*/
default void defaultTest() {
System.out.println("default test");
}
}
如果要让一个类实现某个接口,就需要使用 implements 关键字,这样这个类就变成了一个普通的类,而且需要实现这个接口中的所有抽象方法,还可以使用常规的方法来扩展它。我们现在来实现上面的接口:
public class Ball implements SportInterface{
/**
* 抽象方法实现
*/
@Override
public void play() {
System.out.println("play ball");
}
}
class Baseball extends Ball {
/**
* 重写 play 方法
*/
public void play() {
System.out.println("play baseball");
}
public static void main(String[] args) {
//创建一个 SportInterface 的对象集合
List<SportInterface> sports = Arrays.asList(new Ball(), new Baseball());
//遍历结合
sports.forEach(sport -> {
sport.play();
//default 方法调用
sport.defaultTest();
//static 方法调用
SportInterface.staticTest();
//打印成员变量
System.out.println("num:" + sport.NUM);
});
}
}
打印结果:
play ball
default test
static test
num:2
play baseball
default test
static test
num:2
可以看到,static 方法和 default 的调用和普通的类没有区别,但是有一点,就是接口中的 default 方法,即使不在同一个包也可以调用。