Java继承之抽象类
1.什么是抽象类
用术语说:没有足够的信息来描绘一个具体的对象,这样的类称为抽象类。抽象类被继承后可以实现多态
大白话:其实我也不知道他是啥,不过我的理解,它存在的意义就是被继承,跟接口差不多,然后它可以省很多代码
下面看具体代码:
abstract class Shape {
public abstract void draw(); // 抽象方法不能里有具体的语句
}
// 当一个普通类继承一个抽象类的时候,再这个子类中必须重写抽象类中的抽象方法
class Cycle extends Shape {
@Override // 如果不重写会报错,但如果继承的是普通类则不会报错,用抽象类更安全
public void draw() {
System.out.println("画一个圆圈");
}
}
class Flower extends Shape { // 不同的子类对父类的draw方法进行了不同的重写
@Override
public void draw() {
System.out.println("画一朵花");
}
}
class Square extends Shape {
@Override
public void draw() {
System.out.println("画一个正方形");
}
}
public class Test4 {
public static void main(String[] args) {
Cycle cycle = new Cycle(); // 子类引用cycle
Flower flower = new Flower(); // 子类引用flower
Square square = new Square();
// 数组的类型是Shape,即数组中每一个元素都是一个父类引用
// 在这个过程其实也发生了向上转型,对抽象类中的方法进行了重写
Shape[] shapes = {cycle, flower, square}; // 父类引用引用不同的子类对象
for (int i = 0; i < shapes.length; i++) {
Shape shape = shapes[i]; // 父类引用shape指向—>当前所对应的子类对象
shape.draw(); // 通过父类引用调用子类重写的draw方法
}
}
}
运行结果
// 对上面的代码补充一下
// 可能你对 Shape[] shapes = {cycle, flower, square};不太理解
// 但上面的代码就相当于
Shape[] shapes1 = new Shape[3]; // 有三个不同的子类对象呀!数组大小为3
// (将指向->子类对象)的子类引用赋值给父类对象,不就相当于该夫类引用指向->所对应的子类对象吗
//这是向上转型的另一种写法,应为前面已经实例化了子类对象 Cycle cycle = new Cycle();
shapes1[0] = cycle; // 如果前面没实例化子类对象,就要写成shape1[0] = new Cycle
shapes1[1] = flower;
shapes1[2] = square;
调用同一个方法打印出了不同的结果,用术语就叫多态
2.什么是接口
接口可以看作一种更加抽象的抽象类。
接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体(执行的语句,输出之类的),是由全局常量和公共的抽象方法所组成。
例如:
//接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口
public interface 接口名称{
// 定义变量
int a = 10; // 接口当中的成员变量默认都是public static final
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
void method2(); // 接口当中的成员方法默认都是public abstract, 更推荐用第二种来定义方法
}
就像普通类实现抽象类一样,一个类实现某个接口则必须实现该接口中的抽象方法,否则该类必须被定义为抽象类。
3.接口一般用在什么地方?
一般情况下,实现类和它的抽象类之间具有 “is-a” 的关系,但是如果我们想达到同样的目的,但是又不存在这种关系时,使用接口。
由于 Java 中单继承的特性,导致一个类只能继承一个类,但是可以实现一个或多个接口,此时可以使用接口。
接口的正确用法,实现多继承,代码如下:
由于 Java 中单继承的特性,导致一个类只能继承一个类,但是可以实现一个或多个接口,此时可以使用接口。
class Animal {
String name; // 不能使用private,后面的子类也要用
public Animal(String name) { // 父类的自定义的构造方法
this.name = name;
}
}
interface IFlying { // 自定义多种接口
void fly();
}
interface IRunning {
void run();
}
interface ISwimming {
void swimming();
}
// 小鸭子,不仅会跑,还会游泳、飞行
一个类继承父类,并实现多个接口,间接的解决java中不能多继承的问题
class Duck extends Animal implements IRunning, ISwimming, IFlying {
public Duck(String name) { // 子类构造方法
super(name); // 必须在子类构造方法的第一行
// 在给实现子类的构造方法前,先要用super()调用实现父类的构造方法,比较先有父后有子呀!
// 因为父类自己定义了构造方法,编译器不会自动给给子类构造方法中添加super();来实现父类的构造方法,需要我们自己实现
}
// 对接口中的抽象方法进行重写
@Override
public void fly() {
System.out.println(this.name + "正在用翅膀飞");
}
@Override
public void run() {
System.out.println(this.name + "正在用两条腿跑");
}
@Override
public void swimming() {
System.out.println(this.name + "正在漂在水上");
}
}
public class 接口的使用 { // 不用学我用中文名作为类名,我只是为演示方便
public static void main(String[] args) {
Duck duck = new Duck("第一个小鸭子"); // 实例化鸭子对象
duck.fly(); // 通过引用 变量名.方法名 输出重写后的方法
duck.run();
duck.swimming();
}
}
有人可能会说干嘛用接口,我直接在父类Animal中实现fly、run、swimming这些属性,
然后不同的动物子类再继承父类这些方法不行吗?
但问题是,鸭子会fly、swimming,那猫会飞和游泳吗?你再写个其他动物的子类是不是就不行了
而用接口呢?我们只是把这种飞、游泳的行为给抽象出来了,
只要一个子类有这种行为,他就可以实现相对应的接口,接口是更加灵活的
结果如下
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多个接口。
继承表达的含义是 is-a 语义, 而接口表达的含义是具有 xxx 特性 ,能实现接口的类和该接口并不一定有is-a的关系,只要该类有这个接口的特性就行。
举个例子:
猫是一种动物, 具有会跑的特性.
青蛙也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
所以这个东西可以让程序员忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,只要这个类有有这个特性就好。
举个例子(例子中的IRunning接口用的是上一个代码定义的):
class Robot implements IRunning {
private String name;
public Robot(String name) {
this.name = name;
}
// 对run方法进行重写
@Override
public void run() {
System.out.println("机器人" + this.name + "正在跑");
}
}
public class Test4 {
public static void main(String[] args) {
Robot robot1 = new Robot("图图");
robot1.run();
}
}
// 执行结果
机器人图图正在跑
只要能跑就行,管他是机器人还是动物呢
同时在实际的开发过程中,一般来说,一个类是一个Java文件,一个接口也是一个Java文件。
4.抽象类和接口的区别(暂时还不太懂,我先记下来)
Java中接口和抽象类的定义语法分别为interface与abstract关键字。
相同点:都不能被实例化 ,接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点:(1)抽象类中的抽象方法的修饰符只能为public或者protected,默认为public;接口中的方法默认使用public修饰
(2)接口成员变量默认为public static final,必须赋初值,不能被修改。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;
(3)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(4)接口强调特定功能的实现,而抽象类强调所属关系。
(5)抽象类可以包含方法、构造方法,方法可以实现,但是构造方法不能用于实例化,主要用途是被子类调用。接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体
参考文献:https://blog.csdn.net/weixin_61061381/article/details/124042390?spm=1001.2014.3001.5506,感谢小鱼儿!