【Java】嚼烂基础之接口
文章目录
前言
我们对Java对象和多态的概念了一定的了解,今天我们就来深入学习Java面向对象魅力之“接口”。本篇文章将详细介绍Java语言中的接口interface概念,包括使用方法、特性、使用实例、抽象类与接口的区别,话不多说,让我们开始吧!!
一、接口
1.1 接口的概念
一说到接口,相比会很自然地联想到现实生活中的[接口],比如:笔记本上的USB口,耳机孔,电源插座等等。
电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
电源插座插孔上,可以插:冰箱、电脑电源线、电视机…所有符合规范的设备
总结下来不难发现:接口就是公共的行为规范标准,大家在实现时,只要符合规范,就可以通用。
而今天我们Java语言中的接口也异曲同工。
在JAVA中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
1.2 接口的语法规则
接口的定义格式和定义类的格式基本相同,将class关键字换成interface
关键字就成功定义了一个接口。
public interface 接口名称{
//抽象方法
public abstract void method1();
//public abstract是默认搭配,可以不写
void method2();
void method3();
}
注意:
- 创建接口时,接口的命名一般以大写字母
I
开头- 接口的命名一般使用形容词词性的单词
- 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性
1.3 接口的使用
接口不能直接使用,必须要有一个“实现类”来“实现”该接口,实现接口中的所有抽象方法。
规则如下所示:
public class 子类名 implements 接口名称{
// ...
}
子类和父类之间是
extends
继承关系
类和接口之间是implements
实现关系
1.3.1 接口运用具体样例
请实现笔记本电脑使用USB鼠标、USB键盘的例子
- USB接口:包含打开设备、关闭设备功能
- 笔记本类:包含开机功能、关机功能、使用USB设备功能
- 鼠标类:实现USB接口,并具备点击功能
- 键盘类:实现USB接口,并具备输入功能
代码如下:
interface IUSB{
void OpenDevice();
void CloseDevice();
}
class Mouse implements IUSB{
@Override
public void OpenDevice() {
System.out.println("打开鼠标!");
}
public void click(){
System.out.println("鼠标点击!");
}
@Override
public void CloseDevice() {
System.out.println("关闭鼠标!");
}
}
class KeyBoard implements IUSB{
@Override
public void OpenDevice() {
System.out.println("打开键盘!");
}
public void input(){
System.out.println("键盘输入!");
}
@Override
public void CloseDevice() {
System.out.println("关闭键盘!");
}
}
class Computer{
public void PowerOn() {
System.out.println("打开计算机!");
}
public void PowerOff() {
System.out.println("关闭计算机!");
}
public void UseDevice(IUSB iusb){
iusb.OpenDevice();
if (iusb instanceof Mouse){
Mouse mouse = (Mouse)iusb;
mouse.click();
}else if (iusb instanceof KeyBoard){
KeyBoard keyBoard = (KeyBoard) iusb;
keyBoard.input();
}
iusb.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();
}
}
编译结果如下:
1.4 接口的特性
-
接口类型是一种引用类型,但是不能直接new接口的对象
-
接口中的每一个方法都是public修饰的抽象方法,即在接口中的方法会被隐式指定为public abstract(只能是public abstract,其他的会报错)
-
接口中的方法是不能在接口中实现的,只能由实现接口的类实现
-
重写接口中方法时,不能使用普通类中方法的默认访问权限
-
接口中可以含有变量,但是会被隐式的指定为public static final变量
-
接口中不能有静态代码块和构造方法
-
接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是
.class
-
如果类没有实现接口中的所有抽象方法,则该类必须设置为抽象类
-
jdk8中:接口还可以包含default方法。
1.4.1 实现多个接口
在Java中,类与类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承。
但是!一个类可以实现多个接口。
下面来看一段关于动物类的代码:
先创建一个动物类
class Animal{
protected String name;
public Animal(String name){
this.name = name;
}
}
再创建相关的接口
//有的会游
public interface ISwim {
void swim();
}
//有的会跑
public interface IRun {
void run();
}
//有的会飞
public interface IFly {
void fly();
}
创建一个会跑的狗类
class Dog extends Animal implements IRun{
public Dog(String name){
super(name);
}
@Override
public void run(){
System.out.println(this.name+"正在奔跑.....");
}
}
创建一个海陆空都会的鸭鸭类
class Duck extends Animal implements ISwim,IRun,IFly{
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特性
狗是动物,具备奔跑的能力
鸭子是动物,会飞,会跑,也能游
这样设计非常的巧妙!有了接口之后,类的使用者就不用关注具体类型,只需关注这个类是否具备某种能力。
比如上面的动物类,有跑的接口,有飞的接口。
我们换个角度想,飞机!他也能飞,omg那是不是也可以实现IFly的接口?!
这就是巧妙所在!
1.5 接口间的继承
在Java中,类和类之间是单继承的,一个类可以实现多个接口,而接口与接口之间可以多继承。即:用接口可以间接达到多继承的目的。
我们试试优化下上面的代码:
public interface ISwim {
void swim();
}
//有的会跑
public interface IRun {
void run();
}
//有的会飞
public interface IFly {
void fly();
}
//鸭子能海陆空都可以
interface IAmphibious extends IRun,IFly,ISwim{
}//使用extends关键字实现接口复用的效果
class Duck extends Animal implements IAmphibious{
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+"正在游.....");
}
}
通过接口继承创建一个新的接口IAmphibious(两栖类的),此时实现接口创建的Duck类,要把两栖类中的所有抽象类方法全部重写,但接口选择更为明确。
接口间的继承相当于把多个接口合并到一个中
1.6 Clonable接口和深拷贝
Java中内置了一些很有用的接口,比如Clonable。
clonable接口是Object类中的方法,功能是完全复制拷贝一个类的对象,但是实现的过程中有些注意的地方,其中就有浅拷贝与深拷贝
1.6.1Clonable接口
Object类中存在一个clone方法,调用这个方法可以创建一个对象的“拷贝”,但想要合法调用clone方法,必须要先实现Clonable接口,否则就会抛出 CloneNotSupportedException
异常。
举个例子:
class Money{
public double money = 12.5;
}
class Person implements Cloneable{
public String name;
public int age;
public Money m;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Person(String name, int age) {
this.name = name;
this.age = age;
m = new Money();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class test1 {
public static void main(String[] args) throws CloneNotSupportedException {
⬆ 这里属于异常章节内容,暂且不详细介绍,先用着^^
Person person1 = new Person("zhangsan",13);
Person person2 = (Person)person1.clone();
System.out.println(person1.toString());
System.out.println(person2.toString());
}
}
输出结果:
1.6.2浅拷贝与深拷贝
下面我们通过观察一段代码来了解【浅拷贝与深拷贝】
package demo1;
class Money{
public double money = 12.5;
}
class Person implements Cloneable{
public String name;
public int age;
public Money m;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Person(String name, int age) {
this.name = name;
this.age = age;
m = new Money();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class test1 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("zhangsan",13);
Person person2 = (Person)person1.clone();
System.out.println("修改之前" + person1.m.money);
System.out.println("修改之前" + person2.m.money);
person2.m.money = 99;
System.out.println("修改之后" + person1.m.money);
System.out.println("修改之后" + person2.m.money);
}
}
输出结果如下:
在其中我们对person2的money进行了修改,预期中我们应该输出12.5和99
但我们发现person1的money也被修改了,这是为什么呢?
我们person中的money在被克隆clone的时候,被克隆的其实是用来指向money对象的地址,不会重新给person2创建克隆一个money对象。
最后实现的效果就是person1与person2的m指向的是同一块money空间,所以会出现多个更改的情况。
那么我们想要实现只对一个对象进行更改需要做的就是:给money也实现克隆方法
进行代码更新:
class Money implements Cloneable{
public double money = 12.5;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public String name;
public int age;
public Money m;
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp = (Person)super.clone();
tmp.m = (Money)this.m.clone();
return tmp;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
m = new Money();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class test1 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("zhangsan",13);
Person person2 = (Person)person1.clone();
System.out.println("修改之前" + person1.m.money);
System.out.println("修改之前" + person2.m.money);
person2.m.money = 99;
System.out.println("修改之后" + person1.m.money);
System.out.println("修改之后" + person2.m.money);
}
运行结果:
PS:可以观察到,是不是深拷贝,就看程序员实现的怎么样了🥹🥹
【总结】:
如果一个类实现了Clonable这个空接口/标记接口,那么证明这个类是可以被克隆的
深拷贝:完全克隆出一个独立于原来对象的对象
浅拷贝:克隆一个原来对象的对象,但可能不完整
1.7 抽象类与接口的区别
核心区别:
抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不用重写!)。
接口中不能包含普通方法,子类必须重写所有的抽象方法。
结语
好了以上就是本篇“【Java】嚼烂基础之接口”博客的全部内容啦,感谢各位的阅读=v=,如有不足之处欢迎在评论区指出哦!!
觉得可以的话别忘了点赞三连支持一下欧!拜托啦这对我真的很重要o(>ω< )o!!!