接口概述与定义
接口是java语言中的一种引用数据类型,接口中可以定义:常量丶抽象方法 (jdk7及其以前)丶默认方法和静态方法(jdk8额外增加)丶私有方法(jdk9额外增加) 接口也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。接口的使用,它不能创建对象,但是可以被实现。一个实现接口的类,需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。**继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a关系。**例如:你能不能用USB进行连接,或是否具备USB通信功能,就看你是否遵循USB接口规范
定义格式
接口的定义,它与定义类方式相似,但是使用 interface`关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。接口的声明格式,如下所示:
示例代码:
public interface Demo {
// 常量(jdk7及其以前) 使用public static final关键字修饰,这三个关键字都可以省略
public static final int NUM1 = 10;
int NUM2 = 20;
// 抽象方法(jdk7及其以前) 使用public abstract关键字修饰,这2个关键字都可以省略
public abstract void method1();
void method2();
// 默认方法(jdk8) 使用public default关键字修饰,public可以省略,default不可以省略
public default void method3() {
System.out.println("默认方法 method3");
}
// 静态方法(jdk8) 使用public static关键字修饰,public可以省略,static不可以省略
public static void method4() {
System.out.println("静态方法 method4");
}
// 私有方法(jdk9) 使用private关键字修饰,private不可以省略
private static void method5() {
System.out.println("私有静态方法 method5");
}
private void method6() {
System.out.println("私有非静态方法 method6");
}
}
接口的成员说明
接口定义的是多个类共同的公共行为规范,这些行为规范是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。接口是从多个相似类中抽象出来的规范,不需要提供具体实现
在JDK8之前,接口中只允许出现:
- 公共的静态的常量:其中public static final可以省略
- 公共的抽象的方法:其中public abstract可以省略
在JDK1.8时,接口中允许声明默认方法和静态方法:
- 公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略
- 公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略
在JDK1.9时,接口又增加了:
- 私有方法
除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要初始化。
为什么接口中只能声明公共的静态的常量?
- 因为接口是标准规范,那么在规范中需要声明一些底线边界值,当实现者在实现这些规范时,不能去随意修改和触碰这些底线,否则就有“危险”。
为什么JDK1.8之后要允许接口定义静态方法和默认方法呢?
- 静态方法:因为之前的标准类库设计中,有很多Collection/Colletions或者Path/Paths这样成对的接口和类,后面的类中都是静态方法,而这些静态方法都是为前面的接口服务的,那么这样设计一对API,不如把静态方法直接定义到接口中使用和维护更方便。
- 默认方法:我们要在已有的老版接口中提供新方法时,如果添加抽象方法,就会涉及到原来使用这些接口的类就会有问题,那么为了保持与旧版本代码的兼容性,只能允许在接口中定义默认方法实现。比如:Java8中对Collection、List、Comparator等接口提供了丰富的默认方法。当我们接口的某个抽象方法,在很多实现类中的实现代码是一样的,此时将这个抽象方法设计为默认方法更为合适,那么实现类就可以选择重写,也可以选择不重写。
为什么JDK1.9要允许接口定义私有方法呢?
- 私有方法:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。
实现接口
接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。
实现单个接口
实现多个接口:
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。
实现类继承父类的同时,又实现单个接口
实现类继承父类的同时,又实现多个接口
注意:
- 如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法。
- 默认方法可以选择保留,也可以重写。重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了
- 不能重写静态方法
接口中成员访问特点
下面是讨论的是实现类实现单个接口的情况,这也是我们开发中发生的最多情况
访问:接口中的常量
interface A {
//常量
int AGE = 18;
}
//实现类
class ImpAB implements A {
//实现类可以继承常量
}
public class Test {
public static void main(String[] args) {
//通过接口名称直接访问:
System.out.println(A.AGE); // 18
//通过实现类名称访问,不推荐
System.out.println(ImpAB.AGE);//18
//通过实现类对象名称访问,不推荐
System.out.println(new ImpAB().AGE);//18
}
}
结论:在常量不冲突的情况下,实现类可以继承接口中的常量。我们推荐使用接口名直接访问接口中的常量。
访问接口中的:抽象方法
如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法。接口中的抽象方法: 供实现类重写的
interface A {
//接口中的抽象方法
void eat();
}
//实现类
class ImpAB implements A {
//实现接口中的抽象方法
@Override
public void eat() {
System.out.println("我喜欢吃鱼");
}
}
public class Test {
public static void main(String[] args) {
//创建实现类对象
ImpAB i = new ImpAB();
//调用重写后的抽象方法
i.eat(); //我喜欢吃鱼
}
}
结论:接口中的抽象方法会被实现类继承。接口中的抽象方法必须通实现类对象才能访问。且访问的是实现类重写后的抽象方法
访问接口中的:默认方法
默认方法可以选择保留,也可以重写。 重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了
interface A {
//接口中的默认方法
default void method1() {
System.out.println("我是接口中的默认方法1");
}
default void method2() {
System.out.println("我是接口中的默认方法2");
}
}
//实现类
class ImpAB implements A {
//实现接口中的默认方法1
@Override
public void method1() {
this.method2();// 我是接口中的默认方法2
System.out.println("我是实现类中的默认方法1");
}
}
public class Test {
public static void main(String[] args) {
//创建实现类对象
ImpAB i = new ImpAB();
//调用重写后的默认方法
i.method1();//我是实现类中的默认方法1
//调用继承后的默认方法
i.method2(); //我是接口中的默认方法2
}
}
**结论:**接口中的默认方法会被实现类继承。默认方法就是供实现类重写或者实现类对象直接调用。我们可以在实现类中直接调用接口中的默认方法,也可以通过实现类对象调用继承下来的或者重写后的默认方法
访问接口中的:静态方法
对于接口的静态方法,直接使用“接口名.”进行调用即可。
package demo02;
interface A {
//接口中的静态方法
static void show(){
System.out.println("我是接口中的静态方法");
}
}
//实现类
class ImpAB implements A {
}
public class Test {
public static void main(String[] args) {
//创建实现类对象
ImpAB i = new ImpAB();
// i.show(); 编译错误,找不到符号
//通过接口名称来调用
A.show();
}
}
**结论:**静态方法不能被实现类继承,只能使用“接口名."进行调用,不能通过实现类的对象进行调用。
访问接口中的:私有方法
**结论:**接口中的私有方法,只能在接口中直接调用,实现类继承不了。因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。
interface A {
// 私有方法
private void method1(){
method2();//接口中调用私有方法
System.out.println("IA 接口中的私有方法method4");
}
private static void method2(){
System.out.println("IA 接口中的私有方法method5");
}
}
使用接口的几种冲突情况
多实现时的几种冲突情况
当一个类同时实现了多个接口时候,如果接口中的常量丶抽象方法丶默认方法丶静态方法丶私有方法相同就会发生冲突的情况,那我们应该怎么解决了?
公有静态常量的冲突
interface A {
int NUM1 = 1;
}
interface B {
int NUM1 = 2;
}
class Imp implements A, B {
}
public class Test {
public static void main(String[] args) {
Imp imp = new Imp();
/*
公有静态常量的冲突: 如果多个接口中有相同的常量,那么实现类就无法继承
*/
// System.out.println(imp.NUM1); 错误:对NUM1的引用不明确
System.out.println(A.NUM1); //1
System.out.println(B.NUM1);//2
}
}
**解决方式:**如果多个接口中有相同的常量,那么实现类就无法继承,避免这种情况的方式
公有抽象方法的冲突
interface A{
public abstract void method();
}
interface B{
public abstract void method();
}
class Imp implements A,B{
@Override
public void method() {
System.out.println("实现类重写");
}
}
public class Test {
public static void main(String[] args) {
/*
公有抽象方法的冲突:实现类只需要重写一个
*/
}
}
**解决方式:**如果抽象方法有重名的,只需要重写一次。
公有默认方法的冲突
interface A{
public default void method(){
System.out.println("A 接口的默认方法method");
}
}
interface B{
public default void method(){
System.out.println("B 接口的默认方法method");
}
}
class Imp implements A,B{
@Override
public void method() {
System.out.println("实现类重写的默认方法");
}
}
public class Test {
public static void main(String[] args) {
/*
公有默认方法的冲突:实现类必须重写一次最终版本
*/
Imp imp = new Imp();
imp.method();
}
}
**解决方式:**实现类必须重写一次最终版本 。我们可以完全重写静态方法,也可以在重写的默认方法中通过“接口名.super.方法名"的方法选择调用哪个接口的默认方法。
公有静态方法的冲突
- 静态方法是直接属于接口的,不能被继承,所以不存在冲突
私有方法的冲突:
- 私有方法只能在本接口中直接使用,不存在冲突
实现类继承父类又实现接口时发生了冲突
- **父类和接口的公有静态常量的冲突:**子类无法继承有冲突的常量
- **父类和接口的抽象方法冲突:**子类必须重写一次有冲突的抽象方法
- **父类和接口的公有默认方法的冲突:**优先访问父类的
- **父类和接口的公有静态方法:**只会访问父类的静态方法
- 父类和接口的私有方法:私有方法只能本类访问,不存在冲突
接口和接口的关系
接口与接口之间是继承关系,又可以细分为单继承丶多继承丶多层继承。一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。代码示例
interface IA{}
interface IB{}
interface IC extends IA,IB{//是“继承”,而且可以“多继承”
}
接口多继承接口的冲突情况
当一个接口同时继承了多个接口时候,如果接口中的常量丶抽象方法丶默认方法丶静态方法丶私有方法相同就会发生冲突的情况,那我们应该怎么解决了?
- **公有静态常量的冲突:**子接口无法继承父接口中冲突的常量
- **公有抽象方法冲突 :**子接口只会继承一个有冲突的抽象方法
- **公有默认方法的冲突:**子接口中必须重写一次有冲突的默认方法。实现类重写接口中的默认方法,不需要加default,子接口重写父接口中的默认方法,必须加default
- **公有静态方法和私有方法:**不冲突,因为静态方法是直接属于接口的,只能使用本接口直接访问,而私有方法只能在接口中访问,也没有冲突、
抽象类和接口应用
- 如果一个父类中的某个方法,所有子类都有不同的实现,那么该方法就应该定义成抽象方法,所以该父类就是抽象类 。共性的功能: 在父类中定义,让子类继承
- 如果某个功能是一个类额外增加的,那么就可以把这个额外的功能定义到接口中,再这个类去实现。额外的功能:在接口中定义,让实现类实现
代码举例:
定义父类
// 抽象父类,定义共性功能
public abstract class Dog {
//吃
public abstract void eat();
//睡觉
public abstract void sleep();
}
定义接口
//接口,定义额外功能
public interface CatchThief {
//抓小偷
public abstract void CatchThief();
}
定义实现类
//实现类对象
public class Dahuang extends Dog implements CatchThief {
@Override
public void CatchThief() {
System.out.println("抓小偷");
}
@Override
public void eat() {
System.out.println("吃骨头");
}
@Override
public void sleep() {
System.out.println("睡觉");
}
}
定义测试类
public class Test {
public static void main(String[] args) {
//创建实现类对象
Dahuang dahuang = new Dahuang();
//调方法
dahuang.CatchThief();//抓小偷
dahuang.eat();//吃骨头
dahuang.sleep();//睡觉
}
}