一、接口
1、接口概述
接口是 Java 语言中一种引用类型,是抽象方法和常量值定义的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。
接口就是规范,定义的一组规则。继承是一个“是不是”的关系,而接口实现则是“能不能”的关系。
接口的本质是契约,标准,规范,就像我们的法律一样,制定好后大家都要遵守。
2、接口的定义
它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
Tips:引用数据类型:数组,类,接口等。
3、接口的使用
它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
4、接口的成员
常量值和抽象方法。接口(interface)是抽象方法和常量值定义的集合。
5、接口的特点
(1)接口用 interface 来定义;
(2)接口中的所有成员变量都默认是由 public static final 修饰的;
(3)接口中的所有抽象方法都默认是由 public abstract 修饰的;
(4)接口中没有构造器,意味着接口不可以实例化;
(5)接口是采用多继承机制;
二、定义格式
1、接口的定义
定义格式:
【权限修饰符】 interface 接口名称 {
接口的成员列表如下:
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
2、
三、接口中的成员
1、JDK7以及之前的成员
JDK1.7及以前接口成员有两种:静态常量和抽象方法(JDK7+)
全局的静态常量:接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰(可省略),其实就是接口中的【常量】。
公共的抽象方法:使用publc abstract 关键字修饰,(可以省略)没有方法体。该方法供子类实现使用。
语法格式:
public static final 数据类型 变量名 = 值;
public abstract 返回值类型 方法名称(参数列表);
Demo:
1 public interface InterFaceName {
2 public static final long MAX_SPEED = 7900000; //常量
3 public abstract void method(); //抽象方法
4 }
Tips:一旦使用 final 关键字进行修饰,说明不可改变。
注意事项:
(1)接口当中的常量,可以省略 public static final,注意,省略时也是这样修饰的;(接口中的变量都是常量)
(2)接口当中的常量,必须进行赋值;不能不赋值;
(3)接口中常量的名称,使用完全大写的字母,用下划线进行分割。(推荐命名规则)
2、JDK8之后的成员
JDK1.8 之后接口又增强两个成员:默认方法和静态方法(JDK8+)
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
语法格式:
public default 返回值类型 方法名称(参数列表) {
方法体
}
静态方法:使用 public static 修饰,不可省略,供接口直接调用。
Demo:
1 public interface InterFaceName {
2 public default void method() {
3 // 执行语句
4 }
5 public static void method2() {
6 // 执行语句
7 }
8 }
扩展:
(1)为什么 Java8 要允许接口中定义静态方法?
使用 static 关键字修饰。 可以通过接口直接调用静态方法,并执行其方法体。
因为JDK发展了一段时间后,发现类库中,多了很多的成组的API,如Array和Arrays类,Collection接口和Collections工具类。
一般工具类都是静态方法,这些静态方法,基本上都是为前面这个对应接口服务的。当把这样的静态方法直接挪到接口中定义就好了,减少了这样的工具类的出现。
(2)为什么 Java8 要允许接口中定义默认方法?
默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
因为有的时候,一个接口它的大多数实现类对接口的抽象方法的实现代码是一样的,那样写好几次就非常麻烦。即相当于,默认方法是原来的抽象方法有了一个默认实现。
如果实现类的实现和接口一样,就不需要重写,如果不一样就重写即可。
3、JDK9
JDK1.9 之后增加两个接口成员:私有方法和私有静态方法(JDK9+)
私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。
私有静态方法:使用 private static 修饰,供接口中的默认方法或者静态方法调用。
Demo:
public interface InterfaceTest {
private void method5() {
System.out.println("私有方法");
}
private static void method6() {
System.out.println("私有的静态方法");
}
}
四、接口的实现
1、接口的实现
(1)类与接口的关系为实现关系,即类实现接口,该类也可以称为接口的实现类,也可以称为接口的子类。
(2)实现的动作似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
(3)定义 Java 类的语法格式:先写 extends,后写 implements;
class SubClass extends SuperClass implements InterfaceA{ }
(4)一个类可以实现多个接口, 接口也可以继承其它接口;
(5)实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类;
(6)接口的主要用途就是被实现类实现;与继承关系类似,接口与实现类之间存在多态性;
(7)Java类可以实现多个接口 --->弥补了Java单继承性的局限性;
(8)接口与接口之间可以继承,而且可以多继承;
(9)接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7之前),而没有变量和方法的实现。
2、非抽象子类实现接口
(1)必须重写接口中所有的抽象方法;
(2)继承了接口的默认方法,即可以直接调用,也可以重写;
(3)接口中的静态方法,不能进行重写;
实现格式:
class 类名 implements 接口名 { // 重写接口中抽象方法【必须】 // 重写接口中默认方法【可选】
// 静态方法不可重写 }
3、抽象方法
接口中的抽象方法必须全部实现。
Demo:
1 // 定义接口:
2 public interface LiveAble {
3 // 定义抽象方法
4 public abstract void eat();
5 public abstract void sleep();
6 }
7
8 // 定义实现类
9 public class Animal implements LiveAble {
10 @Override
11 public void eat() {
12 System.out.println("吃东西");
13 }
14 @Override
15 public void sleep() {
16 System.out.println("晚上睡");
17 }
18 }
4、默认方法
可以继承,可以重写,但是只能二选一,并且只能通过实现类的对象来调用;
(1)继承默认方法
Demo:
1 // 定义接口
2 public interface LiveAble {
3 public default void fly(){
4 System.out.println("天上飞");
5 }
6 }
7
8 // 定义实现类
9 public class Animal implements LiveAble {
10 // 继承,什么都不用写,直接调用
11 }
12
13 // 测试类
14 public class InterfaceDemo {
15 public static void main(String[] args) {
16 // 创建子类对象
17 Animal a = new Animal();
18 // 调用默认方法
19 a.fly();
20 }
21 }
22 输出结果:
23 天上飞
(2)重写默认方法
Demo:
1 // 定义接口
2 public interface LiveAble {
3 public default void fly(){
4 System.out.println("天上飞");
5 }
6 }
7
8 // 定义实现类
9 public class Animal implements LiveAble {
10 @Override
11 public void fly() {
12 System.out.println("自由自在的飞");
13 }
14 }
15
16 // 定义测试类
17 public class InterfaceDemo {
18 public static void main(String[] args) {
19 // 创建子类对象
20 Animal a = new Animal();
21 // 调用重写方法
22 a.fly();
23 }
24 }
25 输出结果:
26 自由自在的飞
5、静态方法
静态与 .class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象来调用。
语法格式:
public static 返回值类型 方法名称(参数列表) {
方法体
}
Demo:
1 // 定义接口
2 public interface LiveAble {
3 public static void run(){
4 System.out.println("跑起来~~~");
5 }
6 }
7
8 // 定义实现类
9 public class Animal implements LiveAble {
10 // 无法重写静态方法
11 }
12
13 // 定义测试类
14 public class InterfaceDemo {
15 public static void main(String[] args) {
16 // Animal.run(); // 【错误】无法继承方法,也无法调用
17 LiveAble.run(); //
18 }
19 }
20 输出结果:
21 跑起来~~~
6、私有方法与私有静态方法
-
- 私有方法:只有默认方法可以调用;
- 私有静态方法:默认方法和静态方法可以调用;
语法格式:
private 返回值类型 方法名称(参数列表){ 方法体} 普通私有方法
private static 返回值类型 方法名称(参数列表) { 方法体} 静态私有方法
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。
Demo:
1 public interface LiveAble {
2 default void func(){
3 func1();
4 func2();
5 }
6 private void func1(){
7 System.out.println("跑起来~~~");
8 }
9 private void func2(){
10 System.out.println("跑起来~~~");
11 }
12 }
7、
五、接口的多实现(接口冲突)
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
实现格式:
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【不重名时可选】
}
1、抽象方法
接口中,有多个抽象方法时,实现类必须重写所有抽象方法。
如果抽象方法有重名的,只需要重写一次。
Demo:
1 // 定义接口
2 interface A {
3 public abstract void showA();
4 public abstract void show();
5 }
6 interface B {
7 public abstract void showB();
8 public abstract void show();
9 }
10
11 //定义实现类
12 public class C implements A,B{
13 @Override
14 public void showA() {
15 System.out.println("showA");
16 }
17 @Override
18 public void showB() {
19 System.out.println("showB");
20 }
21 @Override
22 public void show() {
23 System.out.println("show");
24 }
25 }
2、默认方法(接口冲突)
当一个类同时实现了两个甚至多个接口时,这些个接口中出现了方法签名相同的默认方法时,
必须在这个类中,做出选择:
(1)保留其中一个,放弃另一个;
(2)两者都不用,完全重写一个;
Demo:
1 interface A{
2 public default void test(){
3 System.out.println("aaa");
4 }
5 }
6 interface B{
7 public default void test(){
8 System.out.println("bbb");
9 }
10 }
11 class C implements A,B{
12 //选择一:保留其中一个,放弃另一个
13 A.super.test(); //保留A接口的实现
14 B.super.test(); //保留B接口的实现
15 }
16
17 class C1 implements A,B{
18 //选择二:两者都不用,完全重写一个
19 public void test(){
20 System.out.println("ccc");
21 }
22 }
3、静态方法
接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
4、优先级的问题
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的方法签名一样的方法,怎么办?
如何选择?
(1)默认选择:编译器默认选择父类中的方法;
(2)改选保留接口的;
(3)完全自己重写;
Demo:
1 class Father{
2 public void test(){
3 System.out.println("ffff");
4 }
5 }
6 interface D{
7 public default void test(){
8 System.out.println("dddd");
9 }
10 }
11 class Son1 extends Father implements D{
12 //选择一:默认选择, 保留父类的
13 }
14
15 class Son2 extends Father implements D{
16 //选择二:改选接口的
17 public void test() {
18 D.super.test();
19 }
20 }
21
22 class Son3 extends Father implements D{
23 // 完全自己重写
24 public void test() {
25 System.out.println("ssss");
26 }
27 }
5、接口中的默认方法
(1)若一个接口中定义了一个默认方法,而另外一个接口中定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。
解决方法:实现类中必须覆盖接口中同名同参数的方法,来解决冲突。
(2)若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
6、小结
(1)接口中定义的静态方法,只能通过接口名来调用。
(2)通过实现类的对象,可以调用接口中的默认方法,如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法;
(3)如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
(4)如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,这就需要我们必须在实现类中重写此方法。
(5)如何在子类(或实现类)的方法中调用父类、接口中被重写的方法,通过 接口名.super.方法名();
Demo:
1 public interface CompareA {
2
3 //静态方法
4 public static void method1(){
5 System.out.println("CompareA:北京");
6 }
7 //默认方法
8 public default void method2(){
9 System.out.println("CompareA:上海");
10 }
11
12 default void method3(){
13 System.out.println("CompareA:上海");
14 }
15 }
16 public interface CompareB {
17
18 default void method3(){
19 System.out.println("CompareB:上海");
20 }
21
22 }
23 public class SuperClass {
24
25 public void method3(){
26 System.out.println("SuperClass:北京");
27 }
28
29 }
30 class SubClass extends SuperClass implements CompareA,CompareB{
31
32 public void method2(){
33 System.out.println("SubClass:上海");
34 }
35
36 public void method3(){
37 System.out.println("SubClass:深圳");
38 }
39
40 //知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
41 public void myMethod(){
42 method3();//调用自己定义的重写的方法
43 super.method3();//调用的是父类中声明的
44 //调用接口中的默认方法
45 CompareA.super.method3();
46 CompareB.super.method3();
47 }
48 }
49 ----------------------
50 public class SubClassTest {
51
52 public static void main(String[] args) {
53 SubClass s = new SubClass();
54
55 // s.method1();
56 // SubClass.method1();
57 //知识点1:接口中定义的静态方法,只能通过接口来调用。
58 CompareA.method1();
59 //知识点2:通过实现类的对象,可以调用接口中的默认方法。
60 //如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
61 s.method2();
62 //知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
63 //那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
64 //知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
65 //那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
66 //这就需要我们必须在实现类中重写此方法
67 s.method3();
68
69 }
70
71 }
六、接口的多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。
接口的继承使用 extends 关键字,子接口继承父接口的方法。
如果父接口的默认方法有重名的,那么子接口需要重写一次。
Demo:
1 //定义父接口
2 interface A {
3 public default void method(){
4 System.out.println("AAAAA");
5 }
6 }
7 interface B {
8 public default void method(){
9 System.out.println("BBBBB");
10 }
11 }
12
13 // 定义子接口
14 interface D extends A,B{
15 @Override
16 public default void method() {
17 System.out.println("DDDDD");
18 }
19 }
Tips:子接口重写默认方法时,default 关键字可以保留;子类重写默认方法时,default 关键字不可以保留。
1、
2、
3、
4、
七、接口的多态性
1、多态性
接口的多态性
Demo:
1 public class USBTest { 2 public static void main(String[] args) { 3 4 Computer com = new Computer(); 5 //1.创建了接口的非匿名实现类的非匿名对象 6 Flash flash = new Flash(); 7 com.transferData(flash); 8 9 //2. 创建了接口的非匿名实现类的匿名对象 10 com.transferData(new Printer()); 11 12 //3. 创建了接口的匿名实现类的非匿名对象 13 USB phone = new USB(){ 14 15 @Override 16 public void start() { 17 System.out.println("手机开始工作"); 18 } 19 20 @Override 21 public void stop() { 22 System.out.println("手机结束工作"); 23 } 24 25 }; 26 com.transferData(phone); 27 28 29 //4. 创建了接口的匿名实现类的匿名对象 30 31 com.transferData(new USB(){ 32 @Override 33 public void start() { 34 System.out.println("mp3开始工作"); 35 } 36 37 @Override 38 public void stop() { 39 System.out.println("mp3结束工作"); 40 } 41 }); 42 } 43 } 44 45 class Computer{ 46 47 public void transferData(USB usb){//USB usb = new Flash(); 48 usb.start(); 49 50 System.out.println("具体传输数据的细节"); 51 52 usb.stop(); 53 } 54 55 56 } 57 58 interface USB{ 59 //常量:定义了长、宽、最大最小的传输速度等 60 61 void start(); 62 63 void stop(); 64 65 } 66 67 class Flash implements USB{ 68 69 @Override 70 public void start() { 71 System.out.println("U盘开启工作"); 72 } 73 74 @Override 75 public void stop() { 76 System.out.println("U盘结束工作"); 77 } 78 79 } 80 81 class Printer implements USB{ 82 @Override 83 public void start() { 84 System.out.println("打印机开启工作"); 85 } 86 87 @Override 88 public void stop() { 89 System.out.println("打印机结束工作"); 90 } 91 92 }
3、
八、接口中成员特点
1、接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用 public static final 修饰。
2、接口中,没有构造方法,不能创建对象。
3、接口中,没有静态代码块。
4、一个类如果直接继承父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。
九、接口的特点
1、接口是标准,就是用来被遵守的,即就是用来被实现的,那么要求实现类在实现接口时,必须实现/重写所有的抽象方法,否则这个实现类就得是一个抽象类;
2、接口一定没有构造方法,也就说不能直接创建对象的;
3、接口类型的引用变量与实现类的对象构成了多态引用;
4、一个类继承父类时,Java 只支持单继承,但是一个类在实现接口时,可以同时实现多个接口;
5、一个类如果同时继承父类,又实现接口时,要求继承类在前,实现接口在后;
【修饰符】 class 实现类 extends 父类 implements 父接口们{}
6、在 Java 中,接口还可以继承多个接口;
【权限修饰符】 interface 子接口 extends 父接口们{ }
十、常见面试题
1、题目一
1 interface A {
2 int x = 0;
3 }
4
5 class B {
6 int x = 1;
7 }
8
9 class C extends B implements A {
10 public void pX() {
11 System.out.println(x); //编译不通过,此时的 x 是不明确的,不知道是接口中的,还是类中的
12 }
13
14 public static void main(String[] args) {
15 new C().pX();
16 }
17 }
18
19 修改:
20 如果要使用 类中的 变量:System.out.println(super.x);//1
21 如果要使用 接口中的变量(常量):System.out.println(A.x);//0
2、题目二
3、
4、
5、
6、
十一、抽象类与接口
1、抽象类与接口的对比
2、接口与抽象类的异同
接口:
(1)接口中不能定义构造器;
(2)接口中方法全部都是抽象方法(JDK7及之前);
(3)接口中的成员全部都是public 的;
(4)接口中定义的成员变量实际上都是常量;
(5)一个类可以实现多个接口;
抽象类:
(1)抽象类中可以定义构造器;
(2)可以有抽象方法和具体方法;
(3)抽象类中可以定义成员变量;
(4)抽象类中的成员可以是 private、默认、protected、public、
(4)有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法;
(5)抽象类中可以包含静态方法;
(6)一个类只能继承一个抽象类;
相同点:
(1)不能够实例化;
(2)可以将抽象方法和接口类型作为引用类型;
(3)一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要声明为抽象类。