就像类一样,接口也是引用类型。它可以包含常量,方法签名,默认方法,静态方法和嵌套类型。在接口中,只有默认方法和静态方法有方法体。但不同于类,接口无法实例化,它只能被类实现或被其他接口继承。

一.定义接口

  接口的定义由修饰符、关键字interface、接口名、extends关键字(接口可以继承并且支持多继承)和若干继承的父接口(如果有的话)以及接口体组成,语法如下(方括号表示可选内容):

public interface DoIt [extends Interface1, Interface2, ...] [throws Exception1, Exception2, ...] {
void doSomething (int i, double x);
int doSomethingElse(String s);
}

  public修饰符代表这个接口可以在任何地方使用。如果没有指定接口是public的,那么这个接口只能在定义这个接口的包中使用。

  一个接口可以继承其他接口,就像一个类可以继承另外一个类一样。不同的是,一个类只能继承一个父类,而一个接口可以继承多个接口。

  接口体包含了若干个抽象方法,从Java8之后,接口中还可以包含默认方法和静态方法。抽象方法后直接跟分号,没有大括号,因为抽象方法不包含实现。默认方法使用default关键字定义,静态方法使用static关键字定义。接口中的所有方法都默认且必须是public的,因此可以省略方法前的public。

  此外,接口可以包含常量声明。在接口中定义的所有常量都默认是public,static和final的,因此可以省略这些修饰符。

二.实现接口

  要实现接口,需要在定义类时在类名后面使用implements关键字,然后跟着以逗号分隔的要实现的接口列表。按照惯例,如果类既继承了父类,又实现了接口,implements语句应该放在extends语句后面。

  下面定义了一个USB接口:

public interface USB {
void read();
void write();
}

  如果一个类实现了某个接口,则它必须实现这个接口中所有的抽象方法。假设现在我们要定义一个U盘类,它需要实现USB接口,那么它必须实现USB接口中定义的read和write方法:

public class UDisk implements USB {
@Override
public void read() {
System.out.println("Reading UDisk...");
} public void write() {
System.out.println("Writing UDisk...");
}
}

  当定义了一个新的接口时,实际上就是定义了一个新的引用数据类型。如果定义了一个接口类型的变量,则这个变量只能引用实现了这个接口的类的实例。例如:

USB usb = new UDisk();

  若接口的抽象方法中包含异常,那么在实现这个抽象方法时可以抛出异常列表中的全部或部分异常,以及这些异常的子类,甚至不抛出异常。有关异常的知识将会在后面的文章中进行介绍。

三.接口中的静态方法

  和类一样,接口也可以有静态方法。不同的是,在类中,既可以通过定义静态方法的类及其子类调用静态方法,也可以通过该类和子类的实例调用静态方法(可以但不推荐);在接口中,则只能通过定义静态方法的接口去调用静态方法,而不能通过实现它的类及其实例调用静态方法。接口的静态方法仅对于接口本身可见。例如下面的接口:

public interface Bar {
static void foo() {
System.out.println("I'm a static method.");
}
}

  可以通过Bar.foo()的形式调用这个静态方法。假设有个BarImpl实现了这个接口,那么无论是BarImpl.foo()或new BarImpl().foo()都会产生编译错误,因为这个静态方法对它们来说是不可见的。

四.默认方法

  从Java8之后,可以为接口中的方法提供一个默认实现。使用关键字default来表示这是一个默认方法。例如:

public interface Comparable<T> {
default int compareTo(T other) {
return 0;
}
}

  当然,这里的默认方法并没有太大用处,因为Comparable的每一个实现类都要覆盖这个方法。不过有些情况下,默认方法可能很有用。如果希望在发生鼠标点击事件时得到通知,就要实现一个包含5个方法的接口:

public interface MouseListener {
void mouseClicked(MouseEvent event);
void mousePressed(MouseEvent event);
void mouseReleaseed(MouseEvent event);
void mouseEntered(MouseEvent event);
void mouseExited(MouseEvent event);
}

  大多数情况下,我们只关心其中的一两个事件。可以将所有方法声明为默认方法,这些默认方法什么也不做:

public interface MouseListener {
default void mouseClicked(MouseEvent event) {}
default void mousePressed(MouseEvent event) {}
default void mouseReleaseed(MouseEvent event) {}
default void mouseEntered(MouseEvent event) {}
default void mouseExited(MouseEvent event) {}
}

  这样一来,实现这个接口时,只需要覆盖真正关心的事件方法就可以了。

  默认方法可以调用其他的默认方法、静态方法和抽象方法。例如,Collection接口可以定义一个便利方法:

public interface Collection {
int size();
default boolean isEmpty() {
return size() == 0;
}
}

  这样在实现Collection接口时就不用操心isEmpty方法了。

  默认方法的一个重要用法是“接口演化”。以Collection接口为例,这个接口作为Java的一部分已经很多年了。后来,在Java8中,又为这个接口增加了一个stream方法。假设stream不是一个默认方法,那么其他已经实现了Collection接口的类将不能编译,因为要实现接口就必须实现接口所有的抽象方法。这样一来,就要修改其他所有已经实现了Collection接口的类的代码,这显然不是我们愿意看到的。

  不过,如果我们为stream方法提供默认实现,那么问题就迎刃而解了。其他已经实现了Collection接口的类不用做任何修改,就可以通过编译,而且还可以在这些类的实例上调用stream方法。如果真的有部分类需要重新定义stream方法而不是使用默认的stream方法,那么工作量也会小很多。

  下面来讨论默认方法的继承问题。当一个接口继承一个包含默认方法的接口时,可以执行以下操作:

  1. 不做任何修改,完全继承父接口的默认方法;
  2. 重新声明这个默认方法,使它变成抽象方法;
  3. 重写这个默认方法。

  执行第1种和第3种操作时,接口的实现类既可以选择使用默认的实现,也可以覆盖这个默认的方法。但执行第2种操作时,则必须实现这个抽象方法。

Java基础教程(17)--接口的更多相关文章

  1. Java基础教程(18)--继承

    一.继承的概念   继承是面向对象中一个非常重要的概念,使用继承可以从逻辑和层次上更好地组织代码,大大提高代码的复用性.在Java中,继承可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法. ...

  2. Java基础教程(12)--深入理解类

    一.方法的返回值   当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行.当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行: 执行完方法中所有的语句: 遇到return语句: ...

  3. Java基础教程:注解

    Java基础教程:注解 本篇文章参考的相关资料链接: 维基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3 注解基础与高级应用:http: ...

  4. Java基础教程(5)--变量

    一.变量 1.变量的定义   正如上一篇教程<Java基础教程(4)--面向对象概念>中介绍的那样,对象将它的状态存在域中.但是你可能仍然有一些疑问,例如:命名一个域的规则和惯例是什么?除 ...

  5. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

  6. Java基础教程:泛型基础

    Java基础教程:泛型基础 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚 ...

  7. Java基础教程:反射基础

    Java基础教程:反射基础 引入反射 反射是什么 能够动态分析类能力的程序称为反射. 反射是一种很强大且复杂的机制. Class类 在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时 ...

  8. Java基础教程:面向对象编程[2]

    Java基础教程:面向对象编程[2] 内容大纲 访问修饰符 四种访问修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java 支持 4 种不同的访问权限. default ...

  9. Java基础教程:JDBC编程

    Java基础教程:JDBC编程 1.什么是JDBC JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库. JDBC A ...

随机推荐

  1. Code Chef GEOCHEAT(凸包+旋转卡壳+随机化)

    题面 传送门 题解 以下记\(S_i=\{1,2,3,...,i\}\) 我们先用凸包+旋转卡壳求出直径的长度,并记直径的两个端点为\(i,j\)(如果有多条直径随机取两个端点) 因为这个序列被\(r ...

  2. CPU 分支预测

    去年在安宁庄的时候, 有个同事阐述了一个观点:php中的if else  在执行时考虑到效率的原因,不会按我们的代码的顺序一条一条去试,而是随机找出一个分支,执行,如果不对,再随机找到一个分支 当时由 ...

  3. 2018春招-今日头条笔试题-第四题(python)

    题目描述:2018春招-今日头条笔试题5题(后附大佬答案-c++版) #-*- coding:utf-8 -*- class Magic: ''' a:用于存储数组a b:用于存储数组b num:用于 ...

  4. 网络游戏程序员须知 UDP vs TCP(转)

    本文为作者原创或翻译,转载请注明,不得用于商业用途. 作者:rellikt@gmail.com 首发链接:http://blog.csdn.net/rellikt/archive/2010/08/21 ...

  5. Filter应用之-验证用户是否已经登录

    过滤器: public class LoginFilter implements Filter{ @Override public void init(FilterConfig filterConfi ...

  6. 解决ajax跨域问题的一种方法

    解决ajax跨域问题的一种方法 前后端分离经常用json来传输数据,比较常见的问题就有ajax跨域请求的错误问题,这里是我的一种解决方法: 在java中加入如下的注解类: import org.spr ...

  7. JDK1.10+scala环境的搭建之windows环境

    第一步:安装jdk 1,http://www.oracle.com/technetwork/java/javase/downloads/jdk10-downloads-4416644.html 去找下 ...

  8. Php开发银行接口之浦发银行

    Php开发银行接口之浦发银行 (提示:下面的经验都是按照开发文档一步一步踩坑过来的,但是不能不看开发文档!!!) 第一步:开发准备 1,安装java,百度下载JDK很方便(我自己网盘有,然后配置环境变 ...

  9. Web服务端性能提升实践

    随着互联网的不断发展,日常生活中越来越多的需求通过网络来实现,从衣食住行到金融教育,从口袋到身份,人们无时无刻不依赖着网络,而且越来越多的人通过网络来完成自己的需求. 作为直接面对来自客户请求的Web ...

  10. 利用ggplot2画出各种漂亮图片详细教程

    1.Why use ggplot2 ggplot2是我见过最human friendly的画图软件,这得益于Leland Wilkinson在他的著作<The Grammar of Graphi ...