接口的概念
在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口
电源插座
电脑的USB口上,可以插:U盘、鼠标、键盘...所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备
通过上述例子可以看出:
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口
public interface 接口名称{ // 抽象方法 public abstract void method1(); // public abstract 是固定搭配,可以不写 public void method2(); abstract void method3(); void method4(); // 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁 }
提示
1. 创建接口时, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 "形容词" 词性的单词.
3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性
接口使用
接下来我们来看一看接口的简单实现
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法
public class 类名称 implements 接口名称{ // ... }
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
示例
实现笔记本电脑使用USB鼠标、USB键盘的例子
1. USB接口:包含打开设备、关闭设备功能
2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
3. 鼠标类:实现USB接口,并具备点击功能
4. 键盘类:实现USB接口,并具备输入功能
首先我们定义一个抽象类,里面包含打开和关闭的方法,也就是实现USB接口
// USB接口 public interface USB { void openDevice(); void closeDevice(); }
接下来我们实现鼠标类,实现USB接口;
1、我们需要多接口内的方法进行重写,变成所对应类的方法
2、实现该类特有的方法
public class Mouse implements USB { @Override public void openDevice() { System.out.println("打开鼠标"); } @Override public void closeDevice() { System.out.println("关闭鼠标"); } public void click(){ System.out.println("鼠标点击"); } }
然后实现键盘类,实现USB接口;
1、我们依旧需要多接口内的方法进行重写,变成所对应类的方法
2、实现该类特有的方法
public class KeyBoard implements USB { @Override public void openDevice() { System.out.println("打开键盘"); } @Override public void closeDevice() { System.out.println("关闭键盘"); } public void inPut(){ System.out.println("键盘输入"); } }
我们的键盘和鼠标都要依赖笔记本电脑来使用,所以接下来我们写
笔记本类:使用USB设备
public class Computer { public void powerOn(){ System.out.println("打开笔记本电脑"); } public void powerOff(){ System.out.println("关闭笔记本电脑"); } public void useDevice(USB usb){ usb.openDevice(); if(usb instanceof Mouse) { Mouse mouse = (Mouse)usb; mouse.click();//向下转型,调用鼠标类独有的方法 }else if(usb instanceof KeyBoard) { KeyBoard keyBoard = (KeyBoard)usb; keyBoard.inPut();//向下转型,调用键盘独有的方法 } usb.closeDevice(); } }
这里又有一个关键字instanceof
instanceof是Java中的二元运算符,用于测试左边对象是否是右边类的实例,返回boolean的数据类型。
这里用于防止向下转型的不安全性
最后我们书写一个测试类,实现整个流程
public class TestUsb { public static void main(String[] args) { Computer computer = new Computer(); // 打开电脑 computer.powerOn(); // 使用鼠标设备 computer.useDevice(new Mouse()); // 使用键盘设备 computer.useDevice(new KeyBoard()); // 关闭电脑 computer.powerOff(); }
执行结果如下
以上就是接口的一个简单实现,接下来我们看看接口具有哪些特性吧
接口特性
1. 接口类型是一种引用类型,但是不能直接new接口的对象
public class TestUSB { public static void main(String[] args) { USB usb = new USB(); } } // Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化
2. 接口中每一个方法都是public的抽象方法
即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错
public interface USB { // Error:(4, 18) java: 此处不允许使用修饰符private private void openDevice(); void closeDevice(); }
3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
public interface USB { void openDevice(); // 编译失败:因为接口中的方式默认为抽象方法 // Error:(5, 23) java: 接口抽象方法不能带有主体 void closeDevice(){ System.out.println("关闭USB设备"); } }
4. 重写接口中方法时,不能使用默认的访问权限
public interface USB { void openDevice(); // 默认是public的 void closeDevice(); // 默认是public的 } public class Mouse implements USB { @Override void openDevice() { System.out.println("打开鼠标"); } // ... } // 编译报错,重写USB中openDevice方法时,不能使用默认修饰符 // 正在尝试分配更低的访问权限; 以前为public
5. 接口中可以含有变量
但是接口中的变量会被隐式的指定为 public static final 变量
public interface USB { double brand = 3.0; // 默认被:final public static修饰 void openDevice(); void closeDevice(); } public class TestUSB { public static void main(String[] args) { System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的 // 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值 USB.brand = 2.0; // 说明brand具有final属性 } }
6. 接口中不能有静态代码块和构造方法
public interface USB { // 编译失败 public USB(){ } // 编译失败 { } void openDevice(); void closeDevice(); }
7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
8. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
9. jdk8中:接口中还可以包含default方法。
实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过类来表示一组动物
class Animal { protected String name; public Animal(String name) { this.name = name; } }
另外我们再提供一组接口, 分别表示 "会飞的", "会跑的", "会游泳的"
interface IFlying { void fly(); } interface IRunning { void run(); } interface ISwimming { void swim(); }
接下来我们创建几个具体的动物
猫, 是会跑的
class Cat extends Animal implements IRunning { public Cat(String name) { super(name); } @Override public void run() { System.out.println(this.name + "正在用四条腿跑"); } }
鱼, 是会游的
class Fish extends Animal implements ISwimming { public Fish(String name) { super(name); } @Override public void swim() { System.out.println(this.name + "正在用尾巴游泳"); } }
青蛙, 既能跑, 又能游(两栖动物)
class Frog extends Animal implements IRunning, ISwimming { public Frog(String name) { super(name); } @Override public void run() { System.out.println(this.name + "正在往前跳"); } @Override public void swim() { System.out.println(this.name + "正在蹬腿游泳"); } }
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类
在博主所使用的IDEA上,可以使用Ctrl+i实现快速接口
还有一种神奇的动物, 水陆空三栖, 叫做 "鸭子"
class Duck extends Animal implements IRunning, ISwimming, IFlying { public Duck(String name) { super(name); } @Override public void fly() { System.out.println(this.name + "正在用翅膀飞"); } @Override public void run() { System.out.println(this.name + "正在用两条腿跑"); } @Override public void swim() { System.out.println(this.name + "正在漂在水上"); } }
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性
猫是一种动物, 具有会跑的特性.
青蛙也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序⚪忘记类型.
有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
例如, 现在实现一个方法, 叫 "散步"
public static void walk(IRunning running) { System.out.println("我带着伙伴去散步"); running.run(); }
在这个 walk 方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行
Cat cat = new Cat("小猫"); walk(cat); Frog frog = new Frog("小青蛙"); walk(frog); // 执行结果 我带着伙伴去散步 小猫正在用四条腿跑 我带着伙伴去散步 小青蛙正在往前跳
甚至参数可以不是 "动物", 只要会跑!
class Robot implements IRunning { private String name; public Robot(String name) { this.name = name; } @Override public void run() { System.out.println(this.name + "正在用轮子跑"); } } Robot robot = new Robot("机器人"); walk(robot); // 执行结果 机器人正在用轮子跑