🎇🎇🎇作者:
@小鱼不会骑车
🎆🎆🎆专栏:
《java练级之旅》
🎓🎓🎓个人简介:
一名专科大一在读的小比特,努力学习编程是我唯一的出路😎😎😎
抽象类与接口
- ✉️抽象类与接口
- 📫前言
- 📫正文
- ✉️抽象类
- 🍂抽象类的基本概念
- 🍁抽象类的特性
- 🍂对比抽象类和普通类
- 🍂抽象方法和非抽象方法
- 🍂当我们的普通类继承了这个抽象方法
- 🍂抽象类能不能使用private,static,final修饰呢?
- 🍂抽象类和普通类的区别
- 🍁总结抽象类(想看定义的可以直接跳到这里)
- 🍂抽象类的作用
- 🍂小练习
- ✉️接口
- 🍁认识接口
- 🍂接口的概念
- 🍂接口的特点
- 🍂为什么要用接口
- 🍁接口的各种用法(!!!!!)
- 🍃第一部分
- 🍂语法规则
- 🍂接口的使用
- 🍂小练习
- 🍃第二部分
- 🍂接口的特性(重点)
- 🍃第三部分
- 🍂实现多个接口
- 🍂接口间的继承
- 🍂接口的标识用法
- 🍃第四部分
- 🍂接口的通俗理解
- 🍂接口在生活中的思想体现
✉️抽象类与接口
📫前言
任务一:抽象类
任务二:接口
我们在讲抽象类之前,先带大家熟悉一下普通类
class Fruits {
String name;
public void colour() {
System.out.println("颜色");
}
public Fruits(String name) {
this.name = name;
}
}
class Apple extends Fruits {
Apple(String name) {
super(name);
}
@Override
public void colour() {
System.out.println(name+" 是红色的");
}
}
class Banana extends Fruits{
@Override
public void colour() {
System.out.println(name+" 是黄色的");
}
public Banana(String name) {
super(name);
}
}
public class Test {
public static void draw(Fruits fruits) {
fruits.colour();
}
public static void main(String[] args) {
draw(new Apple("苹果"));
draw(new Banana("香蕉"));
}
}
上述代码它们的父类是一个水果,有没有说他是哪一个水果?没有吧!我们的水果有很多种,他并没有办法完整的描述一个图型,所以形容这样一个东西,就要把它的类设计为一个抽象类,下面的篇幅就将解释,什么是抽象类?
📫正文
要完成的任务
任务一:抽象类
✉️抽象类
🍂抽象类的基本概念
普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分,那么具体的组成部分是什么呢?请往下看
🍁抽象类的特性
🍂对比抽象类和普通类
普通类
class Fruits1 {
String name;
public void colour() {
System.out.println("颜色");
}
}
抽象类
abstract class Fruits2 {//用abstract修饰就是抽象类
String name;
public void colour() {
System.out.println("颜色");
}
}
他们直接我们能够直接看到的区别就是抽象类被abstract
修饰了
大家再看这串代码块
public static void main(String[] args) {
Fruits1 fruits1=new Fruits1("苹果");
Fruits2 fruits2=new Fruits2("香蕉");
// 会报错,因为我们的抽象类不能实例化
}
如果想要解决的话,我们可以
Fruits2 fruits2=new Banana("香蕉");//发生了类型提升
虽然我们不可以对抽象类进行实例化,但是我们可以对它的子类进行实例化,最后再把这个对象赋值给Fruits2这个类的引用。(也就是类型提升)
总结:
- 特性1:抽象类使用abstract修饰
- 特性2:抽象类不能实例化
🍂抽象方法和非抽象方法
前面不是讲到了嘛,我们并不能知道我们抽象类里面的方法到底是形容哪个子类的,所以我这个类里面的方法就可以不用具体的实现,但是不去具体实现是有一个前提的,那就是需要用abstract
修饰
例一:不用abstract修饰(两个都是抽象类进行对比)
abstract class Fruits1 {
String name;
public void colour() {
System.out.println("颜色");
}
}
例二:用abstract修饰
abstract class Fruits2 {
String name;
//我们的方法被abstract修饰
abstract public void colour();
}
大家经过对比是不是发现,咦?原来用了abstract
修饰就可以不用去具体实现这个方法。
再去反过头看例一,我们父类中的方法更多的是会被重写的,所以不需要去管父类的方法究竟实现了什么,也就是可以直接如例二一样不去实现这个方法,是不是更简洁,看起来更高大上。
但是!!!
如果我们的类不是抽象类,那么就无法对其方法进行abstract
修饰,只有是抽象类的方法才可以!!!
class Fruits1 {
String name;
abstract public void colour() {
System.out.println("颜色");
}
public Fruits1(String name) {
this.name = name;
}
}
错误信息
不过我们的抽象类中是可以有抽象方法和非抽象方法的。
abstract class Fruits1 {//抽象类
public void print() {//非抽象方法
System.out.println("我是一个抽象类");
}
}
public class Test1 extends Fruits1{
public static void main(String[] args) {
Fruits1 fruits1=new Test1();
fruits1.print();
}
}
抽象类中有构造方法么?
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
abstract class A{//定义一个抽象类
public A(){
System.out.println("我是A类构造方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
public B(){
super();//父类的构造方法没有参数,可以不写。
System.out.println("我是B类构造方法");
}
@Override
public void print() {//强制要求覆写
System.out.println("Hello World !");
}
}
public class Test1 {
public static void main(String[] args) {
A a = new B();//向上转型
}
}
执行结果:
可以直接调用抽象类中用static声明的方法么?
任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接调用,对于抽象类也一样。
总结:
- 特性一:什么是抽象方法?一个被
abstract
修饰的,没有具体的实现 - 特性二:抽象类方法中可以有抽象方法,或者非抽象方法
- 特性三:只要包含抽象方法,那么这个类就必须是抽象类!!!反过来推(普通类中不能使用abstract去修饰方法)
- 特性四:抽象类存在构造方法
- 特性五:抽象类也可以被直接调用静态方法
🍂当我们的普通类继承了这个抽象方法
abstract class Fruits {//抽象方法
String name;//成员变量
abstract public void colour();//抽象方法
public Fruits(String name) {//构造方法,用于初始化
this.name = name;
}
}
//普通类继承抽象类
class Apple extends Fruits {
Apple(String name) {
super(name);
}
@Override
public void colour() {
System.out.println(name+" 是红色的");
}
}
//普通类继承抽象类
class Banana extends Fruits{
@Override
public void colour() {
System.out.println(name+" 是黄色的");
}
public Banana(String name) {
super(name);
}
}
public class Test {
public static void draw(Fruits fruits) {
fruits.colour();
}
public static void main(String[] args) {
draw(new Apple("苹果"));
draw(new Banana("香蕉"));
}
}
正常输出,输出结果为
但是如果我不去重写这个方法呢?
class Apple extends Fruits {
//报错!!!
@Override
public void colour() {
System.out.println(name+" 是红色的");
}
}
class Banana extends Fruits{
//报错!!!
public Banana(String name) {
super(name);
}
}
但是如果我们继承的是普通类,就不会去报错了,其实抽象类这个做法就是为了让我们的代码更严谨些,如果我写的是普通类,我忘记对父类的方法进行重写,,那么系统也没有报错,我以后运行的话,就可能会有未知的错误随时可能发生。
所以我们要注意,继承抽象类时一定要重写抽象类的方法!!!
如果我真的真的不想去重写这个方法呢?
也有解决的办法
abstract class Fruits {
String name;
abstract public void colour();
public Fruits(String name) {
this.name = name;
}
}
//就是将我的子类也变成抽象类
abstract class Apple extends Fruits {
public Apple(String name) {
super(name);
}
}
不过啊,出来混总归是要还的,你这次可以不去重写,那么你下次呢?只要你想用这个抽象类(哪怕是这个抽象类其中的任意一个方法),你就必须得重写这个抽象类中全部的方法,不过你如果想单独使用其中一个方法也有解决的方法,答案就在接口中!
- 特性一:当一个普通类继承了这个抽象类时,必须要重写这个抽象类当中的抽象方法。
- 特性二:当一个子类没有重写(抽象类)父类的方法时,可以将当前的子类也变成
abstract
修饰的抽象类。
🍂抽象类能不能使用private,static,final修饰呢?
其实大家就可以理解为,重写需要满足的条件是什么?
用private
修饰时
如果我们父类中的方法被private
修饰之后,我们还能访问到这个方法嘛?不能啊.
具体关于权限修饰符大家可以访问这篇博客public权限修饰符
abstract class Fruits {
String name;
abstract private void colour();//编译出错
public Fruits(String name) {
this.name = name;
}
}
错误信息
如果我们的方法被static
修饰时?
当我们的方法被static
修饰时,他就不属于对象,而是属于类,这样我们再去运行也会出错
abstract class Fruits {
String name;
abstract static void colour();
//运行出错,不可以用static
public Fruits(String name) {
this.name = name;
}
}
当我们用final
修饰时
被final
修饰的方法是不可以再去改变的,就像变量被final修饰之后成为常量,你还可以去修改这个常量嘛?不能啊!
abstract class Fruits {
String name;
final abstract void colour();//编译出错
public Fruits(String name) {
this.name = name;
}
}
总结:
- 特性一;抽象方法不能被private,final,static修饰,要满足(重写的规则)
🍂抽象类和普通类的区别
抽象类 | 普通类 |
---|---|
抽象类不能实例化 | 普通类可以实例化 |
抽象类中可以包含非抽象方法和抽象方法 | 只能包含非抽象方法 |
🍁总结抽象类(想看定义的可以直接跳到这里)
1.抽象类使用
abstract
修饰。
2.抽象类不能实例化,需要依靠子类采用向上转型的方式处理。
3.抽象类当中可以有抽象方法或者非抽象方法。
4.抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
5.什么是抽象方法?一个方法被abstruct修饰的,没有具体的实现。(只要包含抽象方法就一定是抽象类!!!)。
6.当一个普通(子)类继承了这个抽象类,就必须重写这个抽象类当中的抽象方法。
7.抽象类存在的最大意义就是为了被继承。
8.抽象方法不能被private,static,final
修饰。
9.当一个子类没有重写父类(抽象了)的方法,可以把当前的子类用abstruct
修饰。
10.抽象类当中不一定包含抽象方法。
11.抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
12.抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类。
虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。
🍂抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
既然普通的类也可以被继承, 普通的方法也可以被重写, 为啥非得用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类 了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛?
但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们. 充分利用编译器的校验, 在实际开发中是非常有意义的
🍂小练习
我如果想在屏幕上打印红 红 绿 红 绿 黄下面这串代码怎么做到?
abstract class Fruits {
abstract void colour();
}
class Read extends Fruits {
@Override
public void colour() {
System.out.println("红 ");
}
}
class Yellow extends Fruits{
@Override
public void colour() {
System.out.println("黄 ");
}
}
class Green extends Fruits{
@Override
void colour() {
System.out.println("绿 ");
}
}
在我们没有学习过多态以及抽象类时,我们的想法应该就是写一个逻辑判断条件,就像下面的代码,虽然是正确的打印出来了想要的,但是有些麻烦。
public static void draw() {
// 红 红 绿 红 绿 黄
Red red=new Red();
Green green=new Green();
Yellow yellow=new Yellow();
String[] p={"Red","Red","Green","Red","Green","Yellow"};
for (String colour:p) {
if(colour.equals("Red")) {
red.colour();
} else if (colour.equals("Green")) {
green.colour();
} else {
yellow.colour();
}
}
}
但是!在我们学过了多态和抽象类!那真的就跟砍瓜切菜一样轻松,所以我们要熟练的掌握这些方法和语法。
public static void draw() {
// 红 红 绿 红 绿 黄
Red red=new Red();
Green green=new Green();
Yellow yellow=new Yellow();
Fruits [] fruits={new Red(),new Red(),new Green(),new Red(),new Green(),new Yellow()};
//Fruits 是类型 cloluR是引用变量 fruits是数组名
for (Fruits colouR:fruits) {
colouR.colour();
}
}
输出结果
✉️接口
🍁认识接口
🍂接口的概念
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
通俗易懂的解释:接口可以理解为一种 特殊的类,里面全部是由 全局常量和 公共的抽象方法所组成。,既接口中的方法必须全部是抽象方法.
🍂接口的特点
就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。
总结了四点:
- 接口指明了一个类必须要做什么(如果实现了这个接口,就要重写它的方法)
- 一个接口就是描述一种能力,比如运动可以作为一个接口,所有实现我这个接口的类,都应该具有跑步这个能力,这就告诉我们,如果你想实现我这个接口,你就必须要实现我这个接口里所拥有的功能,这样我才会承认你确实拥有实现我这个接口的资格。
- 如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(总结:抽象方法只能实现在抽象类和接口类中,但是抽象类还可以实现非抽象方法,所以我们可以说接口类是100%的抽象类,所以可以推出抽象类包含接口类)
- 举个例子:我实现了一个接口ISwap,这个接口具有“交换”这个功能,那么所有实现我这个ISwap接口的类,也可以用来进行“交换”的操作。
🍂为什么要用接口
- 接口被用来描述一种抽象。
- 因为Java不像C++一样支持多继承,所以Java可以通过实现接口来弥补这个局限。
- 接口也被用来实现解耦(减少依赖)。如果想了解可以看看这个解耦
- 接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?原因是抽象类内部可能包含非final的变量,但是在接口中存在的变量一定是final,public,static的
。(后面讲到)
🍁接口的各种用法(!!!)
🍃第一部分
🍂语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface
关键字,就定义了一个接口。
interface Iusb//接口名称 {
//全局常量
int a=10;
//抽象方法
void read();//读取数据
void save();//保存数据
}
这样我们就定义好了一个接口,并且在接口里面写入了一个全局常量a和两个抽象方法,但是有同学就会好奇?
这时候同学就有两个疑问?
为什么是全局常量呢?为什么你的抽象方法可以不用去被修饰呢?
小鱼一个一个回答:
问题一:
在我们的接口中,所有的变量都是默认被public static final
修饰的,并且!我们的常量也需要在接口中进行初始化,不然爆出的错误信息就是
问题二:
接口中,默认里面的方法都是被public abstract
修饰的,并且不能对这个方法进行具体的实现,否则就会
总结:
- 接口里面的成员变量一定要进行赋值,并且不可以对它进行
protected static int a=10;
这样的做法,我们的接口里面的数据成员全部都是被public
修饰的,不可以进行修改。 - 我们的抽象方法不可以有方法体,并且我们接口里面的方法默认都是
public abstract
修饰的。
下面是一些实现接口的规范
- 创建接口时, 接口的命名一般以大写字母 I 开头.
- 接口的命名一般使用 “形容词” 词性的单词.
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性(
void read();
这样的写法)
🍂接口的使用
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
这里也就是涉及到了关键字implements
interface Iusb {
int a=10;
void read();//读取数据
void Save();//保存数据
}
//当然,现在这么写是错误的,只是带大家熟悉一下这个关键字
class mouse implements Iusb{
}
我们继承普通类,抽象类用到的是extends
,实现接口用到的就是implements
.
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
🍂小练习
在我们印象里,如果某个设备需要向电脑中读取或者写入某些东西,这些设备一般都是采用USB方式与电脑连接的,所以只要是带有USB功能的设备就可以插入电脑中使用,那么我们可以认为USB就是一种功能,这种功能能够做出很多的事情(实现很多的方法或者行为),我们也可以把USB就可以看做是一种标准,一种接口,只要实现了USB标准的设备我就认为你已经拥有了USB这种功能。(因为你实现了我USB标准中规定的方法)
所以我们就可以尝试完成下面的这个题
请实现笔记本电脑使用USB鼠标、USB键盘的例子
- USB接口:包含打开设备、关闭设备功能
- 笔记本类:包含开机功能、关机功能、使用USB设备功能
- 鼠标类:实现USB接口,并具备点击功能
- 键盘类:实现USB接口,并具备输入功能
这里报错的错误信息就是,我的抽象类方法没有被重写,告诉大家个快捷键
Alt+Enter(将光标移动到类名上)
这样就可以快速创建需要重写的方法了。
(1)先声明USB接口:其中规定了要实现USB接口就必须实现接口规定实现的open( )和close( )这两个方法
interface IUSB {//Iusb接口
void open();//打开
void close();//关闭
}
(2)然后在写一个鼠标类和一个键盘类,这两个类都去实现USB接口。(实现其中的方法)
这是鼠标的具体实现
class Mouse implements IUSB {//鼠标类
@Override
public void open() {
System.out.println("打开鼠标");
}
@Override
public void close() {
System.out.println("关闭鼠标");
}
public void click() {//鼠标类自己的方法
System.out.println("点击鼠标");
}
}
这是键盘的具体实现
class Keyboard implements IUSB {//键盘类
@Override
public void open() {
System.out.println("打开键盘");
}
@Override
public void close() {
System.out.println("关闭键盘");
}
public void typing() {//键盘类自己的方法
System.out.println("敲击键盘输入数据");
}
}
那么,现在鼠标和键盘都实现了USB功能,也就是说鼠标和键盘都能够调用USB接口中规定的方法,并且他们实现的方式都不一样。
我们写个测试,具体实现一下(用main函数去调用draw这个方法)
class computer {//电脑
public static void opencomputer() {
System.out.println("打开电脑");
}
public static void closecomputer() {
System.out.println("关闭电脑");
}
public static void draw(IUSB iusb) {
opencomputer();//打开电脑
iusb.open();
if(iusb instanceof Mouse) {
((Mouse) iusb).click();//鼠标这个设备
} else {
((Keyboard) iusb).typing();//键盘这个设备
}
iusb.close();//关闭设备
closecomputer();//关闭电脑
}
}
主类
//测试类
public class Test {
public static void main(String[] args) {
computer.draw(new Mouse());//去调用这个方法
System.out.println("==========");//分隔线
computer.draw(new Keyboard());
}
}
最后输出结果
关键字instanceof
的意思
🍃第二部分
🍂接口的特性(重点)
- 接口类型是一种引用类型,但是不能直接new接口的对象
interface A {//接口
void print();//写入一个抽象方法
}
public class Test1 {
public static void main(String[] args) {
A a=new A() ;//实例化这个接口
}
}
错误信息
2. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
interface A {
// Error:(4, 18) java: 此处不允许使用修饰符private
private void print();
}
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
interface A {
void print() {
System.out.println("我是接口");
}
//编译出错
// Error:(5, 18) java: 接口抽象方法不能带有主体
}
- 重写接口中方法时,不能使用默认的访问权限
interface A {
void print();// 默认是public的
}
class B implements A {
@Override
void print() {
System.out.println("我是子类");
}
//error 正在尝试分配更低的访问权限; 以前为public
// 编译报错,重写A中print()方法时,不能使用默认修饰符
}
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(前面讲到过的,这里再复习一下)
interface A {
int p=10;//默认被public static final
void print();
}
public class Test1 {
public static void main(String[] args) {
System.out.println(A.p); // 可以直接通过接口名访问,说明是静态的
// 编译报错:Error:(12, 12) java: 无法为最终变量p分配值
A.p = 2; // 说明brand具有final属性
}
}
- 接口中不能有静态代码块和构造方法
interface A {
//编译失败
public A {
}
{}//编译失败
}
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
- 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
- jdk8中:接口中还可以包含default方法。
🍃第三部分
🍂实现多个接口
在Java中,但是一个类可以实现多个接口。类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,下面通过类来表示一组动物.
abstract class Animal {//抽象类
String name;
int age;
public Animal(String name, int age) {//抽象类的构造方法
this.name = name;//初始化name
this.age = age;//初始化年龄
}
}
另外我们再提供一组接口,分别是"飞" “跑” “游泳”。
//跑的接口
interface Run {
void run();
}
//飞的接口
interface Fly {
void fly();
}
//游泳的接口
interface Swim {
void swim();
}
我们分别创建具体的动物,分别是狗类,乌龟类,鸭子类
因为我们的狗是会跑的,我们就可以利用接口
//狗类
class Dog extends Animal implements Run{
public dog(String name, int age) {
super(name, age);
}
@Override
public void run() {
System.out.println(name+" 用四条腿跑");
}
}
因为乌龟既可以跑,又可以游泳,所以可以给乌龟类两个接口(可以实现多接口)
//乌龟类
class Tortoise extends Animal implements Run,Swim{
public tortoise(String name, int age) {
super(name, age);
}
@Override
public void run() {
System.out.println(name+" 用四条腿爬");
}
@Override
public void swim() {
System.out.println(name+" 用四条腿扑腾");
}
}
还有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”
//鸭子类
class Duck extends Animal implements Run,Swim,Fly{
public duck(String name, int age) {
super(name, age);
}
@Override
public void run() {
System.out.println(name+" 正在用两条腿跑");
}
@Override
public void fly() {
System.out.println(name+" 正在用翅膀飞");
}
@Override
public void swim() {
System.out.println(name+" 正在飘在水上");
}
主类
public class Test1 {
public static void main(String[] args) {
Dog dog=new Dog("哮天犬",888);
dog.run();
System.out.println("===========================");
Tortoise tortoise=new Tortoise("杰尼龟",88);
tortoise.run();
tortoise.swim();
System.out.println("===========================");
Duck duck=new Duck("唐老鸭",12);
duck.run();
duck.swim();
duck.fly();
}
}
运行结果
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口.
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx特性
例如
狗是一种动物, 具有会跑的特性.
乌龟也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.
🍂接口间的继承
我们先看这串代码,
//继承Run,Swim这两个接口
interface IAmphibious extends Run,Swim {
//可以自己加上属于自己的抽象方法
//也可以不去加,默认就是继承的这两个接口
}
//乌龟类
class Tortoise extends Animal implements IAmphibious{
public Tortoise(String name, int age) {
super(name, age);
}
@Override
public void run() {
System.out.println(name+" 用四条腿爬");
}
@Override
public void swim() {
System.out.println(name+" 用四条腿扑腾");
}
}
public class Test1 {
public static void main(String[] args) {
Tortoise tortoise=new Tortoise("杰尼龟",88);
tortoise.run();
tortoise.swim();
}
}
输出结果
结论:
- 在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的
- 接口可以继承一个接口, 达到复用的效果. 使用
extends
关键字. - 通过接口继承创建一个新的接口
IAmphibious
表示 “两栖的”. 此时实现接口给Tortoise
类, 就需要实现run
方法, 并且还需要实现swim
方法. - 接口间的继承相当于把多个接口合并在一起
🍂接口的标识用法
虽然接口内部定义了一些抽象方法,但是并不是所有的接口内部都必须要有方法,比如CloneNotSupportedException
接口,CloneNotSupportedException
接口的作用是使对象能够“被克隆”,但是CloneNotSupportedException
接口中却没有任何内容,也就是说,如果有一个类需要实现“克隆”的功能,则这个类必须去实现CloneNotSupportedException
接口,但是却并不用实现方法(因为接口中没有方法),此时,这CloneNotSupportedException
接口就仅仅是一个“标识”接口,是用来标志一个类的,标志这个类具有这个“序列化”功能。
🍃第四部分
🍂接口的通俗理解
用我们晚上平时在写作业时经常使用的台灯来举例吧。
比如,我们把亮度设置为一个“标准”,或者说亮度就是一个“接口”,这个接口有一个方法,就是调整亮度,任何台灯如果想称之为可调节亮度的台灯,那么必须实现“亮度”这个接口,也就必须实现“亮度”接口中规定实现的“调节亮度”的方法,这样才算是真正的实现了“亮度”这个接口,实现了“调节亮度”这个功能。
当我们的台灯实现了这个“接口”时,也就具备了这个功能,那我们就可以直接使用一个“调节亮度”的“按键”去命令空调,虽然我们只是使用了调节亮度的这个按键,但是我们的台灯上拥有这个接口,我们通过这个按键去调整亮度也是行得通的。
🍂接口在生活中的思想体现
其实,在我们的生活当中,有很多地方都体现了“接口”的思想。大家平时都经常用到手机吧,手机有苹果的和安卓的类型,但是如果我们想对手机充电呢?那就需要数据线,大家想一想,如果我的每一个手机都需要有一个专门的数据线来充电,那将会多么糟糕啊?并且每次换新手机还需要再去重新买相匹配的数据线。
因此,每个手机为了能够都使用同一款数据线,就将自己的接口改为了市面上普遍的标准,小米也好,华为也好,只要你按照我数据线接口的标准去生产你们的手机充电接口,那么你的手机都能很好的用数据线进行充电,传输信息等操作。
因此,当我们打开某东,想去买数据线时,就不难发现我们需要根据自己手机的品牌来挑选特定的数据线,这样手机才能被我们的数据线正常进行充电等操作。
回到Java上面来说,其实接口给我们带来的最大的好处就是“解耦”了,数据线能够搭配不同的手机,于是便可以实现方便,快捷的效果。在软件系统中也是一样的,接口可以有很多不同“特色”的实现类,我们只需要声明同一个接口,却可以引用很多个该“接口”引申出来的“子类”,这不也大大增强了我们软件系统中组件的灵活性吗?