一分钟教会你Java接口

在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);
    }
}//加入Java开发交流君样:756584822一起吹水聊天

注意上面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");
    }
}//加入Java开发交流君样:756584822一起吹水聊天

这个类实现了MyInterface和MyOtherInterface两个接口,在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);

}//加入Java开发交流君样:756584822一起吹水聊天

现在假设一个项目使用了这个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) {
        // 提供默认实现
        // 以从指定路径加载资源
        // 并返回对象中的内容
    }
//加入Java开发交流君样:756584822一起吹水聊天
}

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

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

接口与继承

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

public interface MySuperInterface {

    public void saiHello();

}//加入Java开发交流君样:756584822一起吹水聊天
public interface MySubInterface extends MySuperInterface {

    public void sayGoodbye();
}

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

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

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

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

public interface MySubInterface extends
    SuperInterface1, SuperInterface2 {

    public void sayItAll();
}//加入Java开发交流君样:756584822一起吹水聊天

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

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

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

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

接口与多态性

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

看看这个简单的类图:

接口

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

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

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

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

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

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

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

public interface Storable {
    // 存储
    public void store();
}//加入Java开发交流君样:756584822一起吹水聊天
public interface Serializable {
    // 序列化
    public void serializeToXML(Writer writer);
    public void serializeToJSON(Writer writer);
}

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

Car car = new Car();

Storable storable = (Storable) car;
storable.store();
//加入Java开发交流君样:756584822一起吹水聊天
Serializable serializable = (Serializable) car;
serializable.serializeToXML (new FileWriter("car.xml"));
serializable.serializeToJSON(new FileWriter("car.json"));

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

最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,需要获取这些内容的朋友请加Q君样:756584822

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值