Java接口-详解
一、基本概念
接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合。接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
如果一个类只由抽象方法和全局常量组成,那么这种情况下不会将其定义为一个抽象类。只会定义为一个接口,所以接口严格的来讲属于一个特殊的类,而这个类里面只有抽象方法和全局常量,就连构造方法也没有。
范例:定义一个接口
interface A{//定义一个接口
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
二、接口的使用
1、由于接口里面存在抽象方法,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下:
(1)接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口;
(2)接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法;
(3)接口的对象可以利用子类对象的向上转型进行实例化。
范例:
package com.wz.interfacedemo;
interface A{//定义一个接口A
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
interface B{//定义一个接口B
public abstract void get();
}
class X implements A,B{//X类实现了A和B两个接口
@Override
public void print() {
System.out.println("接口A的抽象方法print()");
}
@Override
public void get() {
System.out.println("接口B的抽象方法get()");
}
}
public class TestDemo {
public static void main(String[] args){
X x = new X();//实例化子类对象
A a = x;//向上转型
B b = x;//向上转型
a.print();
b.get();
}
}
运行结果:
接口A的抽象方法print()
接口B的抽象方法get()
以上的代码实例化了X类的对象,由于X类是A和B的子类,那么X类的对象可以变为A接口或者B接口对象。我们把测试主类代码改一下:
public class TestDemo {
public static void main(String[] args){
A a = new X();
B b = (B) a;
b.get();
}
}
运行结果:
接口B的抽象方法get()
- 1
好,没任何问题,我们再来做个验证:
public class TestDemo {
public static void main(String[] args){
A a = new X();
B b = (B) a;
b.get();
System.out.println(a instanceof A);
System.out.println(a instanceof B);
}
运行结果:
接口B的抽象方法get()
true
true
我们发现,从定义结构来讲,A和B两个接口没有任何直接联系,但这两个接口却拥有同一个子类。我们不要被类型和名称所迷惑,因为实例化的是X子类,而这个类对象属于B类的对象,所以以上代码可行,只不过从代码的编写规范来讲,并不是很好。
2、对于子类而言,除了实现接口外,还可以继承抽象类。若既要继承抽象类,同时还要实现接口的话,使用一下语法格式:
class 子类 [extends 父类] [implemetns 接口1,接口2,...] {}
范例:
interface A{//定义一个接口A
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
interface B{//定义一个接口B
public abstract void get();
}
abstract class C{//定义一个抽象类C
public abstract void change();
}
class X extends C implements A,B{//X类继承C类,并实现了A和B两个接口
@Override
public void print() {
System.out.println("接口A的抽象方法print()");
}
@Override
public void get() {
System.out.println("接口B的抽象方法get()");
}
@Override
public void change() {
System.out.println("抽象类C的抽象方法change()");
}
}
对于接口,里面的组成只有抽象方法和全局常量,所以很多时候为了书写简单,可以不用写public abstract 或者public static final。并且,接口中的访问权限只有一种:public,即:定义接口方法和全局常量的时候就算没有写上public,那么最终的访问权限也是public,注意不是default。以下两种写法是完全等价的:
interface A{
public static final String MSG = "hello";
public abstract void print();
}
等价于
interface A{
String MSG = "hello";
void print();
}
但是,这样会不会带来什么问题呢?如果子类子类中的覆写方法也不是public,我们来看:
package com.wz.interfacedemo;
interface A{
String MSG = "hello";
void print();
}
class X implements A{
void print() {
System.out.println("接口A的抽象方法print()");
}
}
public class TestDemo {
public static void main(String[] args){
A a = new X();
a.print();
}
}
运行结果:
Exception in thread "main" java.lang.IllegalAccessError: com.wz.interfacedemo.X.print()V
at com.wz.interfacedemo.TestDemo.main(TestDemo.java:22)
这是因为接口中默认是public修饰,若子类中没用public修饰,则访问权限变严格了,给子类分配的是更低的访问权限。所以,在定义接口的时候强烈建议在抽象方法前加上public ,子类也加上:
interface A{
String MSG = "hello";
public void print();
}
class X implements A{
public void print() {
System.out.println("接口A的抽象方法print()");
}
}
3、在Java中,一个抽象类只能继承一个抽象类,但一个接口却可以使用extends关键字同时继承多个接口(但接口不能继承抽象类)。
范例:
interface A{
public void funA();
}
interface B{
public void funB();
}
//C接口同时继承了A和B两个接口
interface C extends A,B{//使用的是extends
public void funC();
}
class X implements C{
@Override
public void funA() {
}
@Override
public void funB() {
}
@Override
public void funC() {
}
}
由此可见,从继承关系来说接口的限制比抽象类少:
(1)一个抽象类只能继承一个抽象父类,而接口可以继承多个接口;
(2)一个子类只能继承一个抽象类,却可以实现多个接口(在Java中,接口的主要功能是解决单继承局限问题)
4、从接口的概念上来讲,接口只能由抽象方法和全局常量组成,但是内部结构是不受概念限制的,正如抽象类中可以定义抽象内部类一样,在接口中也可以定义普通内部类、抽象内部类和内部接口(但从实际的开发来讲,用户自己去定义内部抽象类或内部接口的时候是比较少见的),范例如下,在接口中定义一个抽象内部类:
interface A{
public void funA();
abstract class B{//定义一个抽象内部类
public abstract void funB();
}
}
在接口中如果使用了static去定义一个内接口,它表示一个外部接口:
interface A{
public void funA();
static interface B{//使用了static,是一个外部接口
public void funB();
}
}
class X implements A.B{
@Override
public void funB() {
}
}
三、接口的实际应用(标准定义)
在日常的生活之中,接口这一名词经常听到的,例如:USB接口、打印接口、充电接口等等。
如果要进行开发,要先开发出USB接口标准,然后设备厂商才可以设计出USB设备。
现在假设每一个USB设备只有两个功能:安装驱动程序、工作。
定义一个USB的标准:
interface USB { // 操作标准
public void install() ;
public void work() ;
}
在电脑上应用此接口:
class Computer {
public void plugin(USB usb) {
usb.install() ;
usb.work() ;
}
}
定义USB设备—手机:
class Phone implements USB {
public void install() {
System.out.println("安装手机驱动程序。") ;
}
public void work() {
System.out.println("手机与电脑进行工作。") ;
}
}
定义USB设备—打印机:
class Print implements USB {
public void install() {
System.out.println("安装打印机驱动程序。") ;
}
public void work() {
System.out.println("进行文件打印。") ;
}
}
定义USB设备—MP3:
class MP3 implements USB {
public void install() {
System.out.println("安装MP3驱动程序。") ;
}
public void work() {
System.out.println("进行MP3拷贝。") ;
}
}
测试主类:
public class TestDemo {
public static void main(String args[]) {
Computer c = new Computer() ;
c.plugin(new Phone()) ;
c.plugin(new Print()) ;
c.plugin(new MP3());
}
}
运行结果:
安装手机驱动程序。
手机与电脑进行工作。
安装打印机驱动程序。
进行文件打印。
安装MP3驱动程序。
进行MP3拷贝。
可以看出,不管有多少个USB接口的子类,都可以在电脑上使用。
在现实生活中,标准的概念随处可见,而在程序里标准使用接口定义的。
一、接口的应用—工厂设计模式(Factory)
我们先看一个范例:
package com.wz.factoryDemo;
interface Fruit{
public void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果。。。");
}
}
public class Client {
public static void main(String[] args) {
Fruit f = new Apple();
f.eat();
}
}
运行结果:
吃苹果。。。
以上程序非常简单,就是通过接口的子类为接口对象实例化,但这样操作会存在什么样的问题呢?在软件开发中,我们强调以下两点:
(1)主方法或主类是一个客户端,客户端的操作应该越简单越好;
(2)客户端之外的代码修改,不影响用户的使用。也就是说,用户可以不用去关心代码是否由变更。
确实,以上范例没有任何语法错误,但关键的问题是客户端中出现的new关键字上。因为,一个接口会有多个子类,对于上面的Furit接口,也可能出现多个子类对象。
来看范例,我们多加上一个接口子类:
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("吃橘子。。。");
}
}
客户端是若要得到这个新的子类对象,需要修改代码为:
public class Client {
public static void main(String[] args) {
//Fruit f = new Apple();
Fruit f = new Orange();
f.eat();
}
}
从上面我们发现,如果直接在客户端上产生一个实例化对象,那么我们每次要更换对象时,都需要修改客户端代码,这样的做法明显是不好的。而在整个代码中,我们最关心的是如何取得一个Fruit接口对象,然后进行方法的调用,至于这个接口对象时被谁实例化的,不是客户端关心的。这个问题就是代码耦合度太高!耦合度太高的产生的直接问题是代码不方便维护。
在本程序之中,最大的问题在于耦合上,发现在主方法中,一个接口和一个子类紧密耦合在一起,这种方式比较直接,可以简单的理解为由A —>B,但是这种紧密的方式不方便于维护,所以我们可以这样改:A—> C—>B,中间经历了一个过渡,这样一来,B改变,然后C去改变,但是A不需要改变。这可以参考Java中JVM的设计思想:程序—> JVM—>适应不同的操作系统。
于是,本程序我们这么修改,加上一个工厂类:
class Factory{
public static Fruit getInstance(String className){
if("apple".equals(className)){
return new Apple();
}else if("orange".equals(className)){
return new Orange();
}else{
return null;
}
}
}
然后修改客户端:
public class Client {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");
f.eat();
}
}
运行结果:
吃苹果。。。
这样的话,客户端不会看见具体的子类,客户端不再和一个具体的子类耦合在一起了,就算以后增加了新的子类,那么只需要修改Factory类即可实现,客户端的调用不会改变。
工厂模式的关系图如下:
从工厂模式关系图看出,客户端不和具体的子类耦合在一起,若要增加新的子类,只需要修改Factory类即可实现。
二、接口的应用—代理设计模式(Proxy)
Java代理设计模式单独讲解,请移步到Java设计模式之代理模式
所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
关系图如下:
代理设计模式的核心精髓就在于:有一个主题操作接口(接口中可能有多个方法),而核心业务主题只完成核心功能,而代理主题负责完成所有与核心主题有关的辅助性操作。
三、Java抽象类和接口的区别
通过上面的分析可以得出结论:在开发之中,抽象类和接口实际上都是可以使用的,并且使用那一个都没有明确的限制,可是抽象类有一个最大的缺点 : 一个子类只能够继承一个抽象类,存在单继承的局限。所以当遇到抽象类和接口都可以使用的情况下,优先考虑接口,避免单继承局限。
一些参考原则(根据自身情况参考):
(1)在进行某些公共操作的时候一定要定义出接口;
(2)有了接口就需要利用子类完善方法;
(3)如果是我们自己写的接口,尽量不要使用关键字new去直接实例化接口子类,要使用工厂类完成。
Java接口-详解的更多相关文章
- Java接口 详解(二)
上一篇Java接口 详解(一)讲到了接口的基本概念.接口的使用和接口的实际应用(标准定义).我们接着来讲. 一.接口的应用—工厂设计模式(Factory) 我们先看一个范例: package com. ...
- java 接口详解
定义接口 接口继承和实现继承的规则不同,一个类只有一个直接父类,但可以实现多个接口.Java 接口本身没有任何实现,只描述 public 行为,因此 Java 接口比 Java 抽象类更抽象化.Jav ...
- Java接口 详解(一)
一.基本概念 接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合.接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法. 如果一个类只由 ...
- “全栈2019”Java第八十四章:接口中嵌套接口详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第八十三章:内部类与接口详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 黑马----JAVA迭代器详解
JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...
- Java虚拟机详解----JVM常见问题总结
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- [转] Java内部类详解
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- Java面向对象详解
Java面向对象详解 前言:接触项目开发也有很长一段时间了,最近开始萌发出想回过头来写写以前学 过的基础知识的想法.一是原来刚开始学习接触编程,一个人跌跌撞撞摸索着往前走,初学的时候很多东西理解的也懵 ...
随机推荐
- 30k的测试简历长这样,进来抄作业!
面试求职: 「面试试题小程序」,内容涵盖 测试基础.Linux操作系统.MySQL数据库.Web功能测试.接口测试.APPium移动端测试.Python知识.Selenium自动化测试相关.性能测试. ...
- Dash 2.18.2版本更新:模式匹配回调性能大提升
本文示例代码已上传至我的Github仓库:https://github.com/CNFeffery/dash-master Gitee同步仓库地址:https://gitee.com/cnfeffer ...
- 每秒550万亿次算力!打破世界纪录!中国造全球首例纯电驱全尺寸人形机器人!直击全球最快人形机器人“天工”The world's first purely electric humanoid robot
地址: https://www.youtube.com/watch?v=uRc-885NpD4
- Quartz集群增强版_00.How to use?(如何使用)
Quartz集群增强版_00.How to use?(如何使用) 转载请著名出处 https://www.cnblogs.com/funnyzpc/p/18540378 开源地址 https://gi ...
- 题解:CF1301B Motarack's Birthday
CF1301D Time to Run 题解 思维题. 分析 把一个格子视作一个点,每个点的度数都是偶数,所以这是一张欧拉图.而需要走遍整个方格图,可以证明只要 \(k\) 不超过 \(4nm-2n- ...
- .NET周刊【11月第2期 2024-11-10】
国内文章 .NET 全能高效的 CMS 内容管理系统 https://www.cnblogs.com/1312mn/p/18511224 SSCMS 是一个完全开源的企业级内容管理系统,基于 .NET ...
- 外网如何通过https访问自己的服务
前情提要 最近在接wx小游戏的sdk, 消息推送需要在wx端配置https开头的地址. 我本地开了一个http服务,我的ip为192.168.40.39, 监听端口 8091.想要达到的效果是,外部浏 ...
- Threejs的三维坐标系
在三维空间中,所有的物体和相机都需要基于一个统一的坐标系来进行定位和操作.理解坐标系的基本概念,对于创建稳定.准确的三维效果至关重要. 基础 Three.js 采用的是右手坐标系,这意味着如果你将右手 ...
- springboot 参数注解 注入参数
什么时注解参数 说明 我们在 使用spring mvc 的时候会使用这样的注解 @ResponseBody 这样,spring mvc 会将 客户端传来的数据,自动构建成 相应类型的对象. 有些情况下 ...
- ChatGPT生成测试用例的最佳实践(四)
通常情况下还应该进行测试用例外部评审.将已完成的基于百度关键字搜索业务的功能和安全测试用例集的存放位置告知项目团队成员,需要预留出一定的时间,便于项目组研发.产品人员阅读,以免在项目团队测试用例评审会 ...