面向对象
接口
接口是抽象类的进一步,抽象类中还可以包含非抽象方法,和字段,而接口中包含的方法都是抽象方法,字段只能包含静态常量。
语法规则
在之前的打印图形的示例中,我们的父类Shape
并没有包含别的非抽象方法,也可以设计成一个接口
interface IShape { void draw(); } class Cycle implements IShape { @Override public void draw() { System.out.println("○"); } } public class Test { public static void main(String[] args) { IShape ishape = new Cycle(); ishape.draw(); } }
- 使用
interface
定义一个接口 - 接口中的方法一定是抽象方法,因此可以省略
abstract
- 接口中的方法一定是
public
,因此可以省略public
Cycle
使用implements
继承接口,此时表达的含义不再是“扩展”,而是“实现”- 在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例
- 接口不能单独被实例化
扩展(
extends
) 实现(implements
)扩展指的是当前已经有一定的功能,进一步扩充功能
实现指的是当前啥都没有需要从头构造出来
接口中只能包含抽象方法,对于字段来说,接口中只能包含静态常量(final static
)
其中的public
static
final
关键字都可以省略,省略后的num
仍然表示public
的静态常量
提示:
1.我们创建接口的时候,接口的命令一般是以大写字母
I
开头接口的命令一般使用“形容词”词性的单词
阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性
一个错误的代码
重写是发生在继承中,接口是无法重写的,并且重写的每个地方都要一样,(返回类型,形参的个数),接口是不能重写的
实现多个接口
有的时候需要我们让一个类同时继承多个父类,这件事情在编程语言中通过多继承的方式来实现的
然而在java
中只支持单继承,一个类只能extends
一个父类,但是可以同时实现多个接口,也能达到多继承类似的效果
通过类来表示一组动物
class Animal { protected String name; public Animal(String name) { this.name = name; } }
另外我们提供一组接口,分别表示“会飞的” , “会跑的” ,“会游泳的”
interface IFly { void fly(); } interface IRun { void run(); } interface ISwim { void swim(); }
接下来,创建一个具体的动物
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
特性
鸭子是一种动物,既能跑,也能游,还能飞
这样设计有什么好处呢?时刻牢记多态的好处,让程序猿忘记类型,有了接口之后,类的使用者就不用关注具体类型,只关注某个类是否具备某种能力
接口使用实例
刚才的例子比较抽象,我们来一个更能实际的例子
给对象数组排序
给定一个学生类
再给一个学生数组,对这个数组中的元素进行排序(按分数排序)
Student[] students = new Student[] { new Student("张三", 95), new Student("李四", 96), new Student("王五", 97), new Student("赵六", 92), };
按照之前的理解,我们直接用sort
方法,能否直接使用这个方法呢?
仔细思考,不难发现和普通的整数不一样,两个整数是可以直接比较大小的,大小关系明确,而两个学生对象的大小关系怎么确定?需要我们额外指定
让我们的Student
类实现Comparable
接口,并实现其中的compareTo
方法
class Student implements Comparable{ private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + '}'; } @Override public int compareTo(Object o) { Student s = (Student)o; if (this.score > s.score) { return -1; } else if (this.score < s.score) { return 1; } else { return 0; } } }
在sort
方法中自动调用compareTo
方法,compareTo
的参数是Object
,其实传入的就是Student
类型的对象
然后比较当前对象和参数对象的大小关系(按分数来算)
- 如果当前对象应排在参数对象之前,返回小于0的数
- 如果当前对象应排在参数对象之后,返回大于0的数
- 如果当前对象和参数对象不分先后,返回0
注意事项 对于sort
方法来说,需要传入的数组的每个对象都是可“比较的”,需要具备compareTo
这样的能力,通过重写compareTo
方法的方式,就可以定义比较规则
接口间的继承
接口可以继承一个接口达到重复用的效果,使用extends
关键字
通过接口创建一个新的接口,就继续要实现父类接口中的各种方法
接口间的继承相当于把多个接口合并在一起
Clonable接口和深拷贝
Object
类中存在一个clone
方法,调用这个方法可以创建一个对象的“拷贝”,但是想要合法调用clone
方法,必须要先实现Clonable
,否则就会抛出CloneNotSupportedException
class Animal implements Cloneable { private String name; @Override public Animal clone() { Animal o = null; try { o = (Animal)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return o; } } public class Test { public static void main(String[] args) { Animal animal = new Animal(); Animal animal2 = animal.clone(); System.out.println(animal == animal2); } }
总结
抽象类和接口都是java
中多态的常见方式,都需要重点掌握,同时又要认清两者的区别(重要!!!常见面试题)
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法
再次提醒:抽象类存在的意义是为了让编译器更好的校验,像
Animal
这样的类我们并不会直接的使用,而是使用它的子类,万一不小心创建了Animal
的实例,编译器会自动提醒我们