本文介绍了Java中接口的基本语法, 什么是接口, java中的接口 语法规则, 接口的使用,接口的特性,如何实现多个接口,接口间的继承,以及抽象类和接口的区别
Java接口的学习
- 一.接口的概念
- 二.Java中的接口
- 1.接口语法规则
- 2.接口的使用
- 3.接口的特性
- 4.实现多个接口
- 5.接口间的继承
- 三.抽象类和接口的区别(面试题)
一.接口的概念
在我们生活中,接口随处可见,比如:USB接口,电源接口…不同接口在形状上也存在不同,也就限制了只有和接口形状对应的连接设备才可以连接对应的接口,这也就是一种外在的标准限制,目的就是为了让满足设计标准的设备进行连接访问
如:鼠标键盘U盘等都可以通过电脑上提供的USB接口与电脑连接和计算机产生交互,
手机充电线可以插在电源接口再通过另外一端连接手机接口来对手机进行充电
但并不是一个接口能被所有设备连接访问,就好比USB接口和电源接口形状设计不同,也就是不满足对应标准规范的设备则不能使用该接口,
例如:电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲…所有符合规范的设备
鼠标键盘U盘这些设备,其本身不需要充电它们是用来和计算机构成连接的设备,插头是USB形状的,因此无法连接电源插口,因此不满足标准规范…
而设计成电源插口形状的一般都是满足了可以充电的标准规范,需要充电的设备才会设有连接电影插口的插头…
通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
二.Java中的接口
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
即当某个类满足这个接口标准规范,此类就能实现这个接口,具备接口提供的行为
1.接口语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。例如:
public interface IShape{ //interface 关键字用来表示接口 接口名一般以大写I开头 后面用大驼峰
//接口内的成员变量都是静态的常量
public String name1="1";
public static String name2="2";
public static final String name3="3"; //接口当中的成员默认是 public static final 修饰的 上面的没有写全 在编译时会自动补全!即三种写法等价于第三种
void func1(); // 接口中的成员方法都是抽象方法.不能有方法体
public abstract void func2();
//接口中的方法 默认是 public abstract 修饰 全为抽象方法没有具体方法体实现
//没有写全在 编译时也会自动补齐!即fun1和fun2是等价的
default void func3(){ // 被default修饰的为接口内的默认方法可以有方法体
System.out.println("接口内默认的方法");
//在java8中 要使接口中的方法有方法体 在写时不能加上abstract 并且前面要加上default! 此时方法内才能写方法体!
}
public static void func4(){//接口内的静态方法
System.out.println("接口中static修饰的静态方法");
//方法也可以被static修饰 表示为静态方法此时可以有方法体 通过类名调用 不会被重写
}
void draw();
//接口中不存在构造方法 接口中的变量都是被static final修饰 已经初始化了 根本不需要构造
//接口中不存在 任何代码块 因为不需要给静态 或非静态的成员变量初始化
}
注意:
- 创建接口时, 接口的命名一般以大写字母 I 开头.
- 接口的命名一般使用 “形容词” 词性的单词.
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性
如:成员变量-> 类型名 变量名 成员方法->返回类型 方法名();
编译器会自动将其转换为public static final 类型名 变量名 和public abstract 返回类型 方法名(); - 接口中可以存在静态方法,直接通过接口名来调用
- 在JDK1.8中,接口内不仅可以有抽象方法,静态方法,还可以出现有方法体的成员方法即默认方法,需要在方法名前加上default修饰,且前面不能出现abstract和static
2.接口的使用
根据上面接口语法规范来看,接口里本质上就是一些抽象的方法,和抽象类一样,它是不能直接实例化使用的,而需要搭配其他类来"实现"接口,重写接口内的所有抽象方法,
例如:
public class 类名称 implements 接口名称{
// ...
}
注意:
子类和父类之间是extends 继承关系 ,类和接口之间是implements 实现关系
Java中接口是一个标准行为规范,当类满足这个接口标准规范,即可实现这个接口,具备接口提供的行为,使类实例化后的对象能够具备接口提供的行为,
但是接口内提供的大多是抽象方法,实现接口的类需要重写接口里的抽象方法,即实现接口后具备了接口提供的行为但具体行为体现根据不同的类自己定义
上面提到USB接口,而满足USB接口这个标准规范的可以是鼠标类键盘类,电脑通过USB接口才能检测到鼠标键盘,鼠标键盘需要USB接口才能和电脑进行交互操作…
而在Java中电脑类和鼠标键盘类也可以设计通过USB接口实现交互的操作
示例:
public interface IUSB { //USB接口具备的行为
void openDevice(); //打开 功能
void closeDevice(); //关闭功能 抽象方法
}
public class KeyBoard implements IUSB{//键盘类实现USB接口 具备USB接口行为 对其行为进行具体重写
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() { //实现 USB接口 重写其打开关闭方法
System.out.println("关闭键盘");
}
public void inPut(){ //键盘自身方法
System.out.println("键盘输入");
}
}
public class Mouse implements IUSB{ //鼠标满足USB标准规范 实现USB接口 具备USB行为 对其抽象方法重写
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() { //实现接口 重写 接口的两个抽象方法 进行 打开关闭鼠标
System.out.println("关闭鼠标");
}
public void click(){ //自身行为 鼠标点击
System.out.println("鼠标点击");
}
}
public class Computer { //电脑类 电脑上有USB接口即可以用组合思想借助USB接口访问实现USB接口的类
public void open(){ //电脑本身行为
System.out.println("开机");
}
public void close(){
System.out.println("关机");
}
public void useDevice(IUSB usb){ //电脑行为 使用use设备 通过usb接口来访问连接usb的设备 多态思想
usb.openDevice(); // 传的是什么对象 调用的是对象对象的打开行为
if(usb instanceof Mouse){ // 用 instanceof 判断当前usb是由哪个类转型上来的 再转型回去 调用对应对象自身的 使用设备行为
Mouse mouse=(Mouse) usb;
mouse.click();
}else if(usb instanceof KeyBoard){
KeyBoard keyBoard=(KeyBoard) usb;
keyBoard.inPut();
}
usb.closeDevice(); // 通过usb接口 调用接受的不同对象的 关闭行为
}
//电脑里有USB接口 通过USB接口访问连接USB接口的对象 对象要实现USB接口才能被电脑接收并访问!
//不用关系usb引用的具体是哪个对象 只要实现了这个接口 传了这个对象过来 就能实现这个对象的行为 省略了代码书写融入了 设计思想高大上
}
public class Test {
public static void main(String[] args) {
Computer computer=new Computer();
computer.useDevice(new KeyBoard()); // 通过传不同 实现了USB接口的对象地址过去 使用USB引用变量接受 调用不同对象的行为
System.out.println("=============");
computer.useDevice(new Mouse());
}
}
USB接口内设计的抽象行为是打开关闭功能,键盘和鼠标类都满足这个标准规范具备打开关闭功能,
即可实现这个接口重写接口内提供的抽象方法,来达到打开关闭键盘鼠标的功能.
而电脑本身具备USB接口,根据组合关系,电脑对象行为里可以使用USB这个接口类的引用来接受实现了该接口的类对象(向上转型)
鼠标,键盘类都实现了USB接口,USB接口创建的引用变量即可接受鼠标和键盘对象,通过传递的不同对象,USB调用自身行为,执行不同对象的自身重写的方法(多态),再通过向下转型判断强转回为对应类型再执行自身的方法,
最后实现了电脑通过USB接口访问不同实现USB接口的对象的功能…
注意:
接口需要被类实现,而使用接口的引用变量也可以接收实现接口的类实例化的对象,达到向上转型,动态绑定,多态的效果
3.接口的特性
- 接口类型是一种引用类型,但是不能直接new接口的对象
public class TestUSB {
public static void main(String[] args) {
USB usb = new USB();
}
} // Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化
- 接口中每一个非静态方法都是public的抽象方法, 即接口中的方法不写修饰符会被隐式的指定为 public abstract(只能是public abstract,显示定义其他修饰符都会报错)
public interface USB {
// Error:(4, 18) java: 此处不允许使用修饰符private
private void openDevice();
void closeDevice();
}
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
public interface USB {
void openDevice();
// 编译失败:因为接口中的方式默认为抽象方法
// Error:(5, 23) java: 接口抽象方法不能带有主体
void closeDevice(){ //不能有具体方法实现
System.out.println("关闭USB设备");
}
}
- 重写接口中方法时,不能使用默认的访问权限
(即重写方法的访问权限一定要大于等于被重写方法的访问权限)
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属性
}
}
- 接口中不能有静态和非静态代码块和构造方法
public interface USB { //接口不能被实例化且自身的属性都是常量且不能修改的,即不存在构造方法和代码块对其初始化
// 编译失败 接口内不存在构造方法
public USB(){
}
{
} // 编译失败 不存在代码块
void openDevice();
void closeDevice();
}
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
接口不是类但属于引用数据类型,可以创建引用变量,可以发生多态
- 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
当实现接口的类是抽象类时,抽象类内可以实现接口的方法也可以不实现,因为其自身也不具备完整的信息来描述对象,被普通类实现时,一定要重写方法表示该对象具备此行为
interface IShape{
void draw();
}
abstract class Shape1 implements IShape{
//当类实现接口 不想重写接口方法时 该类必须设计为 抽象类 即其不能实例化 则此时不需要重写
}
- 接口中可以存在静态的方法但静态方法也必须是public修饰,如果没有显示定义也默认是public,其他的修饰则报错
interface IShape{
private static void func4(){
System.out.println("接口中static修饰的静态方法");
//方法也可以被static修饰但访问权限一定是public 表示为静态方法此时可以有方法体 通过类名调用 不会被重写
} //报错: Modifier 'private' not allowed here
}
- jdk8中:接口中还可以包含default方法(可以有方法体)。
interface IShape{
default void func3(){
System.out.println("接口内默认的方法"); //在java8中 要使接口中的方法有方法体 在写时不能加上abstract 并且前面要加上default! 此时方法内才能写方法体!
}
}
4.实现多个接口
Java中类和类之间只能存在单继承,即一个类只能有一个父类,但是一个类是可以实现多个接口的…
一个类实现多个接口也就是此类满足多个标准规范,具备多个接口的方法具体实现
示例:
abstract class Animal{ // 抽象动物类 用来被继承
String name;
int age;
abstract void eat();
}
//提供三个接口 分别具备 跑步 游泳 飞行的行为
interface IRunning{ //不同接口 不同的标准规范 里面的抽象行为被哪个类实现了就要被其重写 然后可以实现这个行为 即满足了这个标准规范则能做这个行为
void run(); //行为规范 跑 抽象方法
}
interface ISwimming{
void swim(); //游泳行为规范 实现该接口的必须重写这个抽象行为 能进行游泳
}
interface IFly{
void fly(); //飞行 抽象方法
}
上述代码提供了被继承的动物类,和一些动物可能具备的三个行为抽象成的接口即标准规范
class Dog extends Animal implements IRunning{ //狗 能跑 不能游泳 不能飞 只能实现跑行为的接口 一个标准行为规范
@Override
void eat() {
System.out.println(this.name+"正在吃狗粮");
}
Dog(){
this.name="小狗";
}
@Override
public void run() { //重写IRunning接口内的run方法
System.out.println(this.name+"正在用四条腿跑");
}
}
class Bird extends Animal implements IRunning,IFly{ //鸟 能飞 能跑 不能游泳 //满足两个行为标准规范 实现两个接口 重写其方法
@Override
void eat() {
System.out.println(this.name+"正在吃鸟粮");
}
Bird(){
this.name="小鸟";
}
@Override
public void fly() {//重写 IFLY接口内的抽象方法
System.out.println(this.name+"正在天上飞");
}
@Override
public void run() {//重写IRunning接口内的run方法
System.out.println(this.name+"正在用两只脚跑");
}
}
class Goose extends Animal implements IRunning ,ISwimming,IFly{ //鹅是一种三栖动物 它能跑能飞能游泳海陆空 实现多个接口 完成多个特殊的行为
@Override
void eat() {
System.out.println(this.name+"正在"+"吃鹅食");
}
Goose(){
this.name="大白鹅";
}
@Override
public void run() { //重写IRunning接口内的run方法
System.out.println(this.name+"正在用两只脚跑");
}
@Override
public void swim() {//重写ISWimming接口内的swim方法
System.out.println(this.name+"正在水上游");
}
@Override
public void fly() {//重写IFly接口内的fly方法
System.out.println(this.name+"正在飞");
}
实现多个接口需要implements关键字后跟接口名 多个接口间用逗号隔开
一个类可以继承一个父类同时实现一个或者多个接口,但是语法限制要先继承父类再实现接口 即extends 要在implements前面
狗继承动物类,具备动物的属性和行为,其自身也可以跑,即满足跑这个标准规范可以实现IRunning接口重写run抽象方法具备跑的行为
而鸟也是动物,同时具备跑和飞两个行为满足两个标准规范可以实现IRunning和IFly两个接口,
鹅是三栖动物,可以跑可以飞可以游泳,三个标准规范都满足,可以同时实现IRunning IFly ISwimming 三个接口,具备三种行为
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.
通过多实现可以使一个对象具有多个不同接口的行为,即可以同时满足多个标准行为规范
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性
即狗鸟鹅是动物 ,狗具有跑特性 鸟具有跑和飞特性 鹅具有跑飞游泳特性
这样设计有什么好处呢?
时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
也就是说,多个类并不需要它们有任何关系,只要其具备某些相似的行为,即可以抽象出一个标准规范的接口,
这些类实现这个接口即具有这个特性,这种设计在后续出现其它类时也不用考虑类与类之间的关系,只需要具备该特性时就可以实现此接口如此也为一些行为的拓展复用更为方便…
示例:
class Robot implements IRunning{ // 机器人类 具备跑步这个特性
@Override
public void run() {
System.out.println("机器人正在跑步");
}
}
此时出现机器人这个类,它并不属于动物这一继承体系,但是它具备跑步这个特性,则可以直接实现IRunning接口,具有跑步这个行为…
如果跑 飞 游泳不设计为接口,作为动物类的行为存在时,会存在设计上的漏洞,如狗并不具备飞,但是继承了动物又会继承飞这个行为,
显然继承应放的是所有动物的共性,一些特有的行为不能放在父类中…
而接口的存在就是将这某些特殊的行为形成一个标准规范,在设计理念上,一个类具备接口提供的行为,那么就可以实现这个接口,重写接口里的抽象方法来使当前对象具备此行为
5.接口间的继承
在Java中,子类继承父类可以实现代码复用,一个类可以实现多个接口具备多个标准行为规范,但是一个类只能继承一个父类
而接口也可以继承接口,并且一个接口可以继承多个不同的接口,使用接口可以达到多继承的效果,实现代码的复用
实例:
interface ISlowWalk{
void slowWalk();
}
interface IWalk extends ISlowWalk{
//接口可以 extends 继承其他接口 可以单个可以多个 继承后不需要重写 在实现这个接口时 要把继承的接口里的方法也重写
// 这个接口也有上面继承的接口的功能 实现了这个接口 两个功能都具备
void walk();
}
interface IFastWalk extends IWalk{
void fastWalk();
}
class Person implements IFastWalk{ //继承 快散步的接口 要重写 三个方法 因为快散步继承了慢散步 继承了散步 和快走三个功能
@Override
public void slowWalk() {
}
@Override
public void walk() {
}
@Override
public void fastWalk() {
}
}
上述代码ISlowWalk拓展出IWalk再拓展出IFastWalk接口
即 IFastWalk继承IWalk接口IWalk又继承ISlowWalk接口
此时接口间发生了多层继承,最后一层的IFastWalk接口具备上面所有接口的特性
此时被Person实现了,且Person不是抽象类,此时需要重写三个接口内的抽象方法,即表示该类对象具有三个特性
多层继承也可以写成IFastWalk同时继承以上两个继承或者也可以用嵌套的方式 一个接口类具备这多个个抽象方法 都能实现上述效果, 而具体怎么写看场景
三.抽象类和接口的区别(面试题)
Java中抽象类和接口的定义语法分别为abstract与interface关键字
抽象类和接口都是 Java 中多态的常见使用方式.
抽象类和接口都不能被实例化.
抽象类的特点:
1.被关键字abstract修饰的为抽象类
2.抽象类内被abstract修饰的方法为抽象方法(没有具体方法体的方法)
3.抽象类内部具有属性,静态方法 非静态方法(又分为抽象和非抽象方法),代码块,构造方法,但是因为不能被实例化构造方法不能直接调用需要通过实例化子类对象再通过子类构造方法调用父类构造方法…
4.抽象类被被子类继承后必须重写抽象内的抽象方法,除非子类也是抽象类
抽象类是对类的一种抽象,是一个没有完整信息来描述一个具体对象的类,最大用途是用来被子类继承,实现代码复用,比普通类作为父类被子类继承多了限制编译器校验更严格,更不容易出错
接口的特点:
1.被关键字interface修饰的为接口
2.接口是公开的即内部所有的属性和方法都是public类型的
3.接口具有属性和静态方法 非静态方法 ,属性是被public static final修饰的常量,非静态方法是被public abstract修饰的抽象方法
4.在jdk1.8中出现了被default修饰的默认方法有具体的方法实现,但是只能被实现这个接口的类调用或者重写此方法
5.接口内不具备构造方法和代码块
接口是对行为的一种抽象也就是一种标准行为规范,接口的作用就是使满足标准规范的类实现这个接口可以重写接口里的方法,具有某种特性,撇开了类与类之间的关系,更易于拓展
接口在实际开发中有很大的用处,但接口语法更为抽象,需要不断学习才能更深层次理解接口的含义…同时Java中还有很多内置的提供给我们使用的接口比如:
Comparable 接口(两个对象间比较的行为规范)
Comparator(比较器 给指定两个对象按指定内容比较大小的行为规范)
Cloneable接口( 对象可以被clone 克隆的行为规范)…