定义:Java接口(英文名:interface)是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口更像是一个模具,它提供应该有的样子,而具体的内容(方法)实现,需要继承接口的类去实现。有了接口就可以让一个类去实现多个接口,具有多个接口的特征,填补了java继承只能有一个父类的弊端。
1、接口的创建
这里先举一个简单的创建接口的形式:
package cn.edu.ncu.inter;
public interface Human {
int family = 1; //隐含是public static final,可以不写出来
void eat(); //隐含是public abstract ,可以不写出来
void sleep();
void pee();
}
语法格式:
[可见度] interface 接口名称 [extends 其他的类名] {
// 声明变量
// 抽象方法
}
2、接口中属性和方法的限制
可以看到,在创建接口时,对于修饰符有严格的要求,可以不写出来,但是写就不能有错误
对于接口的属性:
- 修饰符只能是public static final
- 属性必须赋初值,否则会报错
对于接口的方法:
- 修饰符只能是public abstract
对于接口:
- 修饰符只能是public
出现接口的初衷,就是为了提供一系列的方法的特征,去让类实现,属性在接口中无太大意义,所以它的修饰符只能是public static final,只读且只有一份。
3、接口的实现
类继承接口,则必须将接口中的所有方法实现,否则会报错
package cn.edu.ncu.inter;
public class Chinese implements Human { //Human接口定义请看 1、接口的创建
@Override
public void eat() {
System.out.println("We Chinese eat");
}
@Override
public void sleep() {
System.out.println("We Chinese sleep");
}
@Override
public void pee() {
System.out.println("We Chinese pee");
}
public static void main(String... args){
Chinese cn1 = new Chinese();
cn1.sleep(); //We Chinese sleep
}
}
其中方法上的“@Override”表示检测是否以实现接口中的方法,如果未实现,会出现编译报错。
语法格式:
可见度 class 类名 implements 要实现的接口(可以多个,逗号隔开) {
所有要实现的接口中的方法
···
···
···
}
注意:
-
尽管public修饰符在接口中可以省略掉,但是在实现这个接口的类中,实现的方法必须使用public修饰,且不可省略
-
在Java 8中,可以使用关键字default修饰接口方法,但是该方法必须有方法体。在实现该接口时,类可以直接使用该方法,不需要再去实现,也可以去重写该方法。
在Asian接口中添加一个默认方法(Japan类和Asian的出处可移步“4、接口的继承”)
default void learn(){ System.out.println("We study very hard."); }
在Japan类中添加主方法创建Japen实例,直接使用该方法
public static void main(String... args){ Japan japanese = new Japan(); japanese.learn(); //We study very hard. }
-
在Java 8中,允许接口存在公有的静态方法,接口中的静态方法和类中的公有静态方法一样使用
-
使用instanceof 检查类型,可以发现japanese实例也属于Comparable
System.out.println(japanese instanceof Comparable); //ture
4、接口的继承
接口的继承类似于类的继承,同样使用“extends”关键字,但是在接口的继承中允许一个接口继承多个类。如果一个接口继承了另一个接口,那么实现这个接口的类,就必须将这两个接口中的所有方法实现。
package cn.edu.ncu.inter;
public interface Asian extends Human{ //Human接口定义请看 1、接口的创建
void shit();
void asianSquats();
}
实现Asian接口的Japan类
package cn.edu.ncu.inter;
public class Japan implements Asian{
@Override
public void eat() {
System.out.println("we Japan eat");
}
@Override
public void sleep() {
System.out.println("we Japan sleep");
}
@Override
public void pee() {
System.out.println("we Japan pee");
}
@Override
public void shit() {
System.out.println("we Japan shit");
}
@Override
public void asianSquats() {
System.out.println("we Japan asianSquats");
}
}
5、Java API中两个重要的接口——Comparable接口
Comparable接口用于实现对象的可比较性,在java.lang包中,有着Comparable接口的定义
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
- 这里的T表示泛型,可以被替换成一种具体的类型
- Comparable接口已经被Byte、Short、Integer、Long、Float、Double、Character、BigInteger、BigDecimal、Calendar、String以及Date实现了,小于返回-1,等于返回0,大于返回1
关于实现Comparable接口的一个例子
package cn.edu.ncu.inter;
public class Chinese implements Human,Comparable<Chinese> { //Human接口定义请看 1、接口的创建
private int age;
private String sex;
public Chinese(int age, String sex) {
this.age = age;
this.sex = sex;
}
public int getAge() {
return age;
}
public String getSex() {
return sex;
}
@Override
public void eat() {
System.out.println("we Chinese eat");
}
@Override
public void sleep() {
System.out.println("we Chinese sleep");
}
@Override
public void pee() {
System.out.println("we Chinese pee");
}
@Override
public int compareTo(Chinese o) {
if (getAge()>o.getAge())
return 1;
else if(getAge()<o.getAge()){
return -1;
}
else
return 0;
}
}
创建Chinese的实例,使用compareTo方法
package cn.edu.ncu.inter;
public class Test {
public static void main(String... args){
Chinese chinese1 = new Chinese(22,"男");
Chinese chinese2 = new Chinese(12,"女");
System.out.println(chinese2.compareTo(chinese1)); //-1
}
}
注意:
- 在实现Comparable接口时,“implement Compareable<Chinese>”,必须要将泛型替换成相应的要比较的类
- 在Chinese对compareTo方法实现中,将自己实例的age与另一Chinese类的实例的age进行比较,自己可以使用this.getAge(),也可以省略this,直接写getAge
java.util.Arrays.sort(Object[ ])方法
定义的java.util.Arrays.sort(Object[])方法中,利用到了对象的compareTo方法,进而来对对象进行排序
java源码中sort方法的定义:
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
利用sort方法的一个例子
package cn.edu.ncu.inter;
public class Test {
public static void main(String... args){
Chinese chinese1 = new Chinese(22,"男");
Chinese chinese2 = new Chinese(12,"女");
Chinese chinese3 = new Chinese(43,"女");
Chinese chinese4 = new Chinese(2,"女");
Chinese chinese5 = new Chinese(12,"女");
Chinese[] chinese = {chinese1, chinese2,chinese3, chinese4,chinese5};
java.util.Arrays.sort(chinese);
for (Chinese cn:chinese
) {
System.out.println(cn);
}
}
}
输出:
Object类中也有一个比较的方法equals,为了防止两者不冲突,对于两个对象o1和o2,应该确保当且仅当o1.equals(o2)为true时o1.compareTo(o2)==0成立,为此可以重写equals方法
6、Java API中两个重要的接口——Cloneable接口
Cloneable接口是一个特殊的接口,是一个空的接口,叫做标记接口(方法体为空的接口),表示该对象可以通过clone方法实现复制。虽然为空,但是Object类中的clone()方法利用这一标记,实现了可克隆。
Cloneable接口的定义
public interface Cloneable {
}
Java库中的很多类(例如,Date、Calendar和ArrayList)实现了Cloneable。这样这些类的实例可以被克隆。例如,下面的代码
package cn.edu.ncu.inter;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class Test {
public static void main(String... args){
Calendar calendar = new GregorianCalendar(2013,2,1);
Calendar calendar1 = calendar;
Calendar calendar2 = (Calendar) calendar.clone();
System.out.println("calendar == calendar1 is "+
(calendar == calendar1));
System.out.println("calendar == calendar2 is "+
(calendar == calendar2));
System.out.println("calendar.equals(calendar2) is "+
calendar.equals(calendar2));
}
}
输出结果
calendar 和 calendar1指向的是同一个对象,而使用clone()后,另外开辟了一块新的空间,复制calendar指向的对象,该对象由calendar2引用。
特别说明的是,类似于ArrayList的集合框架也是可以复制的。
实现Cloneable接口的自定义类
在自定义类中,也要实现Cloneable接口,需要重写Object中的clone方法,一般固定为如下写法
@Override
public Object clone(){
try{
return super.clone();
}
catch (CloneNotSupportedException ex){
return null
}
}
Object中的clone方法定义的方法头如下:
protected native Object clone() throws ClonesNotSupportedException;
说明:
-
实现Cloneable接口的自定义类,只有将protected改为public,才能让该方法在任何一个包中都可以使用
-
自定义类,克隆的对象实例中,对于数据域是基本数据类型,复制的是它的值,而如果数据域是对象,复制的就是该域的引用。
如下图,有两个对象实例,house1和house2(被clone之后的House),whenBuilt是Date类,只能复制该域的引用,默认的clone方法克隆的叫做浅复制,定制的clone方法可以实现深复制
为实现深复制,可以将clone()方法重写为如下形式
public Object clone() throws CloneNotSupportedException{
//Perform a shallow copy
House houseClone = (House)super.clone();
//Deep copy on whenBuilt
houseClone.whenBuilt = (java.util.Date)(whenBuilt.clone());
return houseClone;
}
或者
public Object clone(){
try{
//Perform a shallow copy
House houseClone = (House)super.clone();
//Deep copy on whenBuilt
houseClone.whenBuilt = (java.util.Date)(whenBuilt.clone());
return houseClone;
}
catch (CloneNotSupportedException ex){
return null;
}
}