一、概述
在JDK7以前:接口中只能定义抽象方法。
到了JDK8:接口中可以定义有方法体的方法。这个方法分为两种,一种是默认方法
,另一种是用 static
修饰的 静态方法
。
到了JKD9:接口中就可以定义用 private
修饰的私有方法。
学习这些知识点的时候,很多同学只知道语法,却不知道Java为什么这么去设计。
二、Java为什么这么去设计?
在开发当中,公司的一些大佬,它会提前把一些接口去定义好,定义完了后,就会通知下面的小弟们去用。
于是有个小弟就说:遵命,大哥
。于是它就写了一个 InterImpl1实现类
,实现了 Inter
接口,重写了 Inter接口
中所有的抽象方法。
小弟2号也不甘示弱,也实现了 Inter接口
,按照里面的格式去重写了所有的抽象方法。
过了几天,需求发生了改变,要新增一些其他的方法了,即在接口中新增一些其他的规则。
因此在 Inter接口
中,又添加了一个抽象方法 method2()
,写完后,大哥会说:“小弟们,我又添加了一个规则,你们用的时候记得加进去。”
此时小弟1号脾气不好,就开始发飙了。小弟2号说:“我也一样”。
因此,如果一直按照 JDK7 以前的规则来操作,那么就一定会遇到这么尴尬的事情:只要接口里面发生了变化,那么所有的这些实现类,就不得不跟着一起去改变。如果说不跟着一起改变,那么这些实现类立马会报错。
此时接口就自己琢磨:这些家伙真难伺候,但是我又想加新的规则,又想让他们的代码不报错怎么办呢?
此时它就想出了:我是不是在接口中写那种有方法体的方法就可以了。
因此,JDK8和JDK9新增的特性就是这么出现的。
三、接口升级
接口中有方法体的方法,它是在接口升级时,为了兼容性而使用的。
为了理解 接口升级
,我们来看一个场景。
有一个程序员开发一个项目,在项目版本1.0
的版本中,定义了一个 Inter接口
,下面有两个实现类去实现 Inter接口
。
此时 项目Vsersion1.0 成功上线,没有任何问题
。
但是呢,过了一段时间,项目2.0版本
开始发布了,功能比 1.0
要多,所以说需要在 Inter接口
中加入 10个新的方法
,这个就叫做接口升级。
当接口在升级的时候,此时就可以使用有方法体的方法。
此时实现类就不需要立马修改了,代码是不会报错的。等以后用到某个规则了,再去重写就可以了。
这种 有方法体的方法
分为两种:① 默认的(用 default
修饰);② 静态的(用 static
修饰)
接下来我们来挨个学习。
四、JDK8开始
接口中新增的默认方法
1)介绍
在 JDK8版本后
,允许在接口中定义默认方法,需要使用关键字 default
修饰。
作用:解决接口升级的问题。
接口中默认方法的定义格式:在 public
后面加上一个 default
就可以了。
// 格式
public default 返回值类型 方法名(参数列表){}
// 示例
public default void show(){}
接口中默认方法的注意事项
- 默认方法不是抽象方法,所以不强制被重写。如果被重写,重写的时候需要去掉
default关键字
。 public
是默认修饰符,可以省略。但是default
不能省略,一定要加上。- 如果实现了多个接口,多个接口中有相同名字的默认方法,此时子类不管用不用,必须要对
相同名字的默认方法
来进行重写。
2)代码实现
默认方法不是抽象方法,所以不强制被重写。
Inter.java
package com.itheima.a06interfacedemo6;
public interface InterA {
// 为了演示示例效果,这里额外写一个抽象方法
public abstract void method();
public default void show(){
System.out.println("Inter接口中的默认方法 ---- show");
}
}
InterImpl.java
利用IDEA的快捷方式:用鼠标点击红色波浪线,alt + 回车 选择第一个,可以发现IDEA帮我默认选择的是抽象方法。
最后选择 OK
即可。
package com.itheima.a06interfacedemo6;
public class InterImpl implements InterA,InterB{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
}
写完后,可以发现代码并没有报错,接下来在测试类中,就可以来创建实现类的对象。
Test.java
//创建实现类的对象
InterImpl ii = new InterImpl();
li.method(); // 调用重写后的method方法
ii.show(); // 调用默认方法
打印结果
实现类重写的抽象方法
Inter接口中的默认方法 ---- show
但如果想重写 Inter
中的默认方法怎么办?
我们不需要自己一个个去写,直接在实现类中写方法名,然后回车就好了。
![image-20240418154406573](https://img-blog.csdnimg.cn/img_convert/ba97b60fc84c838ac866821de3500c15.png)
@Override
public void show() {
System.out.println("重写接口中的默认方法");
}
如果被重写,重写的时候需要去掉 default关键字
。
在重写的默认方法中,就不能再去前面加 default
了。如果强行书写就会报错:扩展方法只能在接口内使用。
![image-20240418154551901](https://img-blog.csdnimg.cn/img_convert/2330fd68b32015e0cf032d21e1290c0c.png)
public
是默认修饰符,可以省略
在IDEA中可以直接看见,像这种灰色的,都是可以直接省略的。
![image-20240418154825120](https://img-blog.csdnimg.cn/img_convert/fca1e3c5257bd6d0fe9ee4e0d3cc4a72.png)
但是 default
不能省略,一定要加上。
因为如果 default
你省略了,Java会把这个方法当做是抽象方法。因为接口中的方法会默认加上 public abstract
修饰符。
当你删掉 default
关键字后,就会报错:接口中的抽象方法不能拥有方法体。
如果实现了多个接口,多个接口中有相同名字的默认方法,此时子类不管用不用,必须要对 相同名字的默认方法
来进行重写
InterA.java
package com.itheima.a06interfacedemo6;
public interface InterA {
public abstract void method();
public default void show(){
System.out.println("InterA接口中的默认方法 ---- show");
}
}
InterB.java
InterB.java接口
中也有默认方法 show()
package com.itheima.a06interfacedemo6;
public interface InterB {
public default void show(){
System.out.println("InterB接口中的默认方法 ---- show");
}
}
然后让实现类分别实现 InterA接口
和 InterB接口
,并实现了InterA接口
中的抽象方法。
InterImpl.java
package com.itheima.a06interfacedemo6;
public class InterImpl implements InterA,InterB{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
}
但是,代码报错。
这是因为,如果你没有重写,在测试类中创建实现类的对象,去调用 show()
方法,那Java怎么知道它是调用 InterA
中的 show()
还是 InterB
中的 show()
呢?
Test.java
//创建实现类的对象
InterImpl ii = new InterImpl();
ii.show(); // 调用默认方法
因此在这种情况下,实现类强制重写。那么当我再在测试类中调用 show()
方法的时候,一定是重写之后的 show()
方法。
InterImpl.java
package com.itheima.a06interfacedemo6;
public class InterImpl implements InterA,InterB{
@Override
public void method() {
System.out.println("实现类重写的抽象方法");
}
@Override
public void show() {
System.out.println("重写接口中的默认方法");
}
}
五、JDK8开始
接口中新增的 静态方法
1)介绍
静态方法跟之前一样,直接使用 static
来修饰就行了。
接口中静态方法的定义格式:在 public
后面加一个 static关键字
就行了
// 格式
public static 返回值类型 方法名(参数列表){ }
// 范例
public static void show(){ }
接口中静态方法的注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public
可以省略,static
不能省略
static不能省略
跟 default不能省略
是一样的,如果省略了,Java就会将这个方法当成抽象方法。
2)代码实现
Inter.java
package com.itheima.a07interfacedemo7;
public interface Inter {
public abstract void method();
public static void show(){
System.out.println("Inter接口中的静态方法");
}
}
InterImpl.java
当你 alt + 回车 ,可以发现它只需要你去重写抽象方法就行了,静态方法不需要你重写。
package com.itheima.a07interfacedemo7;
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("InterImpl重写的抽象方法");
}
}
但是当你强行重写静态方法,就会报错:静态方法不能用@Override进行注释
因为静态方法是不能被重写的。
如果你将 @Override
删掉,然后强行加一个 static
,此时这个东西不叫重写。它只是刚好在实现类中跟接口有一个重名的方法而已
package com.itheima.a07interfacedemo7;
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("InterImpl重写的抽象方法");
}
//不叫重写
public static void show() {
System.out.println("InterImpl重写的抽象方法");
}
}
在调用的时候,如果你使用的是 InterImpl.show()
,那么调用的就是实现类中的静态方法。
如果你使用的是 Inter.show()
,那么调用的就是接口中的静态方法。
Test.java
package com.itheima.a07interfacedemo7;
public class Test {
public static void main(String[] args) {
//调用接口中的静态方法
Inter.show();
//调用实现类中的静态方法
InterImpl.show();
// 此时这个东西不叫重写。它只是刚好在实现类中跟接口有一个重名的方法而已。
// 在之前我们曾经说过:什么叫做重写?子类把从父类继承下来的虚方法表里面的方法进行覆盖了,这才叫重写。
// 一定要有一个这样覆盖的过程,它才叫做重写。但是private、static、final修饰的,是不会添加到虚方法表中的。
// 因此这里的写法不叫做重写,只是刚好在实现类中跟接口有一个重名的方法而已。
}
}
六、JDK9
新增的私有方法
1)推导
Java添加私有方法肯定是有它的原因的。
来看下面的这段代码,这段代码是我在接口中定义的两个默认方法:start
、end
。
这两段代码你发现,它会有个问题:有两句代码重复了。
![image-20240418162801150](https://img-blog.csdnimg.cn/img_convert/bec63d5cb4fe7d71d9c893b1c600e13e.png)
既然重复了,就需要将重复的代码抽取出来。
在JDK9以前,我们只能再写一个 default
方法,将重复的代码抽取到 log()
方法中。
此时在左边的代码中直接调用对应的方法就可以了。
这么操作虽然可以,但是会有个小问题:抽取出来的方法,它只是抽取了重复代码而已,这些代码它是为本接口其他方法
去服务的,不想要让外类去访问,因为外类访问它是没有意义的,访问完里面的代码,它仅仅只是一些重复的代码而已。
当一个方法不想让别人去使用时,最好的办法就是把前面的权限修饰符变成 private
。
因此到到JDK9的时候,就提出了私有的方法,像这种抽取出来的重复代码,我们就可以定义在私有方法中。
私有方法它是分为两类的:
一个是大家看到的下图中,普通的私有方法。
还有一个是加静态关键字去修饰的 静态的私有方法
。
2)接口中私有方法的定义格式
1、普通的私有方法
的定义格式
PS:在 private
和 返回值类型
之间,是不加 default
关键字的。
这个方法它是给默认方法服务的。
// 格式
private 返回值类型 方法名(参数列表){ }
// 范例
private void show(){ }
2、静态的私有方法
的定义格式
这个方法它是给静态方法去服务的
// 格式
private static 返回值类型 方法名(参数列表){ }
// 范例
private static void method(){ }
3)代码实现
InterA.java
package com.itheima.a08interfacedemo8;
public interface InterA {
public default void show1(){
System.out.println("show1方法开始执行了");
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
public default void show2(){
System.out.println("show2方法开始执行了");
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
在JDK9以前,它只能重新写一个 默认方法 show3()
,然后将重复的代码写到 show3()
中。
然后 show1()
和 show2()
直接调用 show3()
方法即可。
package com.itheima.a08interfacedemo8;
public interface InterA {
public default void show1(){
System.out.println("show1方法开始执行了");
show3();
}
public default void show2(){
System.out.println("show2方法开始执行了");
show3();
}
public default void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
但是有个小弊端,show3()
方法此时还是可以被外界调用的,但是这个方法被外界调用其实是没有任何意义的,因为它只是记录了一些重复的代码。
因此Java就觉得这种设计方案不好,在JDK9以后就提出来对应的解决方案:将权限修饰符写成 private
即可,然后将前面的 default
删掉即可,如果不删掉就会报错。
这个就是普通的私有方法,它是给默认方法去服务的。
package com.itheima.a08interfacedemo8;
public interface InterA {
public default void show1(){
System.out.println("show1方法开始执行了");
show3();
}
public default void show2(){
System.out.println("show2方法开始执行了");
show3();
}
private void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
但如果 show1()
和 show2()
并不是默认方法,而是静态方法,那么下面就不能再去写这种普通的私有方法了。
而是书写静态的私有方法。静态的私有方法,是给静态方法服务的
package com.itheima.a08interfacedemo8;
public interface InterA {
public static void show1(){
System.out.println("show1方法开始执行了");
show4();
}
public static void show2(){
System.out.println("show2方法开始执行了");
show4();
}
//普通的私有方法,是给默认方法服务的
private void show3(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
//静态的私有方法,是给静态方法服务的
private static void show4(){
System.out.println("记录程序在运行过程中的各种细节,这里有100行代码");
}
}
七、总结
1、JDK7以前:接口中只能定义抽象方法。
2、JDK8:它要考虑接口升级,因此在接口中可以定义有方法体的方法(这个方法分为两种:① 默认的(用 default
修饰);② 静态的(用 static
修饰))
3、JDK9:接口中可以定义私有方法。
私有方法的目的其实就是解决上面这两种(① 默认的(用
default修饰);② 静态的(用
static 修饰))
有重复的代码,需要抽取出来,但是抽取出来的代码又不想让外界提供,就可以使用 私有方法
解决。
4、私有方法分为两种:① 普通的私有方法;② 静态的私有方法。
普通的私有方法
它是给 默认方法
服务的。
静态的私有方法
它是给 静态方法
服务的。