接口的概述
java提供了一个关键字interface来定义接口。接口是一种纯粹的抽象类型,强调能做什么而非是什么。
//接口的定义
public interface A{ }
JDK8以前,接口中只能定义常量和抽象方法。与抽象类类似,接口不能创建对象。接口是用来被类实现(implements)的,一个类可以实现多个接口,实现接口的类必须重写所实现接口的全部抽象方法,否则得定义成抽象类,实现接口的类一般被称为实现类。
实现类的定义
public class B implements A{
//重写的A接口内的抽象方法
}
接口中的常量默认是public static final修饰,此前缀可以省略不写。接口中的抽象方法默认是public abstract修饰,同样在接口中此前缀可省略不写。下面是接口的一个案例:
package implementDemo1;
//JDK8之前,接口中只能定义常量和抽象方法
public interface A {
//1、常量:接口中的常量默认是public static final修饰,可以不写
String NAME="Code Blossom";
//2、抽象方法:接口中的抽象方法默认是public abstract修饰,可以不写
void run();
void show();
String go();
}
package implementDemo1;
public interface B {
void paly();
}
package implementDemo1;
public class Text {
public static void main(String[] args) {
System.out.println(A.NAME);
//接口不能创建对象
//A a=new A() { }; //报错
//接口是用来被类实现(implement)的,实现接口的类型被称为实现类,一个类可以实现多个接口
//与抽象类相同,实现类要实现接口中的所有抽象方法
C c=new C();
c.run();
c.show();
c.paly();
System.out.println(c.go());
}
}
//C称为实现类,同时实现多个接口
//实现类实现多个接口,必须重写所有接口中的所有抽象方法,否则必须定义成抽象类
class C implements A,B{
@Override
public void run() {
System.out.println("C run");
}
@Override
public void show() {
System.out.println("C show");
}
@Override
public String go() {
return "Code Blossom";
}
@Override
public void paly() {
System.out.println("C play");
}
}
接口不同于类,只能被单继承,实现类可以实现多个接口,弥补了单继承的不足,使类的角色更多,功能更强大。例如,一个学生可以有多重身份,他可以是司机,也能是某个女生的男朋友,但类的单继承无疑无法简单实现这种复合身份,而使用接口可以简单解决。
package implementDemo2;
public class Text {
public static void main(String[] args) {
//目的:理解接口的好处
//接口弥补了单继承的不足,一个类可以实现多个接口,使类的角色更多,功能更强大
People p = new Student();
BoyFriend bf = new Student();
Driver d = new Student();
}
}
//弥补了单继承的不足,一个类可以实现多个接口,使类的角色更多,功能更强大
//让程序可以面向接口编程,程序员可以灵活方便的切换各种业务实现(更有利于程序的解耦和)
interface Driver{ }
interface BoyFriend{ }
class People{ }
//继承People类,实现Driver和BoyFriend接口,使Student类功能更强大
class Student extends People implements Driver,BoyFriend{
}
上述代码的Student不仅继承了People类(即人的基本特性),还通过实现多个接口拥有了司机与男朋友的特性,使类的角色变多了,功能也随之变强。
JDK8以后,接口新增了三种方法,分别是默认方法,私有方法与静态方法。默认方法即为普通实例方法,区别在与接口中的默认方法需要在方法的返回类型前加上default修饰,说明这是默认方法。
私有方法用private修饰,若是需要调用私有方法需使用接口中的其他实例方法调用。静态方法与前面学习的相同,是用static修饰的方法,用类名.方法名访问,在接口中public可省略不写。
代码实例
package implementDemo4;
public interface A {
//1、默认方法(普通实例方法):必须加default修饰
//接口中的普通方法,默认是public的,可以省略public
//如何调用? 使用接口的实现类的对象来调用
default void print(){
System.out.println("A.print()");
}
//2、私有方法,JDK9开始支持,用private修饰
//如何调用? 使用接口中的其他实例方法调用
private void print2(){
System.out.println("A.print2()");
}
default void print1(){
print2();
System.out.println("A.print1()");
}
//3、静态方法
//静态方法,默认是public的,可以省略public
//如何调用? 只能使用接口名来调用
static void print3(){
System.out.println("A.print3()");
}
}
package implementDemo4;
//JDK8开始,接口新增的三种方法
public class Text {
public static void main(String[] args) {
A a = new AImpl();
a.print();
//a.print3(); 接口中的静态方法不能用对象调用,此处报错
A.print3();
a.print1();
}
}
class AImpl implements A {
}
与类不同的是,接口中的静态方法无法用对象进行访问,若是用对象名.方法名会报错。
接口的注意事项:
接口与接口间可以多继承
一个类支持多个接口,但如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现
一个类实现了多个接口,如果多个接口中存在同名的方法,可以不冲突,这个类重写这个方法就行。
package implementDemo5;
public class Text {
//目标:理解接口的几个注意事项
//一个类支持多个接口,但如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现
//一个类继承了父类,又同时实现了接口,如果父类和接口中有同名方法,实现类会优先使用父类的方法
//一个类实现了多个接口,如果多个接口中存在同名的方法,可以不冲突,这个类重写这个方法就行
public static void main(String[] args) {
}
}
interface A1{
void show1();
}
interface B1{
String show1();
}
//此时报错
//interface C1 extends A1,B1 {
// void show3();
//}
如上述代码,接口A1与接口B1中show1方法的返回类型不同,此时用接口C1继承接口A1与接口B1就会报错,因为两个方法的签名不同。
一个类继承了父类,又同时实现了接口,如果父类和接口(default定义的实例方法)中有同名方法,实现类会优先使用父类的方法。若是需要使用接口的方法,需要在类中创建一个中转方法,用接口名(必须是本类直接实现的接口).super.方法名
package implementDemo5;
public class Text {
//目标:理解接口的几个注意事项
//一个类支持多个接口,但如果多个接口中存在方法签名冲突,则此时不支持多继承,也不支持多实现
//一个类继承了父类,又同时实现了接口,如果父类和接口中有同名方法,实现类会优先使用父类的方法
//一个类实现了多个接口,如果多个接口中存在同名的方法,可以不冲突,这个类重写这个方法就行
public static void main(String[] args) {
D d=new D();
d.print();
}
}
interface A1{
void show1();
}
interface B1{
String show1();
}
//此时报错
//interface C1 extends A1,B1 {
// void show3();
//}
//接口与接口可以多继承,一个接口可以同时继承多个接口
//类与类,单继承,一个类只能有一个直接父亲
//类与接口,多实现,一个类可以实现多个接口
interface A {
void show1();
default void show4(){
System.out.println("接口的show4");
}
}
class Animal{
public void show4(){
System.out.println("父类的show4");
}
}
interface B {
void show2();
}
interface C extends A,B {
void show3();
}
//接口C继承了接口A与接口B,所以此时想实现接口A,B,C只需要implements C即可
//但需要重写所有接口中的抽象方法
class D extends Animal implements C {
@Override
public void show1() {
System.out.println("show1");
}
@Override
public void show2() {
System.out.println("show2");
}
@Override
public void show3() {
System.out.println("show3");
}
public void print(){
show4();//此时会调用父类的show4
//若是想要调用接口的show4,则需要使用接口名.super.show4()
C.super.show4();//此时会调用接口的show4
}
}
最后,进行一个项目实战,体现接口在业务处理方面的优势
任务:设计一个班级学生的信息管理模块,学生数据有性别,姓名,成绩
业务一、打印出全班学生的信息,打印全班成绩的平均分
业务二、打印出班级全部学生的信息(包含男女人数),打印出平均成绩(去掉一个最高分和一个最低分)
package implementDemo3;
public interface Operator {
void print_student();
void print_averagescore();
}
package implementDemo3;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {
private String name;
private char sex;
private int score;
}
package implementDemo3;
public class Operator1 implements Operator{
private Student[] students;
public Operator1(Student[] students)
{
this.students=students;
}
public Operator1(){ }
public void GetStudent(Student[] sutdents)
{
this.students=students;
}
@Override
public void print_student() {
for(int i=0;i<students.length;i++)
{
System.out.println(students[i].getName()+" "+students[i].getSex()+" "+students[i].getScore());
}
}
@Override
public void print_averagescore() {
int sum=0;
for(int i=0;i<students.length;i++)
{
sum+=students[i].getScore();
}
System.out.println(sum/students.length);
}
}
package implementDemo3;
public class Operator2 implements Operator{
Student[] students;
public Operator2(Student[] students) {
this.students = students;
}
public Operator2(){ }
public void GetStudent(Student[] sutdents)
{
this.students=sutdents;
}
@Override
public void print_student() {
int malecount=0;
for(int i=0;i<students.length;i++)
{
System.out.println(students[i].getName()+" "+students[i].getSex()+" "+students[i].getScore());
if(students[i].getSex()=='男')
malecount++;
}
System.out.println("男学生人数为:"+malecount);
System.out.println("女学生人数为:"+(students.length-malecount));
}
@Override
public void print_averagescore() {
int sum=students[0].getScore();
int max=students[0].getScore();
int min=students[0].getScore();
for(int i=1;i<students.length;i++)
{
sum+=students[i].getScore();
if(students[i].getScore()>max)
max=students[i].getScore();
if(students[i].getScore()<min)
min=students[i].getScore();
}
System.out.println((sum-max-min)/(students.length-2));
}
}
package implementDemo3;
//任务:设计一个班级学生的信息管理模块,学生数据有性别,姓名,成绩
//业务一、打印出全班学生的信息,打印全班成绩的平均分
//业务二、打印出班级全部学生的信息(包含男女人数),打印出平均成绩(去掉一个最高分和一个最低分)
public class Text {
public static void main(String[] args) {
Student[] students = new Student[5];
students[0] = new Student("张三", '男', 90);
students[1] = new Student("李四", '女', 80);
students[2] = new Student("王五", '男', 70);
students[3] = new Student("赵六", '女', 60);
students[4] = new Student("钱七", '男', 50);
Operator operatoruser = new Operator2(students);
//当需要切换业务时,仅需在右边的new修改即可,利用多态实现了解耦合思想。
operatoruser.print_student();
operatoruser.print_averagescore();
}
}
如上述代码,当想要切换业务时,仅需将Text定义业务对象的右边的Operator2修改成Operator1即可完成业务1、2自由切换。
抽象类与接口的相同点与不同点
相同点:
1、都可以有抽象方法,都不能创建对象
2、都是为子类接收他们的特性创建的,抽象类被子类继承,接口被子类实现。
3、一个类若是不设置为抽象类,想要继承抽象类或是实现接口,都需要重写其抽象方法,否则报错。
4、都支持多态,都能实现解耦合。
不同点:
1、抽象类可以定义类中的全部普通成员,接口只能定义常量,抽象方法(与JDK8后新增的三种方式)。
2、一个类继承了抽象类就不能继承其他类,一个类实现了接口还能实现其他接口或继承其他类。
3、抽象类只能被单继承,接口可以被多实现。
4、接口更适合用于代码的解耦合(可以被多实现),解耦合功能更强相比于抽象类。
5、抽象类体现模版思想,更适合作父类,实现代码复用。