简介
如果一个类之中只是由抽象方法和全局常量所组成的,那么在这种情况下会将其定义为一个抽象类(因为抽象类中含有构造方法),而只会定义为接口,所以所谓的接口严格来讲就属于一个特殊的类,而且这个类中只有抽象方法与全局常量(是没有构造方法的)。
要定义一个接口使用 interface 关键字完成。
接口的重要性
Java 接口体现的是一种规范和分离的程序设计原则,充分利用接口可以降低程序各个模块之间的耦合度,从而提高系统的可扩展性和可维护性。包括我们现在最火的 Spring 框架都是“面向接口”编程。
按照组件的开发模型(3C),面向对象、面向过程和接口各司一面,缺一不可。具体如下:
- 面向对象是指在考虑问题时,以对象为单位,考虑它的属性以及方法。
- 面向过程是指在考虑问题时,以一个具体的流程(事务过程)为单位来考虑它的实现
- 接口设计与非接口设是针对复用技术而言,它与面对对象(过程)并不是同一个问题。
接口的定义
接口的定义语法
public interface 接口名 {
常量
抽象方法
}
接口定义的实例
interface A {
public static final String MSG = "Hello"; // 全局常量
public abstract void print(); // 抽象方法
}
相关说明如下:
- public : 接口的修饰符只能是 public。 只有这样,接口才能被任何包中的接口或类访问。
- interface:接口的关键字。声明接口必须要使用此关键字。
- 常量:接口中不能声明变量,因为接口需要具备三个特征,即公共性、静态性和最终性。
- 方法:必须是 public abstract 修饰的。
接口的使用
由于接口里面存在有抽象方法,所有接口对象不能使用关键字 new 来实例化操作。
接口的使用原则如下:
- 接口必须要有子类,但是此时一个子类可以使用 implements 关键字实现多个接口;
- 接口的子类(如果不是抽象类),必须要覆写接口中的全部抽象方法。
- 接口的对象可以利用子类对象的向上转型来进行实例化操作。
代码实例
接口的子类使用 implements 实现多个接口,以及向上转型的示例
interface A{ // 定义一个接口
public abstract void print(); // 接口的抽象方法
}
interface B{ // 定义一个接口
public abstract void get(); // 接口的抽象方法
}
class X implements A, B{
@Override
public void get() {
System.out.println("B接口的中的 get方法");
}
@Override
public void print() {
System.out.println("A接口的中的 print方法");
}
}
public class TestDemo {
public static void main(String[] args) {
X x = new X();
x.get(); // B接口的中的 get方法
x.print(); // A接口的中的 print方法
// 向上转型
A a = x;
B b = x;
a.print(); // A接口的中的 print方法
b.get(); // B接口的中的 get方法
}
}
基于上面的代码,看一个问题:
...省略接口的定义(同上面的代码一致)
public class TestDemo {
public static void main(String[] args) {
A a = new X();
B b = (B) a;
b.get();
}
}
执行之后,输出的结果为:B接口的中的 get方法。
在定义结构上面来看 A 和 B 接口没有任何的直接联系,但是这两个接口有一个共同的子类 X,此时我们不能被类型和名称迷糊了。虽然说 A a = new X()
执行了之后,实例化的是 a 接口对象,但是骨子里还是 X,此时的 X 也还是 B 的子类。所以是可以正确执行的。
抽象类继承接口的情况
interface A{ // 定义一个接口
public abstract void print(); // 接口的抽象方法
}
// 抽象类是可以直接继承于接口
abstract class C implements A{ // 定义一个抽象类去实现 A 接口
// 由于是抽象类,所以可以不用覆写 A 类中的抽象方法
public abstract void show(); //抽象类中的抽象方法
}
// 不同类继承于抽象类(此时的抽象类是实现了接口)
class G extends C{
@Override
public void print() {
System.out.println("G类-来自接口中的方法");
}
@Override
public void show() {
System.out.println("G类-来自抽象类中的方法");
}
}
public class TestDemo {
public static void main(String[] args) {
G g = new G();
g.print(); // G类-来自接口中的方法
g.show(); // G类-来自抽象类中的方法
C c = new G();
c.print(); // G类-来自接口中的方法
c.show(); // G类-来自抽象类中的方法
A a = new G();
a.print(); // G类-来自接口中的方法
// a.show(); 这行代码编译报错
}
}
通过上面的代码测试我们可以看到:
- 抽象类在实现接口时,可以不用覆写接口的抽象方法
- 抽象类实现了接口,然后使用子类向上转型实例化该抽象类时,是可以调用覆写了之后的接口方法以及该抽象类中的方法。
- 接口在通过向上转型实例化时,只能调用自己本身的抽象方法。
一个重要概念
对于接口而言,里面的组成就是抽象方法和全局常量,所以在 Java 中为了省略,可以不用写上 abstract 或者 public static final ,并且在方法上面是否编写 public 结果都是一样的,接口里面的方法只能定义为 public 访问权限
根据省略的概念来改造上面的代码
普通类继承抽象类并且实现接口 A
interface A{ // 定义一个接口
void print(); // 接口的抽象方法
}
abstract class C { // 定义一个抽象类去实现 A 接口
public abstract void show(); //抽象类中的抽象方法
}
// 普通类G 继承抽象类并且实现接口 A
class G extends C implements A{
@Override
public void print() {
System.out.println("G类-来自接口中的方法");
}
@Override
public void show() {
System.out.println("G类-来自抽象类中的方法");
}
}
public class TestDemo {
public static void main(String[] args) {
G g = new G();
g.print(); // G类-来自接口中的方法
g.show(); // G类-来自抽象类中的方法
C c = new G();
// c.print(); // 这行代码编译报错
c.show(); // G类-来自抽象类中的方法
A a = new G();
a.print(); // G类-来自接口中的方法
// a.show(); 这行代码编译报错
}
}
接口的多继承
一个抽象类可以去继承一个抽象类,但是反过来,一个接口却可以使用 extends 继承多个接口(接口不能继承抽象类)。
interface A{
public void funA();
}
interface B{
public void funB();
}
// c接口同时继承了A和B两个父接口
interface C extends A,B{ // 此处使用的是 extends
public void funC();
}
// 一个普通内实现 C,此时需要覆写所有的接口的方法
class X implements C{
@Override
public void funA() {
// TODO Auto-generated method stub
}
@Override
public void funB() {
// TODO Auto-generated method stub
}
@Override
public void funC() {
// TODO Auto-generated method stub
}
}
接口中定义抽象类
接口里面可以定义普通内部类、抽象内部类、内部接口。
定义内部抽象类
interface A{
public void funA();
abstract class B{
public abstract void funB();
}
}
class X implements A{
@Override
public void funA() {
System.out.println("+++++++++++");
}
}
定义内部接口
interface A{
public void funA();
static interface B{
public void funB();
}
}
class X implements A.B{
@Override
public void funB() {
System.out.println("+++++++++++");
}
}