Java 8 有多牛逼?打破一切你对接口的认知!
前段时间面试了一个 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 写了一段逻辑,同事直呼看不懂,你试试看。。
觉得不错,别忘了随手点赞+转发哦!
Java 8 有多牛逼?打破一切你对接口的认知!的更多相关文章
- 推荐一个 Java 里面比较牛逼的公众号!
今天给大家推荐一个牛逼的纯 Java 技术公众号:Java技术栈,作者:栈长. Java程序员.Java爱好者扫码关注吧! 确实牛逼,几十万人关注了,原创文章350+,好友都 3000+ 关注了. 栈 ...
- 为什么我会认为SAP是世界上最好用最牛逼的ERP系统,没有之一?
为什么我认为SAP是世界上最好用最牛逼的ERP系统,没有之一?玩过QAD.Tiptop.用友等产品,深深觉得SAP是贵的有道理! 一套好的ERP系统,不仅能够最大程度承接适配企业的管理和业务流程,在技 ...
- 我喜欢ASP.NET的MVC因为它牛逼的9大理由(转载)
我很早就关注ASP.NET的mvc的,因为最开始是学了Java的MVC,由于工作的原因一直在做.Net开发,最近的几个新项目我采用了MVC做了,我个一直都非常喜欢.Net的MVC.我们为什么使用MVC ...
- 最牛逼android上的图表库MpChart(三) 条形图
最牛逼android上的图表库MpChart三 条形图 BarChart条形图介绍 BarChart条形图实例 BarChart效果 最牛逼android上的图表库MpChart(三) 条形图 最近工 ...
- 最牛逼android上的图表库MpChart(二) 折线图
最牛逼android上的图表库MpChart二 折线图 MpChart折线图介绍 MpChart折线图实例 MpChart效果 最牛逼android上的图表库MpChart(二) 折线图 最近工作中, ...
- 从苦逼到牛逼,详解Linux运维工程师的打怪升级之路
做运维也快四年多了,就像游戏打怪升级,升级后知识体系和运维体系也相对变化挺大,学习了很多新的知识点. 运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感 ...
- 最牛逼的任务调度工具 | Quartz
Quartz 是一个完全由 Java 编写的开源作业调度框架,不要让作业调度这个术语吓着你,其实不难.尽管 Quartz 框架整合了许多额外功能,但就我们使用来说,你会发现它易用得简直让人受不了! 简 ...
- 【项目总结】:怎样做一个牛逼的Team leader?
随着ITOO高校云平台3.1项目的结束,我们各种各样的总结也被提上了日程. Java版本号的全部开发者和Donet版本号的全部开发者坐在一起进行了关于项目开发管理的头脑风暴,尽管我仅仅是Donet开发 ...
- Eclipse 最常用的 10 组快捷键,个个牛逼!
虽然栈长我现在不怎么用 Eclipse 了,但 Eclipse 的快捷键还是忘不了的,可以说 Eclipse 的快捷键很方便,恰到好处. 今天,我大概整理了 10 组 Eclipse 我觉得比较常用的 ...
随机推荐
- 15_Python的模块module
1.模块的概述 1.模块是Python程序架构的一个核心概念,每一个以.py结尾的Python源代码文件都是一个模块 2.模块名和标识符的命名规则一样,由数字字母下划线组成且不能以数字开头,也不要和系 ...
- 剑指 Offer 42. 连续子数组的最大和
题目描述 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组.求所有子数组的和的最大值. 要求时间复杂度为\(O(n)\). 示例1: 输入: nums = [-2,1,-3,4,-1,2,1 ...
- H5简单内容
1.简单认识H5 HTML5不仅仅是作为HTML标记语言的一个最新版本,更重要的是它指定了Web开发的一系列标准,成为第一个将Web作为应用开发平台的HTML语言. 我们日常讨论的H5其实是有一个泛称 ...
- Html中让输入框input和紧接在后的按钮button在垂直方向上对齐
<table border="0px" width="360px"> <tr><td colspan="10" ...
- JavaScript函数及面向对象
函数及面向对象 目录 函数及面向对象 1. 定义函数 1. 定义方式一 2. 定义方式二 2. 调用函数 1. 参数问题 2. arguments 3. rest 3. 变量的作用域 1 . var的 ...
- VMware参数disk.EnableUUID生效扫描不出来UUID
问题描述:搭建RAC中的共享磁盘,在一个节点上部署磁盘,另一个节点加入共享磁盘,使用扫描UUID的方法,但是莫名其妙一直扫描不出来UUID,使用了各种各样的方法,一个星期时间接近崩溃,又搞了一下午的测 ...
- docker启动容器报错 Unknown runtime specified nvidia.
启动docker容器时,报错 问题复现 当我启动一个容器时,运行以下命令: docker run --runtime=nvidia .... 后面一部分命令没写出来,此时报错的信息如下: docker ...
- C的文件操作---笔记
打开文件 FILE *fp = fopen(char *filename, char *mode) 关闭文件 fclose(fp) 字符形式读 char ch = fgetc(fp) 字符形式写 ...
- oracle之三RMAN概述
RMAN概述 6.1 rman的定义和功能: 1) Recovery Manager 2)建立备份和恢复的server process,在oracle server上做备份和恢复 3)rman 备份d ...
- JDK13环境变量配置
第一步:下载JDK(开发工具包) JDK分为OracleJDK和OpenJDK下面简要说明 OracleJDK 部分代码闭源.商业收费 OpenJDK 开放源码.商业免费 两者大部分代码是共用的(除闭 ...