当前位置: 首页 > news >正文

Java——接口

接口概念

Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

Java接口可以看成是多个类的公共规范,是一种引用数据类型,在实现时,只要符合规范标准,就可以通用。


语法规则

将定义类的 class 关键字换成 interface 关键字,就定义了一个接口

创建接口时,接口的命名一般以大写字母 I 开头

接口的命名一般使用 形容词性 的单词


接口使用

接口不能直接使用,必须要有一个“实现类”来实现该接口,实现接口中的所有抽象方法

子类和父类之间是 extends 继承关系,类与接口之间是 implements 实现关系

例:

实现笔记本电脑使用USB鼠标、USB键盘的例子

1、USB接口:包含打开设备、关闭设备的功能

2、笔记本类:包含开机功能、关机功能、使用USB设备功能

3、鼠标类:实现USB功能,并具备点击功能

4、键盘类:实现USB功能,并具备输入功能

示例代码:

//USB接口
public interface USB {void openDevice();void closeDevice();
}//鼠标类,实现USB接口
public class Mouse implements USB{@Overridepublic void openDevice() {System.out.println("打开鼠标");}@Overridepublic void closeDevice() {System.out.println("关闭鼠标");}public void click(){System.out.println("鼠标点击");}
}//键盘类,实现USB接口
public class KeyBoard implements USB{@Overridepublic void openDevice() {System.out.println("打开键盘");}@Overridepublic 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();}
}//测试类
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、接口中的成员变量,默认是 public static final 修饰的

2、接口中的抽象方法,默认是 public abstract 修饰的,不能有方法体

3、如果接口中的方法被default 修饰,可以有具体的实现

4、如果接口当中的方法被static修饰,可以有具体的实现

5、接口不可进行实例化

6、一个接口对应一个字节码文件

7、接口中不能有静态代码和构造方法

8、如果一个类不想实现这个接口中的方法,那么这个类就被定义为抽象类,如果后面这个类被继承,就要实现所有的没有被实现的方法


一个类实现多个接口

Java不支持多继承,但是一个类可以实现多个接口

例:

通过类来表示一组动物,提供一组接口,分别表示“会飞的”、“会跑的”、“会游泳的”。

public class Animal {protected String name;public Animal(String name){this.name = name;}
}public interface IFlying {void fly();
}public interface IRuning {void run();
}public interface ISwimming {void swim();
}

下面创建几个具体的动物

//猫 会跑
class Cat extends Animal implements IRuning{public Cat(String name) {super(name);}@Overridepublic void run() {System.out.println(this.name + " is running");}
}//鱼 会游泳
class Fish extends Animal implements ISwimming{public Fish(String name) {super(name);}@Overridepublic void swim() {System.out.println(this.name + " is swimming");}
}//狗 既能跑,也能游泳
class Dog extends Animal implements IRuning,ISwimming{public Dog(String name) {super(name);}@Overridepublic void run() {System.out.println(this.name + " is running");}@Overridepublic void swim() {System.out.println(this.name + " is swimming");}
}//鸭子 水陆空三栖
class Duck extends Animal implements IRuning,ISwimming,IFlying{public Duck(String name) {super(name);}@Overridepublic void run() {System.out.println(this.name + " is running");}@Overridepublic void swim() {System.out.println(this.name + " is swimming");}@Overridepublic void fly() {System.out.println(this.name + " is flying");}
}

上面代码展示了Java面向对象编程中最常见的用法:一个类继承一个父类,同时实现多种接口

继承表达的含义是 is - a 语义,而接口表达的含义是 具有xxx的特性


接口间的扩展(继承)

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承

即:用接口可以实现多继承的目的

接口可以扩展一个接口,达到复用的效果,使用 extends 关键字,实例如下:

interface IA{void testA();
}interface IB{void testB();
}interface IC extends IA,IB{void testC();
}class D implements IC{@Overridepublic void testC() {}@Overridepublic void testA() {}@Overridepublic void testB() {}
}

IC接口扩展了IA,IB接口,在D类中实现接口IC,就必须重写IA,IB,IC中的所有方法,接口间的扩展(继承)相当于把多个接口合并在一起


接口使用实例

当我们有如下代码:

class Student{public String name;public int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
public class Test {public static void main(String[] args) {Student student1 = new Student("zhangsan",18);Student student2 = new Student("lisi",38);System.out.println(student1 > student2);//error}
}

我们想要自定义的类之间进行比较,有两个问题:

1、当前自定义类要根据什么规则去进行比较?(如上学生类中,按照name,还是age)

2、这个规则该如何定义?


解决方法:

要用到接口 Comparable< > ,根据源码可知< >内需填入要进行比较的类名(语法上来说,这部分叫 泛型

并且要重写comparTo方法,如下:

这里就体现了:接口就是某种定义的规范

然后可以使用重写后的 comparTo方法进行自定义类型的比较,如下:

System.out.println(student1.compareTo(student2));

其中: 

 运行结果:因为 zhngsan 的年龄为18,lisi 的年龄为38,所以会返回一个负值


当我们按照name为规则进行比较时

类型为引用类型,就不能直接相减作比较了,我们去看String的源码:

发现其中也实现了comparTo方法,所以比较代码可改写为:

z 的ASCII码大于 l ,所以运行结果为正值:


第二个应用场景:多个学生进行比较,代码如下:

public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student("zhangsan", 18);students[1] = new Student("lisi", 38);students[2] = new Student("wangwu", 8);System.out.println("排序前:" + Arrays.toString(students));Arrays.sort(students);System.out.println("排序后:" + Arrays.toString(students));}

运行结果:可以看出它是依据 neme 进行排序,是因为我们调用了重新写的 Comparable<> 中的comparTo 方法

 当我们将Comparable屏蔽,将重写的comparTo方法屏蔽,会出现以下报错:

类型转换异常,demo1中的Student类不能转换为Comparable类

我们查看第320行报错的源码:

发现第一步给源码传参一个 Object[ ] 数组, 第二步将数组强制类型转换为Comparable类型,第三步调用ComparTo方法,这里我们可以认为第二步成功后,第三步一定成功

结论:只要是自定义类型涉及到大小的比较,一定要实现Comparable接口

tips:模拟 Arrays.sort

    public static void mysort(Comparable[] comparables){for (int i = 0; i < comparables.length-1; i++) {for (int j = i + 1; j < comparables.length-1-i; j++) {if(comparables[j].compareTo(comparables[j+1]) > 0){Comparable tmp = comparables[j];comparables[j] = comparables[j+1];comparables[j+1] = tmp;}}}}

接下来我们需思考一个问题,如下代码:

此处代码完成后,后续不应再继续改动,因为确定一个公共比较规则后(如按照age进行比较),把此处代码再改为按照年龄比较,别人不知道的情况下,继续传入了age参数,结果就会不一样

根据不同的属性进行比较,无法每次重新修改类已经写好的方法

这就是此代码的缺陷:一般用于固定的比较,不灵活,也就是不解耦

解决方法:使用Comparator<> 接口

新建一个NameComparator类,并实现Comparator<> 接口

public class NameComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return o1.name.compareTo(o2.name);}
}

查看源码,可知需重写其compare方法

main函数中:

可实现按照 name 排序,运行解果如下:


 

同理,新建一个AgeComparator类,并实现Comparator<> 接口,即可实现按照 age 排序,如下:

//AgeComparator类
import java.util.Comparator;public class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}//Test类public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student("zhangsan", 18);students[1] = new Student("lisi", 38);students[2] = new Student("wangwu", 8);System.out.println("排序前:" + Arrays.toString(students));NameComparator nameComparator = new NameComparator();AgeComparator ageComparator = new AgeComparator();Arrays.sort(students, ageComparator);System.out.println("排序后:" + Arrays.toString(students));}

这就达到了 解耦 的目的。

相关文章:

  • Vue 2与Vue 3的区别
  • java图书电子商务网站的设计与实现源码(springboot+vue+mysql)
  • vue3父组件使用ref获取子组件的属性和方法
  • java 拦截器-用户无操作超时退出利用Redis
  • 【智能算法应用】模拟退火算法求解多车型车辆路径问题HFVRP
  • 在CSDN上成长的感悟,你的粉丝长啥样?
  • DEM、DSM和DTM之间的区别及5米高程数据获取
  • DragonKnight CTF2024部分wp
  • 缓存归纳总结1
  • go语言泛型Generic最佳实践 --- slices包
  • Unity 代码实现Animator开始和结束播放动画回调
  • 代码审计--一道简单的文件包含题目的多种利用方式
  • Jenkins + github 自动化部署配置
  • 二十九篇:构建未来:信息系统的核心框架与应用
  • Laravel(Lumen8) + Supervisor 实现多进程redis消息队列
  • 网络传输文件的问题
  • 【知识碎片】第三方登录弹窗效果
  • angular2 简述
  • CentOS 7 修改主机名
  • nodejs:开发并发布一个nodejs包
  • Python - 闭包Closure
  • react 代码优化(一) ——事件处理
  • React16时代,该用什么姿势写 React ?
  • Sass Day-01
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • vue-cli3搭建项目
  • 大主子表关联的性能优化方法
  • 浮动相关
  • 复杂数据处理
  • 关于for循环的简单归纳
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 数据结构java版之冒泡排序及优化
  • 微信公众号开发小记——5.python微信红包
  • 我建了一个叫Hello World的项目
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 正则与JS中的正则
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 大数据全解:定义、价值及挑战
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​如何使用QGIS制作三维建筑
  • # include “ “ 和 # include < >两者的区别
  • #565. 查找之大编号
  • #pragma pack(1)
  • (C语言)球球大作战
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (十三)Flask之特殊装饰器详解
  • (算法)前K大的和
  • (学习总结)STM32CubeMX HAL库 学习笔记撰写心得
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)