接口
接口(interface)是Java所提供的另一种重要技术,是一种特殊的类,它的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有不同,并且Java 8中又添加了新特性。
1、接口的定义与使用
(1)接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误)。接口里的数据成员必须初始化,常见的是全局变量。
(2)接口中的方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误)。也就是说,接口不能像抽象类一样定义一般的方法,需定义“抽象方法”。
(3) 另,Java8中为避免在接口中添加新方法后要修改所有实现类,允许定义默认方法,即default方法,也可以称为Defender方法,或者虚拟扩展方法(Virtual extension methods)。
Default方法是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。
(4)接口中不能含有静态代码块以及静态方法(抽象类中可以有静态代码块和静态方法)。
(5)在Java中使用interface关键字来定义一个接口。
接口定义的语法如下:
interface 接口名称 {
final 数据类型 成员名 = 常量; // 数据成员必须赋初值
abstract 返回值的数据类型 方法名(参数...); // 抽象方法
default 返回值的数据类型 方法名(参数...) { // 默认方法,包含方法体
// ... 方法体
}
}
定义一个接口实例:
interface A {
public static final String INFP = "Hello,world!"; // 全局常量
public abstract void print(); // 抽象方法
}
带默认方法的接口定义实例:
interface A {
public static final String INFP = "Hello,world!"; // 全局常量
public abstract void print(); // 抽象方法
default public void otherPrint(){
System.out.println("default methods!");
}
}
虽然有了接口,可是定义的接口A和接口B因里面存在抽象方法,都不能被用户直接使用。
(6)接口必须有子类,子类依靠implements关键字可以同时实现多个接口。
(7)接口的子类(如果不是抽象类)则必须覆写接口之中的全部抽象方法。
(8)接口可以利用对象多态性,利用子类实现对象的实例化。
(9)接口与一般类一样,本身也具有数据成员与方法,但数据成员一定要赋初值,且此值不能再更改,方法也必须是“抽象方法”或default方法。
也正因为接口中的方法除default方法外必须是抽象方法,而没有一般的方法,所以接口定义格式中,抽象方法声明的关键字abstract是可以省略的。
同理,接口的数据成员身上,因数据成员必须赋初值,且此值不能再被更改,所以声明数据成员的关键字final也可省略。
例:
interface A {
public static String INFP = "Hello,world!"; // 全局常量,省略final
public void print(); // 抽象方法,省略abstract
default public void otherPrint(){
System.out.println("default methods!");
}
}
(10)在Java中接口是用于实现多继承的一种机制,也是Java设计中最重要的一个环节,每一个由接口实现的类必须在类内部覆写接口中的抽象方法,且可自由地使用接口中的常量。
既然接口里只有抽象方法,它只需声明而不用定义处理方式,于是自然可以联想到接口没有办法像一般类一样,再用它来创建对象。利用接口创建新类的过程,称之为接口的实现(implementation)。
接口实现的语法:
class 类名 implements 接口A,接口B... {
...
}
带default方法接口的实现:
interface InterfaceA {
public static String INFO = "static final"; // 全局常量,省略final
public void print(); // 抽象方法,省略abstract
default public void otherPrint() {
System.out.print("Print default1 method InterfaceA!");
}
}
class InterfaceAB implements InterfaceA {
@Override
public void print() {
System.out.print("Print abstract methods InterfaceA!");
System.out.print(INFO);
}
}
public class InterfaceDefault {
public static void main(String[] args) {
InterfaceAB ab = new InterfaceAB();
ab.print(); // 调用覆写过的抽象方法
ab.otherPrint(); // 调用接口中的默认方法
System.out.print(InterfaceA.INFO); // 输出接口中的常量
}
}
【结果】
(11)Java 8 中允许接口中只定义默认方法,无抽象方法。
interface InterfaceA {
default public void otherPrint() {
System.out.print("Print default1 method InterfaceA!");
}
}
class InterfaceAB implements InterfaceA {}
public class InterfaceDefault {
public static void main(String[] args) {
InterfaceAB ab = new InterfaceAB();
ab.otherPrint(); // 调用接口中的默认方法
}
}
【结果】
上例中定义了仅有一个默认方法的接口,无抽象方法,继承接口的子类因不需覆写抽象方法,内容为空。
接口与抽象类相比,最大的区别就在于子类上,子类可以同时实现多个接口。
例:
interface A {
public static String INFO = "Hello,world!";
public void print();
}
interface B {
public void get();
}
class X implements A,B {
public void print() {
System.out.println(INFO);
}
public void get() {
System.out.println("你好!");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
X x = new X(); // 实例化子类对象
A a = x; // 为父接口实例化
B b = x; // 为父接口实例化
a.print();
b.get();
}
}
【结果】
(12)一个类实现多个接口时,若接口中有默认方法,不能出现同名默认方法。
但在Java8中,如果一个类实现两个或多个接口,即多继承,但是若其中两个接口中都包含一个名字相同的default方法,如下例中的InterfaceA,InterfaceB,有同名的默认方法DefaultMethod(),但方法体不同。
interface A {
public void someMethod();
default public void defaultMethod() {
System.out.println("Default method implementation in the interface A");
}
}
interface B {
default public void defaultMethod() {
System.out.println("Default method implementation in the interface B");
}
}
class X implements A,B {
public void someMethod() {
System.out.println("Some methods implementation in the class!");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
X x = new X(); // 实例化子类对象
x.someMethod();
x.defaultMethod(); // 错误,
}
}
【结果】
如果编译以上的代码,编译器会报错,因为编译器不知道应该在两个同名的default方法中选择哪一个,因此产生了二义性。
(13)多继承中,如果说在一个子类即要实现接口又要继承抽象类,则应该采用先继承后实现的顺序完成。
interface A {
String INFO = "Hello,world!";
public void print();
}
interface B {
public void get();
}
abstract class C {
public abstract void fun();
}
class X extends C implements A,B { // 先继承,后实现
public void print() {
System.out.println(INFO);
}
public void get() {
System.out.println("你好!");
}
public void fun() {
System.out.println("你好!JAVA");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
X x = new X(); // 实例化子类对象
A a = x; // 为父接口实例化
B b = x; // 为父接口实例化
C c = x; // 为抽象类实例化
a.print();
b.get();
c.fun();
}
}
【结果】
(14)接口使用过程中,一个抽象类可以继承多个接口,但是反过来讲,一个接口却不能够继承抽象类,但一个接口却可以使用extends关键字继承多个接口,也可以同时继承多个接口的抽象方法与常量。
interface A {
String INFO = "Hello,world!";
public void print();
}
interface B {
public void get();
}
abstract class C implements A,B {
public abstract void fun();
}
interface D extends A,B { // 同时继承两个接口
public void printD();
}
class X extends C implements D { // 先继承,后实现
public void print() {
System.out.println(INFO);
}
public void get() {
System.out.println("你好!");
}
public void fun() {
System.out.println("抽象类C实现接口A,B!");
}
public void printD() {
System.out.println("抽象类D继承两个接口A,B!");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
X x = new X(); // 实例化子类对象
A a = x; // 为父接口实例化
B b = x; // 为父接口实例化
C c = x; // 为抽象类实例化
D d = x; // 为父接口实例化
a.print();
b.get();
c.fun();
d.printD();
}
}
【结果】