🎇个人主页:Ice_Sugar_7
🎇所属专栏:快来卷Java啦
🎇欢迎点赞收藏加关注哦!
在面向对象的概念中,所有的对象都是通过类来描绘的,
但是不是所有的类都是用来描绘对象的。如果一个类没有包含足够的信息来描绘一个具体的对象,那它就叫作抽象类
比如说,Animal是动物类,每种动物都有不同的叫声,但由于Animal并不是具体的动物,所以其内部的bark方法没办法实现
而对于Dog类和Cat类,首先猫和狗都属于动物,那么它们和Animal类就是继承关系,而且它们都是具体的动物,所以它们的bark方法可以实现
上一篇文章打印图形的例子中,我们发现,父类 Shape 中的 draw 方法其实啥都没做,绘制图形的工作都是由Shape的子类的draw方法来完成的
public class Shape {
public Shape shape;
public void draw() {
System.out.println("画一个图形");
}
}
像这种没有实际工作的方法,我们可以把它设计成一个抽象方法
(abstract method),包含抽象方法的类我们称为抽象类
abstract class Shape {
int a;
int b = 10;
private double area;
//抽象方法
abstract public void draw();
//可以在抽象类中实现普通方法
public double getArea() { //得到绘制的图形的面积
return this.area;
}
}
在Java中,一个类如果被 abstract
修饰,那就称为抽象类,抽象类中被 abstract
修饰的方法称为抽象方法,抽象方法不用给出具体的实现体(即没有方法体)
抽象类作为一种类,里面自然也可以添加普通方法和成员变量
不能实例化
刚才我们说抽象类无法描绘一个具体的对象,所以它不能实例化对象
抽象方法不能用private修饰
要被子类重写,肯定不能设为私有
抽象方法不能被final和static修饰
因为被final或static修饰的方法不能被重写,但是抽象方法在继承时要求子类重写该方法,所以final、static和abstract是天敌
抽象类必须被继承
,并且继承后子类要重写
父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
抽象类中可以有构造方法
,供子类创建对象时,初始化父类的成员变量
因为抽象类不能实例化,所以要想使用的话,就要创建该抽象类的子类,然后让子类重写抽象类中的抽象方法
不过你可能会说:普通的类也可以被继承呀,普通的方法也可以被重写呀,为啥非得用抽象类和抽象方法呢?
因为使用抽象类相当于多了一重编译器的校验
比如上面画图形的例子,如果使用普通类进行继承,那不小心忘记重写子类的draw方法,那此时编译器是不会报错的
反之,如果是抽象类,编译器就会“逼你”重写
很多语法存在的意义就是为了“预防出错”
比如我们前面学过的 final 就是这样,创建的变量不希望被用户修改,所以加上 final ,这样就能够在不小心被修改的时候,让编译器及时提醒我们
充分利用编译器的校验, 在实际开发中是非常有意义的
如果将抽象类再进一步抽象化,就成了接口
抽象类中可以包含成员变量,但是接口不可以(不过仍然可以包含静态变量)
抽象类中可以实现非抽象方法
,但是接口只能包含方法的声明。如果要实现方法,那只能实现静态方法
下面来看怎么实现一个接口
public interface 接口名称{
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
// 注意:在接口中上述写法都是抽象方法,更推荐方式4,代码更简洁
}
public abstract
编译器会默认给你加上的,所以不用写
关于接口的命名,需要注意:
以大写字母 I 开头
任何修饰符号
,保持代码的简洁性接口有点类似C语言的头文件,因为头文件里面包含的就是函数声明,要具体实现这个函数的话,就得在其他源文件中实现
抽象方法
,也就是说接口中每个方法默认都是public abstract
修饰的。不能改为其他,否则会报错public static final
修饰public interface test2 {
int a = 2;
final public static double b = 3.0;
}
.class
接口不能直接使用,必须要有一个“实现类”来实现该接口中所有抽象方法,实现接口需要用到关键字implements
接口是一种类型,它可以引用实现该接口的具体的类型
public class 类名称 implements 接口名称{
// ...
}
举个例子:
实现笔记本电脑使用USB鼠标和键盘
这里的USB接口就是一个接口
,它提供两种功能:连接设备、断开连接
public interface USB {
void openDevice();
void closeDevice();
}
笔记本类需要根据接入接口的类(鼠标、键盘类),来调用相应的类的功能
public class Computer{
public void powerOn() {
System.out.println("电脑开机");
}
//因为鼠标类和键盘类都实现了USB接口,所以它们都能向上转型传参给usb
public void useService(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 void powerOff() {
System.out.println("电脑关机");
}
}
鼠标和键盘实现基本的功能:
//鼠标类实现USB接口
public class Mouse extends Computer implements USB{
@Override
public void openDevice() {
System.out.println("连接鼠标");
}
public void click() {
System.out.println("使用鼠标点击");
}
@Override
public void closeDevice() {
System.out.println("鼠标已断开连接");
}
}
//键盘类实现USB接口
public class Keyboard extends Computer implements USB{
@Override
public void openDevice() {
System.out.println("连接键盘");
}
public void input() {
System.out.println("使用键盘输入");
}
@Override
public void closeDevice() {
System.out.println("键盘断开连接");
}
}
接口解决了Java不支持多继承的问题
以动物类为例,我们想写一个Dog类来继承动物类,狗会跑,也会游泳。但不是所有动物都会这两种行为,而Java不支持多继承,但是支持一个类实现多个接口,所以我们可以把动物的行为封装成一个个接口
public class Animal {
public String name;
public int age;
}
//接口
public interface IFly {
void Fly();
}
public interface ISwimming {
void swimming();
}
public class Bird extends Animal implements IFly{
public String color;
@Override
public void Fly() {
System.out.println(this.name + "正在振动翅膀飞翔");
}
}
//同时实现两个接口
public class Dog extends Animal implements IRun,ISwimming{
public String color;
@Override
public void run() {
System.out.println(this.name + "正在跑");
}
@Override
public void swimming() {
System.out.println(this.name + "正在游泳");
}
}
小结:
接口类型
作为形参,就可以接收所有实现这个接口的类类型(因为它们传参时会向上转型为接口类型)