李刚老师《JAVA疯狂讲义》第5版,第6章学习笔记。
抽象类是从多个类中抽象出的模板,若将这种抽象进行的更彻底,则可以提炼出一种特殊的“抽象类”,接口。
接口是从多个相似类中抽象出来的规范,他本身不提供任何实现。接口定义的是多个类共同的公共行为规范,具体就是一些公共的抽象方法,它并不关心这些类的内部状态数据,也不关心这些类里面方法的实现细节,它只规定这些类必须提供某些方法,这些方法就是类与外部交流的通道。
接口中可以包含常量、抽象方法、内部类、接口、枚举,在JAVA8之后,可以包含默认方法和类方法。
接口的定义不再使用class关键字,而是使用interface关键字,例如:
interface NBAteam{ //接口定义成员变量只能是常量 int teamNum = 30; //接口定义普通方法 void signPlayers(); void excuteTactics(int tacticalNum); //接口定义默认方法 default void test() { System.out.println("我是接口的默认方法"); } //接口定义类方法 static void test2() { System.out.println("我是接口的类方法"); } } public class Demo01{ public static void main(String[] args) { //访问接口中的成员变量 System.out.println(NBAteam.teamNum); //调用接口中的类方法 NBAteam.test2(); } }接口定义的规则:
接口前可以添加public修饰符或者无修饰符,无修饰符代表包访问权限。由于接口只是一种规范,因此不能包含构造器和初始化块由于接口只是一种规范,因此接口中所有的成员,都是public访问权限,public访问修饰符可以省略对于接口中定义的成员变量,系统会自动添加static final修饰符,因为接口中不能定义初始化块和构造器,因此必须在定义时指定默认值接口中的方法只能是普通方法、类方法或默认方法: 接口中的普通方法需使用public abstract修饰,若省略则系统自动加上,普通方法不能包含方法体; 默认方法使用public default修饰,必须包含方法体,public可以省略。由于默认方法没有static修饰,因此不能直接利用接口调用默认方法,需要使用接口实现类的实例来调用; 类方法用public static修饰,public可以省略,不能用default修饰,类方法可以直接使用接口调用。接口的继承与类继承不同,接口支持多继承,一个接口可以有多个直接父类,子接口会获得父接口定义的所有抽象方法、常量等。例如:
interface A { int a = 5; void testA(); } interface B { int b = 5; void testB(); } interface C extends A,B { int c = 5; void testC(); } public class Demo01{ public static void main(String[] args) { //接口C可以调用A、B接口中的常量 System.out.println(C.a); System.out.println(C.b); System.out.println(C.c); } }C接口继承了A、B两个接口,可以调用A、B中的常量。
接口可以用于声明引用变量,但是不能用于直接创建实例。因此,当接口用来声明引用变量时,这个引用变量必须引用到其实现类的量。接口的主要用途包括:被实现类实现(使用implements关键字),调用接口中定义的常量。例如:
//定义output接口 interface Output{ //定义常量 int MAX_CACHE_LINE = 50; //定义普通方法 void out(); void getData(String msg); //定义默认方法 //意思是msgs这个参数的长度是方法 default void print(String...msgs) { for(String msg:msgs) { System.out.println(msg); } } default void test() { System.out.println("我是Output接口的默认方法"); } //定义接口中的类方法 static String staticTest() { return "我是Output接口的类方法"; } } //定义Product接口 interface Product{ int getProduceTime(); } //定义Deom01实现Output、Product接口 public class Printer implements Output,Product{ //定义字符串数组,用以保存需要打印的数据 private String[] printData = new String[MAX_CACHE_LINE]; //记录当前需要打印的作业数 private int dataNum = 0; public void out() { //只要需要打印的作业数>0,就继续打印 while(dataNum > 0) { System.out.println("打印机打印:"+printData[0]); //作业队列整体前移一位,剩下作业数减1 System.arraycopy(printData,1,printData,0,--dataNum); } } //实现接口中的普通方法 public void getData(String msg) { if(dataNum >= MAX_CACHE_LINE) { System.out.println("输出队列已满,添加失败"); } else { //把要打印的数据添加至打印队列中 printData[dataNum++] = msg; } } public int getProduceTime() { return 45; } public static void main(String[] args) { //创建Printer对象,但是用Output对象引用 Output o = new Printer(); o.getData("湖人队"); o.getData("热火队"); o.getData("掘金队"); o.getData("凯尔特人队"); o.out(); //调用接口中的默认方法 o.print("马赛克队","快船队","雄鹿队","猛龙队"); o.test(); //调用接口中的类方法 System.out.println(Output.staticTest()); //创建一个Printer对象,当作Product使用 Product p = new Printer(); System.out.println(p.getProduceTime()); } }上述代码的输出为:
打印机打印:湖人队 打印机打印:热火队 打印机打印:掘金队 打印机打印:凯尔特人队 马赛克队 快船队 雄鹿队 猛龙队 我是Output接口的默认方法 我是Output接口的类方法 45
可见:
接口的实现类的实例,可以被赋值给接口的引用变量接口的实现类必须实现接口的所有普通方法接口的默认方法需要借助接口的实现类的实例来调用接口的类方法无需借助实现类来实现因为Printer类实现了Output接口和Product接口,因此Printer对象可以直接赋值给Output变量,也可以赋值给Product对象,JAVA以此来模拟多继承实现类在实现接口的普通方法时,必须使用public修饰符,因为接口里的普通方法都是默认public修饰的接口和抽象类存在一些相同特征:
接口和抽象类均不能被实例化,均用于被其他类实现和继承接口和抽象类都可以包含抽象方法,实现类都必须实现这些抽象方法但是,接口和抽象类之间有很大的差别,其本质差异在与设计目的:
接口的设计目的: 接口是系统与外界交互的窗口,规定了交互的规范。也就是,系统的设计者需要遵守接口的规范,来定义提供的服务;系统的使用者需要遵守接口的规范,来调用提供的服务。这里的服务就可以理解为方法。
抽象类的设计目的: 抽象类是一种设计模板,可以理解为系统的中间产品。这个中间产品已经实现了部分功能(就是抽象类中提供实现的方法),但是需要进一步完善才能得到最终产品。
除此之外,接口和抽象类在用法上也存在一些差别:
接口不能为普通方法提供方法实现,抽象类可以接口不能定义静态方法,抽象类可以接口只能定义静态常量,不能定义普通成员变量,抽象类都可以接口中不能包括构造器,抽象类可以接口中不能包括初始化块,抽象类可以一个类只能有一个直接父类(包括抽象类),但是可以实现多个接口