目录

在Java中,接口是一个抽象类型,有点类似于类,但Java接口只能包含方法签名与属性,不能包含括方法的实现。


Java接口示例

public interface MyInterface {
public String hello = "hello";
public void sayHello();
}

如上所示,java接口是使用关键词interface声明的。就像类一样,Java接口可以被声明为public或者包范围(无修饰符)。

上面的接口包含了一个变量和一个方法。这个变量可以直接通过这个接口访问,就像这样:

System.out.println(MyInterface.hello);

如上所示,访问接口中的变量与从类中访问静态变量非常相似。(接口中的变量被默认声明为:public static final

但是,在访问接口中的方法时,必须先从类中实现该方法,下面将解释如何实现该方法。

实现一个接口

在真正使用接口之前,你必须在Java类中实现该接口。下面的MyInterfaceImpl类实现了上面的MyInterface接口:

public class MyInterfaceImpl implements MyInterface {

    public void sayHello() {
System.out.println(MyInterface.hello);
}
}

注意上面MyInterface接口的声明部分,其中implements标识告诉了Java编译器MyInterfaceImpl类实现了MyInterface接口。

实现某接口的类必须实现该接口中声明的所有方法,实现接口方法时必须使用和接口声明中完全一样的签名( 名称 + 参数 ),接口中的变量不需要类来实现,仅仅需要实现方法。

接口实例

一旦一个Java类实现了一个Java接口,你就可以使用Java类的实例作为该接口的实例。请看下面的示例:

MyInterface myInterface = new MyInterfaceImpl();

myInterface.sayHello();

注意创建MyInterface类型接口实例时,引用了MyInterfaceImpl类创建的对象。Java之所以允许这么做,是因为类MyInterfaceImpl实现了MyInterface接口。你可以将MyInterfaceImpl类的实例作为MyInterface接口的实例。

你不能直接创建Java接口的实例,你必须始终先创建实现某个接口的类的实例,并引用该实例作为接口的实例。

实现多个接口

一个Java类中可以实现多个Java接口。在这种情况情况下,该类必须实现所有所实现接口声明中的所有方法。这里有个示例:

public class MyInterfaceImpl
implements MyInterface, MyOtherInterface { public void sayHello() {
System.out.println("Hello");
} public void sayGoodbye() {
System.out.println("Goodbye");
}
}

这个类实现了MyInterfaceMyOtherInterface两个接口,在implements关键词之后列出需要实现的接口的名称,使用逗号分隔。

如果接口与实现接口的类不在同一个包中,你还需要先导入接口。Java接口是使用import标识导入的,就像类一样。例如:

import com.jenkov.package1.MyInterface;
import com.jenkov.package2.MyOtherInterface; public class MyInterfaceImpl implements MyInterface, MyOtherInterface {
...
}

下面是由上面的类实现的两个Java接口:

public interface MyInterface {

    public void sayHello();
} public interface MyOtherInterface { public void sayGoodbye();
}

如你所见,每个接口都包含一个方法,这些方法由类MyInterfaceImpl实现。

方法签名重叠

如果一个Java类实现了多个接口,那么有些接口可能含有相同的方法签名(名称+参数)的风险,由于Java类中一个签名只能实现一次,所以这可能会导致一些问题。

Java规范中没有给出解决这个问题的解决方案,在这种情况下该怎么做由你自己决定。

接口变量

Java接口可以包含变量和常量。然而,通常在Java接口中包含变量是没有意义的。在某些情况下,在Java接口中定义常量是有意义的。特别是这些常量被实现接口的类使用,例如在计算中,或者作为接口中某些方法的参数。然而,我的建议是,如果可以的话,避免在Java接口中放置变量。

所有的变量在接口中都是公共的,即便你在变量中省略了public关键词。

接口方法

一个Java接口中可以包含一个或多个方法的声明。如前面所述,Java接口不能为这些方法指定任何实现。它由实现接口的类来指定实现。

所有的方法在接口中都是公共的,即便你在方法中省略了public关键词。

接口中可以包含静态方法。

接口默认方法

在Java 8之前,Java接口不能包含接口的实现,只能包含方法签名。但是,当API需要向一个接口添加一个方法时,这就会导致一些问题。如果API只是将该方法添加到所需的接口中,则实现该接口的所有类都必须实现该新方法。如果所有实现类都位于该API中,那当然没有问题。但是,如果某些实现接口的类位于使用该API的客户端代码中,则该代码会被中断。

让我们举例来说明这一点。看看下面这个接口,想象它是一个开源API的一部分,许多应用程序都在内部使用它。

public interface ResourceLoader {

    Resource load(String resourcePath);

}

现在假设一个项目使用了这个API,并通过下面的FileLoader类实现了ResourceLoader接口:

public class FileLoader implements ResourceLoader {

    public Resource load(String resourcePath) {
// 这里是实现 +
// 一个返回语句.
}
}

如果该API的开发人员想在ResourceLoader接口中添加一个新方法,当项目升级到新版本的API时,FileLoader类将被破坏。

为了缓解Java接口中的扩展问题,Java 8中新增了"接口默认方法"这个概念。接口的默认方法可以包含接口的默认实现。实现了该接口的类,但不包含默认接口方法的类,将自动获得默认方法的实现。

使用default关键词将方法标记为默认方法,下面是一个向ResourceLoader接口添加默认方法的示例:

public interface ResourceLoader {

    Resource load(String resourcePath);

    default Resource load(Path resourcePath) {
// 提供默认实现
// 以从指定路径加载资源
// 并返回对象中的内容
} }

这个示例添加了默认方法load(Path),这个实现省略了实际实现(在方法内部),因为这并不重要,重要的是告诉你如何声明接口中的默认方法。

类可以通过显示实现接口默认方法来覆盖接口默认方法的实现,正如在类中实现接口中的其他方法一样,类中的任何方法实现都优先于接口默认方法实现。

接口与继承

Java接口可以从另外一个Java接口中继承,就像类可以从其他类中继承一样。你可以使用extends来指定继承。下面是一个简单的接口继承示例:

public interface MySuperInterface {

    public void saiHello();

}
public interface MySubInterface extends MySuperInterface {

    public void sayGoodbye();
}

MySubInterface接口继承于MySuperInterface接口。这意味着,MySubInterface将继承MySuperInterface所有的属性和方法,实现接口的类必须实现MySubInterfaceMySuperInterface接口中所有的方法。

在子接口中可以定义与父接口中具有相同签名(名称+参数)的方法。

与类的继承不同是,子接口可以继承多个父接口。列出所有你想要继承的接口名称,以逗号分隔。要实现继承多个父接口的子接口,实现接口的类必须实现子接口与所有父接口中的所有方法。

下面是一个继承多个父接口的示例:

public interface MySubInterface extends
SuperInterface1, SuperInterface2 { public void sayItAll();
}

在实现多个接口时,多个父接口具有相同的签名时,没有规则来说明这种情况。

继承与默认方法

接口默认方法为接口继承增加了复杂性。虽然通常一个类可以实现多个接口,即使接口之间具有相同签名的方法,但是如果这些方法中的一个或多个是默认方法,若没有在类中覆盖此方法则会报错。换言之,如果两个接口包含相同的方法签名(名称+参数),而其中一个接口将此方法声明为默认方法,则类不能自动实现这个方法(可以在实现接口的类中显示覆盖该默认方法)。

如果一个接口继承自多个接口,并且其中一个或多个接口包含具有相同签名的方法,并且其中一个父接口将重叠的方法声明为默认方法,则情况和上面相同。

在上述两种情况下,编译器要求实现接口的类需要显示的实现导致问题的方法。这样的话,这个类的实现就没有问题了。类中的实现优先于任何默认实现。

接口与多态性

Java接口是实现多态性的一种手段。多态性是一个需要实践和思考才能掌握的概念。基本上,多态性意味着类(对象)的实例可以作为不同的类型去使用。在这里,类型指的是一个类或接口。

看看这个简单的类图:

在同一个应用程序中使用两个并行的类层次结构

上面的类模型代表不同类型的车辆和司机,使用属性和方法来描述它们,这就是类的责任——从现实生活中对这些实体进行建模。

现在假设你需要将这些对象存储在数据库中,并将他们序列化为XML、JSON或其他格式。你现在希望在轿车、卡车或车辆对象中使用相同的方法进行操作。这时候就需要实现store()serializeToXML()serializeToJSON()这三个方法操作所有对象。

请先忘记上面这些,假如这些功能直接使用对象中的方法来实现的话,可能会导致混乱的类层次结构。这并不是你希望的方法。

在上面的图表中,你会把这三种方法放在哪里,以便在所有类上都可以访问。

解决这个问题的一种常见方法是为车辆和司机这两个类创建一个超类,它具有存储和序列化的方法。然而,这将导致概念上的混淆,类层次结构不再为车辆和司机建模,而且还会使车辆和司机与“存储和序列化机制”相关联。

更好的解决方案是创建一些存储和序列化方法有关的接口,并让类实现这些接口。下面是此类接口的示例:

public interface Storable {
// 存储
public void store();
}
public interface Serializable {
// 序列化
public void serializeToXML(Writer writer);
public void serializeToJSON(Writer writer);
}

当每个类需要实现这两个接口及其方法时,可以通过将对象强制转化为该接口类型的实例来访问这些接口的方法。你不需要确切的知道给定对象的类型是什么,只需要知道它实现的接口,下面是一个示例:

Car car = new Car();

Storable storable = (Storable) car;
storable.store(); Serializable serializable = (Serializable) car;
serializable.serializeToXML (new FileWriter("car.xml"));
serializable.serializeToJSON(new FileWriter("car.json"));

正如你希望的那样,在类中,接口提供了一个比继承更干净的实现跨服务功能的方法。

Java 接口基础详解的更多相关文章

  1. java封装基础详解

    java封装基础详解 java的封装性即是信息隐藏,把对象的属性和行为结合成一个相同的独立单体,并尽可能地隐藏对象的内部细节. 封装的特性是对属性来讲的. 封装的目标就是要实现软件部件的"高 ...

  2. java继承基础详解

    java继承基础详解 继承是一种由已存在的类型创建一个或多个子类的机制,即在现有类的基础上构建子类. 在java中使用关键字extends表示继承关系. 基本语法结构: 访问控制符 class 子类名 ...

  3. Java :内部类基础详解

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 第一次见面 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass { pr ...

  4. Java多线程基础详解

    基础概念进程进程是操作系统结构的基础:是一次程序的执行:是一个程序及其数据在处理机上顺序执行时所发生的活动.操作系统中,几乎所有运行中的任务对应一条进程(Process).一个程序进入内存运行,即变成 ...

  5. Java 异常基础详解

    目录 1. Java 中的异常 1.1 什么是异常? 1.2 什么是异常处理? 1.2.1 异常处理的优势 1.3 Java 异常类的层次结构 1.4 异常类型 1.5 检查和未检查异常之间的区别 1 ...

  6. 浅析Java 数组-基础详解

    什么是数组:数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同. Java 数组:用来存储固定大小的同类型元素. 一 声明.创建,初始化Java 数组 写在前面 ...

  7. Java语言Socket接口用法详解

    Socket接口用法详解   在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...

  8. Java之函数式接口@FunctionalInterface详解(附源码)

    Java之函数式接口@FunctionalInterface详解 函数式接口的定义 在java8中,满足下面任意一个条件的接口都是函数式接口: 1.被@FunctionalInterface注释的接口 ...

  9. 最新java数组的详解

    java中HashMap详解 http://alex09.iteye.com/blog/539545 总结: 1.就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java ...

随机推荐

  1. 使用localStorage保存搜索记录

    //过滤一个结果的空记录添加,过滤空搜索 默认保存10条记录,自己可修改 function setHistoryItems(keyword) { keyword = keyword.replace(& ...

  2. Oracle安装11.2.0.4.180116补丁及如何检查数据库安装补丁

    最近做了一个安装11.2.0.4.180116补丁的实验,突然想起之前和同事讨论的一个问题:如何检查数据库安装补丁的版本,之前搜到的是去查dba_registry_history,有的说在操作系统中执 ...

  3. Spring Boot + Freemarker多语言国际化的实现

    最近在写一些Web的东西,技术上采用了Spring Boot + Bootstrap + jQuery + Freemarker.过程中查了大量的资料,也感受到了前端技术的分裂,每种东西都有N种实现, ...

  4. 控制反转( IoC)和依赖注入(DI)

    控制反转( IoC)和依赖注入(DI) tags: 容器 依赖注入 IOC DI 控制反转 引言:如果你看过一些框架的源码或者手册,像是laravel或者tp5之类的,应该会提到容器,依赖注入,控制反 ...

  5. Struct_2路径问题

    今天在自学那个Struct2的知识点的时候,发现那个相对路径和绝对路径有点遗忘.特地去看了视频还有在百度上查了一些资料.我觉得这个路径问题对于我这个初学者来说还是有点容易遗忘的.所以,今天就添加这个新 ...

  6. web.config中configSections section节 -Z

    由于最近一个项目的数据库变动比较频繁, 为了减少数据层的负担, 打算采用.net的MVC框架, 使用LINQ对付数据层.       这个框架的web.config文件里出现了configSectio ...

  7. 201621123031 《Java程序设计》第4周学习总结

    Week04-面向对象设计与继承 1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 关键词:继承.覆盖.多态.抽象 1.2 尝试使用思维导图将这些关键词组织起来. 1.3 可选: ...

  8. 201621123031 《Java程序设计》第9周学习总结

    作业09-集合与泛型 1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 一.泛型的基本概念 泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Paramet ...

  9. 详谈C++虚函数表那回事(一般继承关系)

    沿途总是会出现关于C++虚函数表的问题,今天做一总结: 1.什么是虚函数表: 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table. ...

  10. 利用Python爬取新浪微博营销案例库并下载到本地

    from bs4 import BeautifulSoup import requests,urllib.request,urllib.parse import json import time im ...