接口
(1)什么是接口
我们知道,类之间只能单继承。为了实现类似“多继承”的效果
,所以就引入了接口。接口是抽象类的更近一步,比抽象类还抽象。抽象类只是不能实例化,但是其他各个方面都和普通类差不多,接口就更抽象了,不光不能实例化,同时也不具备类的各种特性。
命名:接口的命名一般以大写字母I作为前缀,一般使用形容词进行命名。表示的语义:一个类具有XXX特性
(2)语法规则
接口的注意事项:
接口是用 interface 来修饰的
- 接口当中的普通方法不能有具体的实现,非要实现需要加关键字 default(不可以被重写)
- 接口中可以有静态方法
- 里面的所以方法都是 public 的
- 抽象方法默认是一个 public abstract 的
- 接口不可以被通过关键字 new 来进行实例化的
类和接口之间的关系是通过 implements 来实现的
- 当一个类实现了一个接口那么就必须重新接口当中的抽象方法
- 接口中的成员变量默认是被 public static final 所修饰的,一定在定义的时候初始化
- 当一个类实现一个接口之后,重写这个方法,这个方法前面必须加上 pubilc
- 一个类可以通过通过关键字 static 继承一个抽象类或一个普通类,但是只能继承一个类,也可以通过 implements 来实现多个接口,接口之间使用(,)隔开
- 接口B和接口C可以使用 extends 关键字来进行操作,此时译为:拓展,C接口通过 extends 拓展B的功能,当一个类 D 通过 implements 来实现这个接口 B 的时候,不仅需要重写 B 的抽象方法,还有重写从C拓展的抽象方法
举例:
①接口中只能放抽象方法,不能放普通方法(下图中的波浪线~)
接口中的抽象方法可以不写public abstract关键字,写或者不写,都表示抽象的共有的方法~
但是抽象类中的抽象方法必须得写abstract关键字,抽象类中除了可以放抽象方法还可以放普通方法,所以不写得话就表示一个普通方法,就得加上方法体了。
②接口中只能放public static final修饰的属性,不能放普通的属性
③接口和类之间不能继承,只能是类实现了(implements)某个接口。接口和接口之间可以继承。
Animal类
IEating接口
IJump接口
Cat类继承自Animal类,实现了IEating,IJump接口
注意:
- 实现的类必须重写接口中所有的方法。
- 实现多个接口,接口之前使用逗号分割。
(3)实现多个接口
有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的,然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果。
现在我们通过类来表示一组动物
另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, "会游泳的”
接下来我们创建几个具体的动物。
鱼, 是会游的
青蛙, 既能跑, 又能游(两栖动物)
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口。
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性。
有了接口之后,我们可以也可以实现多态。
例如, 现在实现一个方法, 叫 “散步”。
参数可以不是 “动物”, 只要会跑,就能够实现多态。
(4)接口间的继承
接口继承的关键字是extends,如果一个类只实现了其中的一个接口,那么而那个接口又继承了其它的接口,那么那个类就需要重写实现的接口及其继承的接口的所有方法。
通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法,也需要实现 swim 方法。接口间的继承相当于把多个接口合并在一起,意为拓展。
(5)抽象类和接口的对比
抽象类和普通类差不多,只是不能实例化。而接口和普通的类之间相去甚远(包含的属性,方法,和其它类的关系)
一个类只能继承自一个抽象类,但是一个类可以同时实现多个接口。
为啥要发明接口这样的语法?
解决Java中不能多继承的问题。Java中的继承是单继承,有些场景下多继承是有用的。Java中可以通过继承一个类,实现多个接口的方式来完成类似于多继承的效果。
接口的使用实例
1.Comparable接口
以下用接口给对象数组进行排序。
给定一个学生类
再给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序)
按照我们之前的理解, 数组我们有一个现成的 sort 方法。但是sort方法无法知道此时Student类型是按照什么排序的。名字?分数还是年龄?
所以这里需要我们告诉 Arrays.sort 以怎样的方式去进行比较
利用 Comparable 接口来让 Arrays.sort 知道比较的是学生
此时引入Comparable 接口, 并实现其中的 compareTo 方法。
重写 compareTo 方法后 Arrays.sort 就知道了是按照什么去排序的,就可以进行排序了
在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象。也可以在接口后面加表明比较的是Student类中的某个属性。按alt+insert重写接口当中的compareTo方法。
然后比较当前对象和参数对象的大小关系(按分数来算)。
- 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
- 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
- 如果当前对象和参数对象不分先后, 返回 0;
也可以直接return this.score-o.score ; 大于0则升序,小于0降序,等于0不排序。
此时执行代码运行结果:
注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则。如果比较的是name属性,则需要this.name.compareTo(o.name) 。
2.Comparator接口
但是Comparable接口有一个缺点,它的可入侵性非常强,假设我们要求用名字去排序,必须在原来的基础上去改变。当其他人要使用这个方法时却不知道方法内部已经被改了,会造成很多麻烦。
因此还有个接口也是对自定义类型作比较的。它是Comparator接口
。当一个类实现了Comparator接口就需要重写compare方法
。因此对于多个变量属性可以分成多个不同的类来实现compare方法。例如:在上面代码的基础上改为:
这样写的好处是:更灵活,对类的侵入性很小
这两个接口的使用,取决业务,但一般推荐比较器
3.Cloneable接口
对于一个自定义类型的拷贝,我们无法用 引用.clone() 方法直接进行拷贝,那么如何做到拷贝出一个新的引用并且改变原来的对象不改变新拷贝出来的对象。这种拷贝也称为深拷贝。运行Cloneable接口。
当我们实现Cloneable接口时按住ctrl键点入Cloneable接口时,我们发现是一个空接口,也称为标记接口,里面没有任何的字段和方法。标记接口的作用是说明一个类如果实现了Cloneable接口时就标明这个类是可以被克隆的。
下面的代码就能够做到对一个自定义类型进行拷贝。
代码运行结果:
实现Cloneable接口进行拷贝有几个需要注意的点(此时单纯拷贝一个引用):
- 第一步:要实现Cloneable接口。
- 第二步:对Cloneable接口中的clone()方法进行重写。(按alt键+insert键会有提示)
main方法中如果是第一次.clone()后也会报错,此时按alt键+enter键有以下页面提示,选中第一个即可。
对于在main方法中将一个克隆后的引用强转为自定义类型是因为我们重写的方法当中返回值是Object类型。
如果一个类当中又实例化了另一个类的对象。那么我们又需要写为下面这种形式,代码示例:
代码运行结果:
此时最好利用图解来帮助理解。
主要针对的是下面这行代码:
这个方法是重写父类clone()方法的。理解它很重要。我们先用一个引用来接收父类克隆出来的引用,再用当前student引用来接收当前对象的student的引用,说明已经完全克隆完成,此时直接返回克隆出来的引用即可。此过程就是类似于main函数中的克隆过程。
此时最好用图来理解:
由此我们可以发现Cloneable接口实现的是深拷贝。
面试中Java三大特性封装、继承、多态与抽象类、接口问题
面试问题1:普通类和抽象类的区别:
答:抽象类是由abstract关键字修饰的。在抽象类中被abstract修饰的方法可以不用实现。抽象类主要是用来被继承的。如果普通子类继承了抽象类则需要重写抽象类中的abstract方法。被absract修饰的方法不能同时被final与private修饰。其余的字段与方法都与普通类的相同。
面试问题2:接口和抽象类的区别:
接口中的只有常量,默认被public static final修饰,而方法都默认被public abstract修饰。而抽象类中的抽象方法需要手动添加abstract。接口中所有的方法都需要被重写,而抽象类中的方法只有abstract方法需要被重写。接口中所有的方法都不能实现,除非加default修饰,而抽象类中只有abstract方法不用实现。
面试问题三:什么是多态?
多态指的是一种思想,它能够降低类的调用者使用类的成本。就算不知道一个引用实例的是什么对象,只要在这个对象下有父类方法的重写,则实现一个静态方法能够调用子类当中的重写。而该方法的调用的重写能够调用多种子类的对象,并且有多种表现形式,就称为多态。