什么是?
它是一套标准,是一套规则,是一套对所有类的统一的要求。
做什么?
它是为了实现多继承而出现的。
有什么?
它只包含两种成份:
1) 公共的静态的常量。
2) 抽象方法。
如何做?
通过 interface 关键字来实现 。
格式:
<span style="font-size:18px;"><span style="font-size:18px;">public interface 接口名 { ..... }</span></span>
已知的:
1) 现存的接口(由专家提供)。
2) 自定义的接口(由程序员自己编写)。
<span style="font-size:18px;"><span style="font-size:18px;"> int obj1.compareTo(obj2 )</span></span>
规定: 对象比较大小必须使用 compareTo 作为方法名, 比较大小后的结果必须是一个int型整数。
且当这个整数大于零时,则表示前者大于后者;
当这个整数小于零时,则表示前者小于后者;
当这个整数等于零时,则表示前者与后者相等。
然而,这两个对象按什么属性比较,当前无法确定。因此,这个方法就必须是抽象的。
因此,这个方法如下:
<span style="font-size:18px;"><span style="font-size:18px;"> public abstract intcompareTo( Object obj );</span></span>
此方法为抽象的。根据所学知识可知:抽象方法要么在抽象类中,要么在接口中。
试想: 当以上 compareTo()方法置于抽象类中时,则问: 所有类是否均可以与这个抽象类发生继承关系?
答案: 否定的。
当以上 compareTo()方法置于接口中,则问: 所有类是否可以与这个接口发生继承(实现)关系?
答案: 肯定的。
由于对象比较大小的规则可以提前制定,因此,专家就为程序员提前准备了一个接口 java.lang.Comparable,其中
存放了 以上抽象方法[ int compareTo(Object obj ) ]
如何实现接口?
通过关键字 implements 关键字来实现
格式为:
<span style="font-size:18px;"><span style="font-size:18px;"> public class 类 implements 接口1 { ...... }</span></span>
格式为:
<span style="font-size:18px;"><span style="font-size:18px;">public class 类 implements 接口1, 接口2, ... { ...... }</span></span>
注意: 在Java中,一个类可以不实现接口,也可只实现一个接口, 还可以实现多个接口.
注意: 当一个类实现了接口时,则这个类必须重写来自接口的所有抽象方法。否则,这个类就是抽象类。
编写一个类去实现给定的接口。同时,在这个类中重写来自接口的所有抽象方法。
最后,由此类去创建对象,调用对象的方法来解决业务。完成。
1) 导入 import 包.接口名; 如: import java.lang.Comparable;
2) 编写类去实现这个接口; 如: public class Student implementsComparable { ...... };
3) 在这个类中重写来自接口的抽象方法; 如: 重写 compareTo()方法;
4) 应用(编写应用程序)。
<span style="font-size:18px;"><span style="font-size:18px;">Demo //学生类 packagecom.hqg.oop.d39; //1导入 接口 importjava.lang.Comparable; importcom.hqg.oop.d38.a2.MyDate; public classStudent extends Object implementsComparable { //2 实现接口 //3 重写来自接口的所有抽象方法: 目的是让当前类的对象拥有比较大小的能力。 @Override public int compareTo(Object obj) { Student st2 = (Student)obj; if( this.score > st2.score ){ return 1; }else if( this.score <st2.score ){ return -1; }else{ return 0; } //return -(this.score - st2.score); /** * 1)基本类型的数据比较相等用 == , 比较大小 用关系运算符 * 2)引用类型的数据比较相等用 equals() 方法, 比较大小用 compareTo()方法。 * */ //return this.name.compareTo(st2.name); //return-(this.SEX+"").compareTo( st2.SEX+"" ); int x = (this.SEX+"").compareTo(st2.SEX+"" ); int y = -(this.score - st2.score); if( x == 0 ){ return y; }else{ return x; } String s1 =this.getBirthday().toString(); String s2 =st2.getBirthday().toString(); return s1.compareTo( s2 ); } //实例变量 private String name; private final boolean SEX; private MyDate birthday; private int score; //构造器 public Student() { int n = (int)(2 * Math.random()); this.SEX = n == 1; } public Student(String name, MyDatebirthday, int score) { this(); this.name = name; this.birthday = birthday; this.score = score; } public Student(String name, boolean sEX,MyDate birthday, int score) { this.name = name; SEX = sEX; this.birthday = birthday; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday){ this.birthday = birthday; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public boolean isSEX() { return SEX; } @Override public String toString() { return " " + name +" " + (SEX ? "男":"女") + " " + birthday + " "+ score; } } package com.hqg.oop.d39; //importjava.util.Arrays; //专家给定的工具类, 用来实现 数组的排序 importcom.hqg.oop.d38.a2.MyDate; public classQuTest5 { public static void main(String[] args) { Student[] ss = new Student[5]; ss[0] = new Student("张无忌", new MyDate(1992, 10, 11), 650); ss[1] = new Student("赵敏", false, new MyDate(1992, 5, 15),680); ss[2] = new Student("韦一笑", new MyDate(1993, 1, 31), 670); ss[3] = new Student(“蛛儿”, true, new MyDate(1992, 9, 10), 660); ss[4] = new Student("周芷若", false, new MyDate(1993, 2, 7),700); System.out.println("五个学生的初始状态如下:"); System.out.println("============================"); System.out.println("姓名 性别 出生日期 入学成绩"); System.out.println("============================"); for(Student tem : ss){ System.out.println( tem ); System.out.println("----------------------------"); } //业务1 :所有学生按入学成绩升序排列 Arrays.sort( ss ); System.out.println("\n\n五个学生按入学成绩升序如下:"); System.out.println("============================"); System.out.println("姓名 性别 出生日期 入学成绩"); System.out.println("============================"); for(Student tem : ss){ System.out.println( tem ); System.out.println("----------------------------"); } //业务2: 入学成绩降序排列 //将 compareTo()方法中的值取反即可降序 //业务3: 按姓名排序 //将 compareTo()方法中的返回值改变姓名比较后的值。 //业务4: 性别分类 //布尔类型不能比较大小,就将其转换为其它类型数据再比较。 //业务5: //复合条件排序 //业务6: 按出生日期排序 } } class Arrays { public static void sort( Student[] stu ){ for(int i = 0; i <stu.length-1; i++){ for(int j = 0; j <stu.length - i - 1; j++ ){ int re =stu[j].compareTo( stu[j+1] ); //两个学生比较大小,结果存放在re中 if( re > 0 ){ //交换 Student tem =stu[j]; stu[j] =stu[j+1]; stu[j+1] =tem; } } } } }</span></span>
所谓抽象类是用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象;所谓接口,相当于电源插座,可插入构件相当于电器。可插入构件的关键在于存在一个公用的接口,以及每个构件都实现了这个接口。接口是实现构件的可插入性的关键。
(1)从语法层来讲,Java语言对于抽象类和接口给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。
Demo抽象类的定义方式如下:
<span style="font-size:18px;"><span style="font-size:18px;">abstract classDemo { abstract voidmethod1(); abstract void method2(); … }</span></span>
Demo的接口定义方式如下:
<span style="font-size:18px;"><span style="font-size:18px;">interface Demo { void method1(); void method2(); … }</span></span>
在抽象类的定义中,Demo可以有自己的数据成员,也可以有非abstract的成员方法,而在接口的定义中,Demo只能够有static final数据成员,所有的成员方法都是abstract的。从某种意义上说,接口是一种特殊形式的抽象类。
从编程的角度来看,首先,抽象类和接口的继承规则不同,抽象只允许单继承,而一个类却可以实现多个接口。接口对于多重继承的支持方面的一种折中考虑;其次,在抽象类的定义中,可以赋予方法的默认行为,而在接口的定义中,方法不能拥有默认行为,必须使用委托,从某种意义上来说,接口比抽象类更为抽象化。
(2)从设计层来讲,抽象类在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"Is-A"关系,即父类和子类在概念本质上应该是相同的。对于接口来说则不然,接口并不要求实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已。考虑这样一个例子,假设在有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过抽象类或者接口来定义一个表示该抽象概念的类型,定义方式分别如下所示:
Door抽象类的定义方式如下:
<span style="font-size:18px;"><span style="font-size:18px;">abstract classDoor { abstract voidopen(); abstract voidclose(); }</span></span>
Door的接口定义方式如下:
<span style="font-size:18px;"><span style="font-size:18px;">interface Door { void open(); void close(); }</span></span>
其他具体的Door类型可以extends使用抽象类方式定义的Door或者implements使用接口方式定义的Door。看起来好像使用抽象类和接口没有大的区别。如果现在要求Door还要具有报警的功能。下面将罗列出可能的解决方案,并从设计层对方案进行分析。
解决方案一:
简单的在Door的定义中增加一个alarm方法,如下:
<span style="font-size:18px;"><span style="font-size:18px;">abstract classDoor { abstract voidopen(); abstract voidclose(); abstract voidalarm(); }</span></span>
或者
<span style="font-size:18px;"><span style="font-size:18px;">interface Door { void open(); void close(); void alarm(); }</span></span>
那么具有报警功能的AlarmDoor的定义方式如下:
<span style="font-size:18px;"><span style="font-size:18px;">class AlarmDoorextends Door { void open(){…} void close(){…} void alarm(){…} }</span></span>
或者
<span style="font-size:18px;"><span style="font-size:18px;">class AlarmDoorimplements Door{ void open(){…} void close(){…} void alarm(){…} }</span></span>
这种方法违反了接口隔离原则,在Door的定义中把Door概念本身固有的行为方法和另外一个概念“报警器”的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为“报警器”这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。
解决方案二:
既然open、close和alarm属于两个不同的概念,根据接口隔离原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用抽象类方式定义;两个概念都使用接口方式定义;一个概念使用抽象类方式定义,另一个概念使用接口方式定义。
显然,由于Java语言不支持多重继承,所以两个概念都使用抽象类方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。
如果两个概念都使用接口方式来定义,那么就反映出两个问题:第一,我们可能没有理解清楚问题领域,AlarmDoor在概念本质上 到底是Door还是报警器?第二,如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用接口方式定义)反映不出上述含义。
如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,抽象类在Java语言中表示一种继承关系,而继承关系在本质上是"Is-A"关系。所以对于Door这个概念,我们应该使用抽象类方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过接口方式定义。如下所示:
<span style="font-size:18px;"><span style="font-size:18px;">abstract classDoor{ abstract voidopen(); abstract voidclose(); } interface Alarm{ void alarm(); } class AlarmDoorextends Door implements Alarm{ void open(){…} void close(){…} void alarm(){…} }</span></span>
这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是"Is-A"关系,interface表示的是"Has-A"关系,在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有 Door的功能,那么上述的定义方式就要反过来了。
抽象类和接口是Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系。只有正确理解面向对象的设计原则并灵活使用抽象类和接口,才能设计出易用的系统。
接口的出现大大的方便了我们变编程人员的工作,多继承现象也可以顺利解决。其实,接口孕育于生活,稍一体会,就感觉到生活中接口的重要性呢,于编程亦如此。