一、引言
在 Java 编程中,接口是一种关键的概念,它提供了定义行为和方法规范的方式。接口允许我们定义一组方法,但不包含方法的具体实现。通过接口,我们可以实现类之间的解耦和多态性,使得程序更加灵活、可扩展和易于维护。
二、概念
- 从功能上看, 实现接口就意味着扩展了某些功能
- 从抽象上看,接口对于类而言就是特殊的抽象父类
- 从规则上看, 是接口定义者和接口实现者都必须遵守的某些规则
三、语法
[public] interface 接口名{
}
写在com.xxx.dao包下
四、规则
- 属性必须为公开静态常量(public static final)
- 由于修饰符固定,所以可以省略或者缺失,编译器会自动填充
- 方法为公开抽象方法(public abstract)
- 不存在构造方法
- 不能被实例化对象
package com.by.dao;
public interface IA {
public static final int A = 100;
final int B = 200;
int C = 300;
public abstract void m1();
public void m2();
void m3();
}
public interface IB {
public void ma();
}
五、实现类
[public] class 实现类名 implements 接口名{
}
实现类写在com.xxx.dao.impl包下
package com.by.dao.impl;
import com.by.dao.IA;
import com.by.dao.IB;
import com.by.entity.Super;
public class IA_IB_Impl extends Super implements IA, IB{
@Override
public void m1() {
System.out.println("IA_IB_Impl-m1");
}
@Override
public void m2() {
System.out.println("IA_IB_Impl-m2");
}
@Override
public void m3() {
System.out.println("IA_IB_Impl-m3");
}
@Override
public void ma() {
System.out.println("IA_IB_Impl-ma");
}
public void method() {
System.out.println("IA_IB_Impl-method");
}
}
规则
-
必须对接口的抽象方法提供方法实现
- 除非实现类本身是抽象类(不推荐)
-
一个实现类可以实现多个接口,一个接口也可以拥有多个实现类(多实现)
- 语法补充:
[public] class 实现类名 implements 接口名1,接口名2,..{ }
-
实现类如果实现了多个接口,必须对所有接口中的抽象方法都提供方法实现
-
类可以同时继承父类和实现接口,先继承后实现
[public] class 类名 extends 父类类名 implements 接口名1,接口名2,..{ }
-
接口名仍然可以参与多态
接口名 引用名=new 实现类名();
-
接口参与多态时,仍然不可访问实现类独有内容
- 需要访问独有内容时,必须进行类型强转
- 只能转向原本指向的实现类类型|实现类的其他引用类型
- 同级实现类之间不可强转
//利用多态创建实现类对象 IA ia = new IA_IB_Impl(); ia.m1(); ia.m2(); ia.m3(); /*ia.ma(); ia.method(); 错误,编译失败*/ //将ia引用类型强转为IB接口 IB ib = (IB) ia; ib.ma(); //将ia引用强转为实现类类型 IA_IB_Impl ia_ib = (IA_IB_Impl) ia; // IAImpl iaimpl = (IAImpl) ia; 运行报错 // IAImpl iaImpl = (IAImpl) ia_ib; 编译报错
- 需要访问独有内容时,必须进行类型强转
-
仍然可以使用instanceof关键字
引用 instanceof 接口名
IA ia = new IA_IB_Impl(); System.out.println(ia instanceof IA_IB_Impl);//t System.out.println(ia instanceof IA);//t System.out.println(ia instanceof IB);//t
六、接口间的继承
一个接口可以继承多个父接口(多继承)
[public] interface 子接口名 extends 父接口名1,父接口名2,..{
}
- 子接口可以继承拥有所有父接口中的内容
七、接口与抽象类的区别
抽象类 | 接口 | |
---|---|---|
关键字 | abstract class | interface |
属性 | 不做要求 | 公开静态常量 |
方法 | 不做要求,可以存在非抽象方法 | 公开抽象方法 |
构造 | 有 | 无 |
继承性 | 单继承 | 多继承 |
八、接口高级
(1)高版本的接口
JDK8.0
-
默认方法
public default 返回值类型 方法名(形参列表){ }
- 当类继承父类又实现接口, 父类和接口中的内容出现冲突时,优先执行父类内容(类优先原则)
- 当同时实现多个接口, 接口之间的方法出现冲突时, 实现类必须对冲突方法提供重写,使用自身重写内容
-
静态方法
public static 返回值类型 方法名(形参列表){ }
- 可以通过
接口名.方法名(实参)
的方式直接调用
- 可以通过
JDK9.0
-
私有方法
private 返回值类型 方法名(形参列表){ }
- 实现类无法扩展该方法
(2)接口回调
“开闭原则”:扩展开放,修改关闭
允许在现有代码的基础上扩展功能,但是前提时不允许更改已有的代码
1.概念
将方法的形参声明为接口类型,在传入实参时可以传入不同的实现类对象
本质就是多态的第二个使用场景的体现
/*
* 定义一个灯泡接口,要求内置发光方法
* 定义三个实现类,分别表示红灯泡、绿灯泡、蓝灯泡,重写方法
*
* 定义测试类
* 定义一个函数,要求传出参数,可以执行“红灯泡发红光”、“绿灯泡发绿光”、“蓝灯泡发蓝光”
*
* */
package com.by.dao;
/**
* 灯泡接口
*/
public interface Light {
/**
* 发光方法
*/
void shine();
}
//实现类
package com.by.dao.impl;
import com.by.dao.Light;
public class RedLight implements Light {
@Override
public void shine() {
System.out.println("红灯泡发红光");
}
}
public class GreenLight implements Light {
@Override
public void shine() {
System.out.println("绿灯泡发绿光");
}
}
public class BlueLight implements Light {
@Override
public void shine() {
System.out.println("蓝灯泡发蓝光");
}
}
//测试类
package com.by.test;
import com.by.dao.Light;
import com.by.dao.impl.BlueLight;
import com.by.dao.impl.GreenLight;
import com.by.dao.impl.RedLight;
import com.by.dao.impl.YellewLight;
public class TestLight {
public static void main(String[] args) {
//调用方法时关注实际传入的实现类对象
method(new RedLight());
method(new GreenLight());
method(new BlueLight());
//新增需求:发黄光 1. 创建一个黄灯泡的实现类 2. 调用method方法时传入黄灯泡的对象
method(new YellewLight());
}
//只需要关注接口类型的声明
//只需要约束调用者传入要给实现类对象,并且重写发光方法即可
public static void method(Light light) {
light.shine();
}
}
2.比较器案例
-
创建比较器接口的实现类,并重写排序方法
public class 实现类名 implements Comparator<被排序的类名>{ public int compare(被排序的类名 o1,被排序的类名 o2){ //排序规则 } }
-
书写排序规则:
- 从小到大:
- o1的值>o2的值,返回正数
- o1的值<o2的值,返回负数
- 相等,返回0
- 从大到小:相反
public class StudentComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { //根据学生年龄从小到大 if (o1.getAge() > o2.getAge()) { return 1; } else if (o1.getAge() < o2.getAge()) { return -1; } else { return 0; } } }
- 从小到大:
-
调用
java.util.Arrays.sort(被排序的数组名,Comparator接口的实现类对象)
执行数组排序package com.by.test; import com.by.dao.impl.StudentComparator; import com.by.entity.Student; import java.util.Arrays; public class TestComparator { public static void main(String[] args) { /*int[] arr = {10, 2, 99, 77}; //从小到大的排序 Arrays.sort(arr); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+" "); }*/ //创建一个学生数组 Student[] ss = {new Student("zhangsan", 20, 98), new Student("lisi", 19, 88), new Student("wangwu", 21, 97)}; //根据学生年龄从小到大对其排序 Arrays.sort(ss,new StudentComparator()); //查看 for (int i = 0; i < ss.length; i++) { System.out.println(ss[i].getName()+" "+ss[i].getAge()+" "+ss[i].getScore()); } } /* * * interface 排序接口{ * //书写排序规则 * 排序方法(); * } * * class 实现类 implements 排序接口{ * 排序方法(){ * 书写排序规则 * } * } * * public static void sort(被排序的数组名,包含排序规则的接口){ * * } * *调用 sort(数组名,排序接口的实现类对象) * * */ }
(3)接口的好处
- 代码解耦合
- 提升代码扩展性
- 优化代码结构
九、结语
使用接口,我们可以将类的行为抽象为一组方法,并通过实现接口的类来提供具体的实现。这样,我们可以实现类之间的解耦,提高代码的可维护性和可扩展性。同时,接口还可以用于定义回调函数、实现事件驱动等特性,为我们编写更加灵活和高效的程序提供了便利。
然而,我们也需要注意接口的合理使用,避免过度设计和滥用接口带来的复杂性。在设计接口时,应该考虑到其稳定性和易用性,遵循接口隔离原则,只定义必要的方法,避免接口中的方法过多或过于复杂。
通过对接口的学习和应用,我们可以更好地掌握 Java 编程语言的基础知识,提升自己的编程能力,并在实际项目中写出高效、可靠的代码。接口不仅是面向对象编程的核心概念,也是设计模式和软件架构的基础。