可以这样认为,抽象类就是普通类抽象化的结果,它与普通类相比,同样具有属性、方法等,唯一的区别就是抽象类具有抽象的效果,即无法被实例化(如果可以被实例化,就失去抽象的意义了)。抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。而一些具体类(相对于抽象类而言的普通类)的父类一般就是抽象类或者其他基类。
这里要说明的一点就是,抽象类 ≠ 基类。基类一般而言都是非抽象类,从名字上可以知道,基类意为基本的类,应该是多个子类的同时继承的一个父类,此父类拥有这些子类所共用的属性和方法,也就是说,基类是这些子类的基本。而抽象类是具体类的抽象,两者完全不一样。此外,基类只是开发人员对多个子类的共同父类的一种说法而已,Java 中并没有专门的基类对象,但是抽象类是有的。
抽象类通过在 class 关键字前面添加 abstract 关键字来声明:
abstract class AbstractClassExample {}
抽象类无法被实例化,因此,当你执行以下语句时,将会触发报错:
abstract class AbstractClassExample {}
public class Test {
public static void main(String[] args) {
AbstractClassExample abstractClassExample = new AbstractClassExample();
// Error: AbstractClassExample 是抽象的; 无法实例化
}
}
因此,我们应该通过继承抽象类来使用抽象类。抽象类的继承和普通类无异。
与抽象类相似,方法也可以被抽象化,这样的方法被称为抽象方法,其与普通方法的差异和抽象方法与普通方法的差异类似,但略有一些不同。抽象方法既然是抽象的,那就没有具体的实现过程,因此也就没有方法体了(就算有,也只能是空的,还不如没有,因此不允许有方法体)。
抽象方法的声明如下:
abstract class AbstractClassExample {
abstract void abstractMethod(); // 此处的返回类型没有强制为 void,这里只是举例
}
这里要注意的几点是:
如果具体类不重写抽象方法,将会触发报错:
abstract class AbstractClassExample {
abstract void abstractMethod();
}
class ConcreteChildClass extends AbstractClassExample {}
// Error: ConcreteChildClass不是抽象的, 并且未覆盖AbstractClassExample中的抽象方法abstractMethod()
那么,有一个问题,既然抽象类无法被实例化,那它能不能有构造方法呢?按道理说,不能被实例化,构造方法就没有用啊,存在的意义是什么呢?
但其实,在 Java 中,抽象类是可以有构造方法的。尽管抽象类无法被实例化,看似抽象类没有任何作用,但其实它可以被具体类继承,从而调用相关的方法。抽象类本就是具体类的抽象化,其本身具有抽象意义,但并不代表它不能拥有构造方法。
接口在 Java 编程语言中是一个抽象类型,是抽象方法的集合,接口通常以 interface 关键字来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法(要实现,但并没有真的实现,只是描述有没有这样一个方法)。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口通过 interface 关键字来声明,interface 翻译过来就是接口的意思。
interface InterfaceExample {}
注意,接口是隐式抽象的,不需要显示对其使用 abstract 关键字进行修饰,其方法也是隐式抽象的,不必添加 abstract 关键字,当然,加了也不会报错,但是没必要。接口的方法也都是隐式公有的,因此也无需对其方法添加 public 关键字,加了不会报错,但是没必要。
注意,接口的方法隐式抽象并非指其只能为抽象的,也可以是非抽象的方法,如通过 static 关键字进行修饰后的方法就不是抽象方法。
接口是隐式抽象的,无法被实现,因此下面的代码将会报错:
abstract interface InterfaceExample {}
public class Test {
public static void main(String[] args) {
InterfaceExample interfaceExample = new InterfaceExample();
// Error: InterfaceExample 是抽象的; 无法实例化
}
}
注意上面的报错信息,并没有说接口是抽象类,只是说它是抽象的,无法被实现。接口不是类,尽管两者在很多地方非常相似。
接口的实现有点类似于抽象类的实现,不过“继承”接口的方式不一样,不是通过 extends 关键字,而是通过 implements 关键字。implements 翻译过来就是实现的意思。
实现其的必须是类,如果这个类是抽象类,那么其不必将接口中的方法全部实现,如果是具体类,则需要将接口中全部的方法都实现,因为这些方法是隐式抽象的。
接口的实现,为什么要用 implements 关键字来声明,而不是继续用 extends 关键字呢?难道仅仅只是因为含义不同吗?这其实并不是 Java 当初故意设计的,而是因为 extends 关键字对于接口而已另有它用。实际上,类与接口之间的关系是下面这样的:
类与接口的关系上图中,抽象类被包含在类里面了。“实现”(implements)一定会真正地实现方法的功能,即有方法体,且方法体中有具体的内容,而继承只是将其父类(父接口)的属性和方法“拿”过来用。简单说,“继承”就是“是不是”的问题,而“实现”就是“有没有”的问题。
可以这样简单的理解,接口之间的继承就类似于抽象类之间的继承,因为抽象类和接口真的非常相似,但必须明确,它们不是同一个东西,接口甚至都不是类。
此外,要注意,继承时只能继承一个类或者接口,但是实现可以一次多个接口。