前段时间面试了一个 39 岁的程序员,结果不是很理想,没看过的点击这里阅读。

最近也面试一些 Java 程序员,不乏工作 4、5 年经验的,当我问他一些 Java 8 的新特性时,大多却答不上来。

比如下面这道题:

栈长:接口里面可以写方法吗?

小A:当然可以啊,默认就是抽象方法。

栈长:那接口里面可以写实现方法吗?

小A:不可以,所有方法必须是抽象的。

栈长:你确定吗?

小A:确定……

小A看起来对我的问题有点怀疑人生,心里肯定估摸着,我不会在给他埋了什么坑吧。然后他还是仔细再想了一下,最后还是斩钉截铁的告诉我:接口里面只能写抽象方法,不能写实现方法

栈长:接口里面是可以写实现方法的,Java 8 开始就可以了,你用过 Java 8 吗?

小A:好吧,看来是我学艺不精,Java 8 有了解一点,比如那个 Lambda 表达式,但实际项目中也没怎么用。

通过和小A的交流,我也看到了许多开发者的问题,虽然开发版本用的是 Java 8,但实际用的还是 Java 8 之前的最基础的语法,对 Java 8 新增的特性一无所知。

Java 8 至 2014 年发布至今,已经过了 6 个年头了,最新的 Java 14 都发布了,OK,这个不在本篇讨论范围之内, Java 8+ 系列教程请关注公众号回复 "java" 进行阅读,本篇就是想顺着问小A的这个问题展开。

什么是默认方法和静态方法?

上面也说了,Java 8 开始是可以有方法实现的,可以在接口中添加默认方法和静态方法。

默认方法用 default 修饰,只能用在接口中,静态方法用 static 修饰,这个我们不陌生了。并且接口中的默认方法、静态方法可以同时有多个。

在接口中写实现方法一点也不稀奇,像这样的用法,从 Java 8 到 Java 14 已是遍地开花,到处都可以看到接口默认方法和静态方法的身影。

比如我们来看下在 JDK API 中 java.util.Map 关于接口默认方法和静态方法的应用。

/*
* 来源公众号:Java技术栈
*/
public interface Map<K,V> { ... /**
* 接口默认方法
*/
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
} ... /**
* 接口静态方法
*/
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
} ... }

为什么要有接口默认方法?

举一个很现实的例子:

我们的接口老早就写好了,后面因为各种业务问题,避免不了要修改接口。

在 Java 8 之前,比如要在一个接口中添加一个抽象方法,那所有的接口实现类都要去实现这个方法,不然就会编译错误,而某些实现类根本就不需要实现这个方法也被迫要写一个空实现,改动会非常大。

所以,接口默认方法就是为了解决这个问题,只要在一个接口添加了一个默认方法,所有的实现类就自动继承,不需要改动任何实现类,也不会影响业务,爽歪歪。

另外,接口默认方法可以被接口实现类重写。

为什么要有接口静态方法?

接口静态方法和默认方法类似,只是接口静态方法不可以被接口实现类重写。

接口静态方法只可以直接通过静态方法所在的 接口名.静态方法名 来调用。

接口默认方法多继承冲突问题

因为接口默认方法可以被继承并重写,如果继承的多个接口都存在相同的默认方法,那就存在冲突问题。

下面我会列举 3 个冲突示例场景。

冲突一

来看下面这段程序:

/*
* 来源公众号:Java技术栈
*/
interface People {
default void eat(){
System.out.println("人吃饭");
}
} /*
* 来源公众号:Java技术栈
*/
interface Man {
default void eat(){
System.out.println("男人吃饭");
}
} /*
* 来源公众号:Java技术栈
*/
interface Boy extends Man, People { }

Boy 同时继承了 People 和 Man,此时在 IDEA 编辑器中就会报错:

这就是接口多继承带来的冲突问题,Boy 不知道该继承谁的,这显然也是个问题,IDEA 也会提示,需要重写这个方法才能解决问题:

/*
* 来源公众号:Java技术栈
*/
interface Boy extends Man, People { @Override
default void eat() {
System.out.println("男孩吃饭");
}
}

在方法里面还能直接调用指定父接口的默认方法,比如:

/*
* 来源公众号:Java技术栈
*/
interface Boy extends Man, People { @Override
default void eat() {
People.super.eat();
Man.super.eat();
System.out.println("男孩吃饭");
}
}

再加个实现类测试一下:

/*
* 来源公众号:Java技术栈
*/
static class Student implements Boy { public static void main(String[] args) {
Student student = new Student();
student.eat();
} }

输出:

人吃饭
男人吃饭
男孩吃饭

嗯,很强大!

冲突二

我们再换一种写法,把 Man 继承 People,然后 Man 重写 People 中的默认方法。

此时,编辑器不报错了,而 People 的默认方法置灰了,提示没有被用到。

再运行一下上面的示例,输出:

男人吃饭

因为 Man 继承 People,Man 又重定了默认方法。很显然,这个时候,Boy 知道该继承谁的默认方法了。

冲突三

在 Man 接口中新增一个方法:say,然后在 Boy 接口中新增一个默认方法:say。

这时候,Man 中的抽象方法居然被忽略了,IDEA 都提示说没用到,这显然是默认方法优先于抽象方法。

总结

本文介绍了 Java 8 的默认方法和静态方法,以及默认方法的冲突问题解决方案。所以,大家出去面试时,再也不要说接口不能写实现方法了,那就太 OUT 了。。

文中只举了 3 个默认方法的冲突场景,不确定还没有更多冲突问题。默认方法虽然解决了接口变动带来的问题,但如果设计不当,或者过度设计,其带来的方法冲突问题也是需要引起注意的。

本文到此就结束了,之前我也陆续分享了一系列 Java 8+ 新特性文章,感兴趣的可以关注公众号Java技术栈在菜单中获取,后续还会继续分享,公众号第一时间推送,持续关注哦。

老铁们,觉得有用,在看、转发分享一下哦~

近期热文推荐:

1.Java 15 正式发布, 14 个新特性,刷新你的认知!!

2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

3.我用 Java 8 写了一段逻辑,同事直呼看不懂,你试试看。。

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

Java 8 有多牛逼?打破一切你对接口的认知!的更多相关文章

  1. 推荐一个 Java 里面比较牛逼的公众号!

    今天给大家推荐一个牛逼的纯 Java 技术公众号:Java技术栈,作者:栈长. Java程序员.Java爱好者扫码关注吧! 确实牛逼,几十万人关注了,原创文章350+,好友都 3000+ 关注了. 栈 ...

  2. 为什么我会认为SAP是世界上最好用最牛逼的ERP系统,没有之一?

    为什么我认为SAP是世界上最好用最牛逼的ERP系统,没有之一?玩过QAD.Tiptop.用友等产品,深深觉得SAP是贵的有道理! 一套好的ERP系统,不仅能够最大程度承接适配企业的管理和业务流程,在技 ...

  3. 我喜欢ASP.NET的MVC因为它牛逼的9大理由(转载)

    我很早就关注ASP.NET的mvc的,因为最开始是学了Java的MVC,由于工作的原因一直在做.Net开发,最近的几个新项目我采用了MVC做了,我个一直都非常喜欢.Net的MVC.我们为什么使用MVC ...

  4. 最牛逼android上的图表库MpChart(三) 条形图

    最牛逼android上的图表库MpChart三 条形图 BarChart条形图介绍 BarChart条形图实例 BarChart效果 最牛逼android上的图表库MpChart(三) 条形图 最近工 ...

  5. 最牛逼android上的图表库MpChart(二) 折线图

    最牛逼android上的图表库MpChart二 折线图 MpChart折线图介绍 MpChart折线图实例 MpChart效果 最牛逼android上的图表库MpChart(二) 折线图 最近工作中, ...

  6. 从苦逼到牛逼,详解Linux运维工程师的打怪升级之路

    做运维也快四年多了,就像游戏打怪升级,升级后知识体系和运维体系也相对变化挺大,学习了很多新的知识点. 运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感 ...

  7. 最牛逼的任务调度工具 | Quartz

    Quartz 是一个完全由 Java 编写的开源作业调度框架,不要让作业调度这个术语吓着你,其实不难.尽管 Quartz 框架整合了许多额外功能,但就我们使用来说,你会发现它易用得简直让人受不了! 简 ...

  8. 【项目总结】:怎样做一个牛逼的Team leader?

    随着ITOO高校云平台3.1项目的结束,我们各种各样的总结也被提上了日程. Java版本号的全部开发者和Donet版本号的全部开发者坐在一起进行了关于项目开发管理的头脑风暴,尽管我仅仅是Donet开发 ...

  9. Eclipse 最常用的 10 组快捷键,个个牛逼!

    虽然栈长我现在不怎么用 Eclipse 了,但 Eclipse 的快捷键还是忘不了的,可以说 Eclipse 的快捷键很方便,恰到好处. 今天,我大概整理了 10 组 Eclipse 我觉得比较常用的 ...

随机推荐

  1. 15_Python的模块module

    1.模块的概述 1.模块是Python程序架构的一个核心概念,每一个以.py结尾的Python源代码文件都是一个模块 2.模块名和标识符的命名规则一样,由数字字母下划线组成且不能以数字开头,也不要和系 ...

  2. 剑指 Offer 42. 连续子数组的最大和

    题目描述 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组.求所有子数组的和的最大值. 要求时间复杂度为\(O(n)\). 示例1: 输入: nums = [-2,1,-3,4,-1,2,1 ...

  3. H5简单内容

    1.简单认识H5 HTML5不仅仅是作为HTML标记语言的一个最新版本,更重要的是它指定了Web开发的一系列标准,成为第一个将Web作为应用开发平台的HTML语言. 我们日常讨论的H5其实是有一个泛称 ...

  4. Html中让输入框input和紧接在后的按钮button在垂直方向上对齐

    <table border="0px" width="360px"> <tr><td colspan="10" ...

  5. JavaScript函数及面向对象

    函数及面向对象 目录 函数及面向对象 1. 定义函数 1. 定义方式一 2. 定义方式二 2. 调用函数 1. 参数问题 2. arguments 3. rest 3. 变量的作用域 1 . var的 ...

  6. VMware参数disk.EnableUUID生效扫描不出来UUID

    问题描述:搭建RAC中的共享磁盘,在一个节点上部署磁盘,另一个节点加入共享磁盘,使用扫描UUID的方法,但是莫名其妙一直扫描不出来UUID,使用了各种各样的方法,一个星期时间接近崩溃,又搞了一下午的测 ...

  7. docker启动容器报错 Unknown runtime specified nvidia.

    启动docker容器时,报错 问题复现 当我启动一个容器时,运行以下命令: docker run --runtime=nvidia .... 后面一部分命令没写出来,此时报错的信息如下: docker ...

  8. C的文件操作---笔记

    打开文件  FILE *fp = fopen(char *filename, char *mode) 关闭文件  fclose(fp) 字符形式读  char ch = fgetc(fp) 字符形式写 ...

  9. oracle之三RMAN概述

    RMAN概述 6.1 rman的定义和功能: 1) Recovery Manager 2)建立备份和恢复的server process,在oracle server上做备份和恢复 3)rman 备份d ...

  10. JDK13环境变量配置

    第一步:下载JDK(开发工具包) JDK分为OracleJDK和OpenJDK下面简要说明 OracleJDK 部分代码闭源.商业收费 OpenJDK 开放源码.商业免费 两者大部分代码是共用的(除闭 ...