文章目录
接口的概念
在生活中,接口的例子比比皆是,比如,电脑上的USB接口、电源插座、手机type-c接口
电脑上USB接口,可以插:U盘、鼠标、键盘…所有符合USB协议的设备;
电源插座的插口上,可以插:电脑、电视、吹风机…所有符合规范的电器;
通过以上的例子我们可以看出:接口就是公共的行为规范,大家在实现时,只要符合标准规范,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
语法规则
接口的定义与定义类的格式基本相同,将class关键换成interface关键字,就定义了一个接口。
//接口语法规则
public interface InterfaceName {
//抽象方法
public abstract void method1();//public abstract是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
//注意接口在上述的描述中都是抽象方法,跟推荐方式4,代码更加简洁明了
}
【提示】
- 创建接口的命名一般以大写字母I开头
- 接口的命名一般使用“形容词”词性的单词
- 阿里编码规范中,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性。
接口的使用
接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口中所有的抽象方法。
public class 类名称 implement 接口名称 {
//…
}
注意:子类与父类直接是extends继承关系,类和接口之间是implements实现关系
//USB接口
interface USB {
void openDevice();
void closeDevice();
}
//鼠标类,实现USB接口
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接口
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接口
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 Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
//使用鼠标
computer.useDevice(new Mouse());
//使用键盘
computer.useDevice(new KeyBoard());
computer.powerOff();
}
}
在以上代码中,我们通过鼠标、键盘类简单实现了USB接口,,并通过电脑类进行使用。
接口的特征
- 接口类型是一种引用类型,但是不能直接new接口的对象
- 接口中每一个方法都是public的抽象方法,即接口中的方法会被隐式的指定为 public abstract(不能添加其他修饰符,或者修改)
- 接口中的方法是不能在接口中实现的,只能由实现中由类来实现。
- 重写接口中方法时,不能使用默认的权限
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量
- 接口中不能有静态代码块和构造方法
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀也是.class
- 如果类没有实现接口中的所有抽象方法,则类必须设为抽象类
- jdk8中:接口还可以包含defaylt方法
实现多个接口
在Java中,类和类之间是单继承的,一个类只有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口,以供使用。
【试题】
下面我们用类表示一组动物,包含猫、鱼、青蛙;同时提供一组接口,分别是”会飞的,会跑的,会游的“
//下面我们用类表示一组动物,包含猫、鱼、青蛙;同时提供一组接口,分别是”会飞的,会跑的,会游的“
interface IFlying {
void fly();
}
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
class Animal{
protected String name;
public Animal(String name) {
this.name = name;
}
}
//猫
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 + "正在蹬腿游~~~");
}
}
【注意】一个类实现多个接口,每个接口中的抽象方法都要实现,否则类要被设为抽象类。
上面的代码展示了Java面向对象编程中最常见的用法:一个类继承一个父类,同时实现多种接口。
继承的表达含义是is-a语义,而接口表达的含义是具有XXX特性
猫是一种动物,具有会跑的特性
青蛙是一种动物,既跑,也能游
鸭子也是一种动物,既能跑,也能游,还能飞
使用接口以后,类的使用这就不必关注具体的类型,而关注具有某种能力
接口间的继承
在Java中,类和类直接是单继承的,一个类可以实现多个接口,接口和接口之间可以多继承。即:用接口可以达到多继承的目的。
接口可以继承一个接口,达到复用的效果。使用sxtends关键字。
//接口间的继承
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
//两栖动物,既能跑,又能游
interface IAmphibious extends IRunning, ISwimming {
}
接口的继承相当于把多个接口合并在一起。
接口使用实例
给对象数组排序
//接口使用示例
//给对象数组排序
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "[" + this.name + ":" + this.score + "]";
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[] {
new Student("张三", 95),
new Student("李四", 96),
new Student("王五", 97),
new Student("赵六", 92),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
仔细思考发现,和普通的整数不一样,两个数之间可以比较大小,而两个学生对象之间要如何比较呢?
让我们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 "[" + this.name + ":" + this.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;
}
}
}
public class Test {
public static void main(String[] args) {
Student[] students = new Student[] {
new Student("张三", 95),
new Student("李四", 96),
new Student("王五", 97),
new Student("赵六", 92),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
在sort方法中会调用compareTo方法。compareTo的参数是Object,其实传入的是Student类型的对象,然后比较当前对象和参数对象的大小关系,在执行程序,结果就是正确的。
【注意事项】对于sort方法来说,需要传入的数组的每个对象都是“可比较的”,才具有compareTo这样的能力,通过重写compareTo方法的方式,就可以定义比较规则。
Clonable 接口和深拷贝
Java中内置一些很有用的接口,Clonable就是其中之一。
Object类中存在一个clone方法,调用这个方法可以创建一个对象的“拷贝”。但是想合法的调用clone 方法,必须实现Clonable接口,否则就会抛出CloneNotSupportedException异常。
//Clonable接口
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);
}
}
浅拷贝和深拷贝
Cloneable拷贝的对象是一份“浅拷贝”
观察以下代码
//浅拷贝
class Money {
public double m = 80.1;
}
class Person implements Cloneable {
public Money money = new Money();
@Override
protected Object clone()throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(person1.money.m);
System.out.println(person2.money.m);
System.out.println("=====================");
person2.money.m = 20.9;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
这里我们通过clone来拷贝对象animal1给animal2引用,但是实际上,我们只拷贝了animal1引用中money的地址,并没有把money中实际的内容拷贝进去,所以出现了连个money引用指向同一对象的问题,后面修改任何一个money的值,都会发生变化。
//深拷贝
class Money implements Cloneable {
public double m = 80.1;
@Override
protected Object clone()throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
public Money money = new Money();
@Override
protected Object clone()throws CloneNotSupportedException {
//克隆person对象
Person person = (Person) super.clone();
//克隆money
person.money = (Money) this.money.clone();
return person;
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person1.clone();
System.out.println(person1.money.m);
System.out.println(person2.money.m);
System.out.println("=====================");
person2.money.m = 20.9;
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
这里我们把money对象再拷贝以下即可实现深拷贝的目的
抽象类和接口的区别
抽象类和接口都是Java中多态的常见的使用方式。
【核心区别】抽象类可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用,而不被重写,而接口中不能包含普通方法,子类必须要重写所有的抽象方法。
区别 | 抽象类(abstract) | 接口(interface) |
---|---|---|
结构组成 | 普通类和抽象方法 | 抽象方法+全局变量 |
权限 | 各种权限 | public abstract |
子类使用 | 使用extends关键字 | 使用implements关键字 |
关系 | 一个抽象类可以实现若干接口 | 接口可以使用extends实现继承多个接口 |
子类限制 | 一个子类只能继承一个抽象类 | 一个类可以实现若干接口 |
Object
Object是Java中默认提供的一个类,Java中出了Object类,所有的类都是存在继承关系的,默认继承Object类,所有类的对象都可以使用Object的引用来接收
示例:使用object接受所有类的对象。
在开发中,Object类是参数的最高统一类型。但是Object类中也存在着一些定义好的方法。如
但是现在我们要熟悉的的有:toString() \ equals() \ hashcode 方法
获取对象信息(toString)
如果要打印对象中的内容,我们重写Object 中toString 方法即可
//toString
class Studnet {
String name;
int age;
public Studnet(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " " + age;
}
}
这样就能按照自己的想法的到想的内容;
我们看看原来的toString所放的内容
得到的是 类名 + @ + 类似于地址的字符串
对象比较equals方法
在Java中,== 进行比较时:
- 如果是基本数据类型,比较的是变量中的值;
- 如果是引用数据类型,比较的是引用的地址;
- 如果我们要比较对象中的内容,我们要重写Object中equals方法,因为equals方法默认也是按照地址比较。
//equals
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(this == obj) {
return true;
}
if(!(obj instanceof Student)) {
return true;
}
Student s = (Student) obj;
return this.name.equals(s.name) && this.age == s.age;
}
@Override
public String toString() {
return name + " " + age;
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("xiaohong", 9);
Student s2 = new Student("xiaohong", 9);
System.out.println(s1.equals(s2));
}
}
这里我们比较了这个对象的所有内容,全部相等,代表他们相等。