在之前的博客中,我有写到接口,但是还有一些语法没有总结到。本篇博客旨在于将接口拎出来单独总结。
一、接口
接口从某种意义上来说,就是完完全全的抽象类。
接口:由 interface 实现,如:
interface IA { }
1. 接口的语法规则
① 接口中的普通方法,不能有具体实现,如果非要实现,需要被 default 修饰这个方法
② 接口中的所有方法的限定符都是 public,那么所有的方法都可以省略 public,而接口中的所有抽象方法的限定符都是 abstract public,那么所有的抽象方法同样可以省略 abstract public
③ 接口中的成员变量默认是被 public static final 修饰的,也就是说,接口中的成员变量必须被初始化
④ 接口不可以通过 new 实例化对象
⑤ 如果一个类实现了一个接口,那么在这个类中,必须重写接口中所有的抽象方法,而且重写的抽象方法必须被 public 修饰(因为接口中的抽象方法默认是 public abstract)
⑥ 如果一个类 A 实现了接口 B,那么对应的代码格式为:
interface IB{ } class A implements IB{ }
⑦ 一个类可以实现多个接口,然而一个类只能继承一个类
interface IX{ } interface IY{ } class Z{ } class A extends Z implements IX,IY{ }
⑧ 两个接口之间使用 extends,表示一个接口拓展另一个接口。
注意下面代码,当类 A 实现了接口 IY ,类 A 必须重写两个方法。
interface IX{ void func1(); } interface IY extends IX{ void func2(); } class A implements IY{ @Override public void func2() { } @Override public void func1() { } }
2. 通过代码来演示接口的语法
在程序清单1中,我通过注释标明了接口使用时的一些语法规则,这十分重要!
程序清单1:
interface IShape{ public int a; //error public static final int b = 10; //right int c = 20; //right public void func1(){ //error } default public void func2(){ //right } public static void write(){ //right } abstract public void draw1(){ //error } abstract public void draw2(); //right void draw3(); //right } public class Test { public static void main(String[] args) { IShape iShape = new IShape(); //error } }
3. 继承与接口的综合
程序清单2:
class Animal{ String name; public Animal(String name) { this.name = name; } public void eat(){ System.out.println(name + " 正在吃东西"); } } interface IFlying{ void fly(); } class Bird extends Animal implements IFlying{ public Bird(String name) { super(name); } //重写了接口中的方法 @Override public void fly() { System.out.println(name + " 正在飞"); } //重写了父类中的方法 @Override public void eat() { System.out.println(name + " 正在喝蜂蜜"); } } public class Test { public static void fly(IFlying iFlying){ iFlying.fly(); } public static void main(String[] args) { fly(new Bird("老鹰")); new Bird("蜜蜂").eat(); } }
输出结果:
对程序清单2 进行分析:
在程序清单2 中,我们演示了继承和接口,有几个语法点很关键:
① 在父类构造带参数的方法的同时,子类也需要构造同样的带参数方法。
public Bird(String name) { }
② 在父类已有的普通成员方法情况下,子类却重写了父类的此方法,那么在编译时,系统直接使用子类重写的方法。
public void eat(){ }
③ 接口中的抽象方法是被 abstract public 修饰的,那么在一个类实现接口的时候,我们就必须对此抽象方法进行重写。
@Override public void fly() { System.out.println(name + " 正在飞"); }
二、为什么有些代码 new 了一个接口
演示1
通常情况下,一个类实现了另一个接口,我们只能使用 implements 显示地表现出来,如下面的程序:
程序清单3:
interface IA{ void test(); } class B implements IA { @Override public void test() { System.out.println("我通过类 B 重写了接口的 test 方法"); } } public class Test { public static void main(String[] args) { B b = new B(); b.test(); } }
输出结果:
演示2
而我们知道接口是不能通过 new 来实例化对象的,但有时候我们也会看到【 new 接口 】的情况出现,但不会被编译器报错。因为这是一个匿名内部类实现接口的缘故,只是这个匿名类看起来被隐藏了一样,给人一种 new 了一个接口的错觉,而实际上匿名内部类实现了另一个接口,那么它依然需要重写接口中的抽象方法。所以,一个接口依然不能通过 new 实例化对象!
//语法格式 new + 接口名 + { //这里必须重写接口中的抽象方法! };
通过下列程序来进行演示为什么可以这么做:
程序清单4:
interface IA{ void test(); } public class Test { public static void main(String[] args) { IA ia = new IA(){ //1 @Override public void test() { //2 System.out.println("我匿名重写了接口的 test 方法"); } }; ia.test();//3 } } //注释 //1. 匿名类实现了接口 IA //2. 匿名类重写了 test 方法 //3. 匿名类调用了 test 方法
输出结果:
演示3
程序清单4 又可以简化成程序清单5
程序清单5:
interface IA{ void test(); } public class Test1 { public static void main(String[] args) { new IA(){ //1 @Override public void test() { //2 System.out.println("我匿名重写了接口的 test 方法"); } }.test(); //3 } } //注释 //1. 匿名类实现了接口 IA //2. 匿名类重写了 test 方法 //3. 匿名类调用了 test 方法
输出结果:
演示4
而程序清单5 又可以写成 Lambda 表达式
( (IA) () -> { System.out.println("我匿名重写了接口的 test 方法");} ).test();