可能是把 Java 接口讲得最通俗的一篇文章(2)

简介: 可能是把 Java 接口讲得最通俗的一篇文章

03、接口可以做什么


1)使某些实现类具有我们想要的功能,比如说,实现了 Cloneable 接口的类具有拷贝的功能,实现了 Comparable 或者 Comparator 的类具有比较功能。


Cloneable 和 Serializable 一样,都属于标记型接口,它们内部都是空的。实现了 Cloneable 接口的类可以使用 Object.clone() 方法,否则会抛出 CloneNotSupportedException。


public class CloneableTest implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest c1 = new CloneableTest();
        CloneableTest c2 = (CloneableTest) c1.clone();
    }
}



运行后没有报错。现在把 implements Cloneable 去掉。


public class CloneableTest {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        CloneableTest c1 = new CloneableTest();
        CloneableTest c2 = (CloneableTest) c1.clone();
    }
}



运行后抛出 CloneNotSupportedException:


Exception in thread "main" java.lang.CloneNotSupportedException: com.cmower.baeldung.interface1.CloneableTest
  at java.base/java.lang.Object.clone(Native Method)
  at com.cmower.baeldung.interface1.CloneableTest.clone(CloneableTest.java:6)
  at com.cmower.baeldung.interface1.CloneableTest.main(CloneableTest.java:11)



至于 Comparable 和 Comparator 的用法,感兴趣的读者可以参照我之前写的另外一篇文章《来吧,一文彻底搞懂Java中的Comparable和Comparator》。


http://www.itwanger.com/java/2020/01/04/java-comparable-comparator.html

2)Java 原则上只支持单一继承,但通过接口可以实现多重继承的目的。


可能有些读者会问,“二哥,为什么 Java 只支持单一继承?”简单来解释一下。


如果有两个类共同继承(extends)一个有特定方法的父类,那么该方法会被两个子类重写。然后,如果你决定同时继承这两个子类,那么在你调用该重写方法时,编译器不能识别你要调用哪个子类的方法。这也正是著名的菱形问题,见下图。


image.png


ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重载的方法时,就不知道该调用 ClassA 的方法,还是 ClassB 的方法。


接口没有这方面的困扰。来定义两个接口,Fly 会飞,Run 会跑。


public interface Fly {
    void fly();
}
public interface Run {
    void run();
}

然后让一个类同时实现这两个接口。


public class Pig implements Fly,Run{
    @Override
    public void fly() {
        System.out.println("会飞的猪");
    }
    @Override
    public void run() {
        System.out.println("会跑的猪");
    }
}



这就在某种形式上达到了多重继承的目的:现实世界里,猪的确只会跑,但在雷军的眼里,站在风口的猪就会飞,这就需要赋予这只猪更多的能力,通过抽象类是无法实现的,只能通过接口。


3)实现多态。


什么是多态呢?通俗的理解,就是同一个事件发生在不同的对象上会产生不同的结果,鼠标左键点击窗口上的 X 号可以关闭窗口,点击超链接却可以打开新的网页。


多态可以通过继承(extends)的关系实现,也可以通过接口的形式实现。来看这样一个例子。


Shape 是表示一个形状。
public interface Shape {
    String name();
}
圆是一个形状。
public class Circle implements Shape {
    @Override
    public String name() {
        return "圆";
    }
}
正方形也是一个形状。
public class Square implements Shape {
    @Override
    public String name() {
        return "正方形";
    }
}



然后来看测试类。


List<Shape> shapes = new ArrayList<>();
Shape circleShape = new Circle();
Shape squareShape = new Square();
shapes.add(circleShape);
shapes.add(squareShape);
for (Shape shape : shapes) {
    System.out.println(shape.name());
}



多态的存在 3 个前提:


1、要有继承关系,Circle 和 Square 都实现了 Shape 接口

2、子类要重写父类的方法,Circle 和 Square 都重写了 name() 方法

3、父类引用指向子类对象,circleShape 和 squareShape 的类型都为 Shape,但前者指向的是 Circle 对象,后者指向的是 Square 对象。


然后,我们来看一下测试结果:


正方形

1

2

也就意味着,尽管在 for 循环中,shape 的类型都为 Shape,但在调用 name() 方法的时候,它知道 Circle 对象应该调用 Circle 类的 name() 方法,Square 对象应该调用 Square 类的 name() 方法。


04、接口与抽象类的区别


好了,关于接口的一切,你应该都搞清楚了。现在回到读者春夏秋冬的那条留言,“兄弟,说说抽象类和接口之间的区别?”


1)语法层面上


接口中不能有 public 和 protected 修饰的方法,抽象类中可以有。

接口中的变量只能是隐式的常量,抽象类中可以有任意类型的变量。

一个类只能继承一个抽象类,但却可以实现多个接口。

2)设计层面上


抽象类是对类的一种抽象,继承抽象类的类和抽象类本身是一种 is-a 的关系。


接口是对类的某种行为的一种抽象,接口和类之间并没有很强的关联关系,所有的类都可以实现 Serializable 接口,从而具有序列化的功能。


就这么多吧,能说道这份上,我相信面试官就不会为难你了。


如果觉得文章对你有点帮助,请微信搜索「 沉默王二 」第一时间阅读,回复「并发」更有一份阿里大牛重写的 Java 并发编程实战,从此再也不用担心面试官在这方面的刁难了。


本文已收录 GitHub,传送门~ ,里面更有大厂面试完整考点,欢迎 Star。

我是沉默王二,一枚有颜值却靠才华苟且的程序员。关注即可提升学习效率,别忘了三连啊,点赞、收藏、留言,我不挑,嘻嘻。


相关文章
|
1月前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
200 1
|
2月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
134 11
|
3月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
256 23
|
3月前
|
存储 安全 Java
Java集合框架(一):List接口及其实现类剖析
本文深入解析Java中List集合的实现原理,涵盖ArrayList的动态数组机制、LinkedList的链表结构、Vector与Stack的线程安全性及其不推荐使用的原因,对比了不同实现的性能与适用场景,帮助开发者根据实际需求选择合适的List实现。
|
3月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
3月前
|
安全 Java 开发者
Java集合框架:详解Deque接口的栈操作方法全集
理解和掌握这些方法对于实现像浏览器后退功能这样的栈操作来说至关重要,它们能够帮助开发者编写既高效又稳定的应用程序。此外,在多线程环境中想保证线程安全,可以考虑使用ConcurrentLinkedDeque,它是Deque的线程安全版本,尽管它并未直接实现栈操作的方法,但是Deque的接口方法可以相对应地使用。
236 12
|
3月前
|
Java API 网络架构
java调用api接口自动判断节假日信息
java调用api接口自动判断节假日信息
1278 0
|
Java 开发者 微服务
Java精品文章合集,快来收藏吧
社区为广大开发者精心准备了一份 “Java 学习宝典” ,还不快来收藏?
5394 0
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
141 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
160 1
下一篇
oss云网关配置