抽象类
1. 什么是抽象类和抽象方法?
- 多个子类抽取共性时,无法确定方法体,就把父类中的方法定义为抽象。
- 强制让子类按照某种格式重写。因为继承抽象类要么子类也是抽象类,要么子类必须重写父类中的抽象方法
- 抽象方法所在的类必须是抽象类
2. 抽象类和抽象方法的格式?
- public abstract 返回值类型 方法名 (参数列表);
- public abstract class 类名
注意:抽象方法没有方法体,即没有花括号和里面的内容
3. 继承抽象类有哪些要注意?
- 抽象类不能实例化()
- 继承抽象类时,要么重写抽象类中的所有抽象方法
- 要么子类也是一个抽象类,或者我们也可以借助adapter(中间体)重写父类的所有抽象方法,然后让子类继承该类,这样子类就不用重写全部抽象方法,只需要重写自己独有的
4. 为什么抽象类不能实例化?
因为抽象类本身就是 “不完整、未完工” 的类,Java 不允许创建一个不完整的对象。
1. 从语法定义看
抽象类用abstract修饰,它的设计初衷就是:
只做父类,用来被继承,不能自己独立存在
Java语法直接规定:
- new 抽象类() → 编译报错
- 只有普通类才能被实例化
2. 从逻辑上看:抽象类是“半成品”
抽象类里可以包含抽象方法:
abstract class Animal {
// 只有声明,没有方法体
public abstract void eat();
}
- eat() 只是一个约定,没有具体实现
- 如果能 new Animal(),那调用 eat() 时,JVM 根本不知道要执行什么代码
- 所以 Java 直接禁止这种逻辑上不完整的对象被创建
3. 从设计目的看
抽象类的作用是:
- 抽取子类的通用结构
- 强制子类必须实现某些方法
- 本身不代表任何具体事物
比如:
- Animal 是抽象的
- 真正存在的是 Dog、Cat
- 你不能造出一个 “抽象的动物”,只能造出具体的狗、猫
4. 那抽象类有构造方法,为什么还不能实例化?
很多人疑惑:
抽象类可以写构造方法,那为什么不能 new?
因为:
- 抽象类的构造方法不是给自己用的
- 是给子类构造时调用的(子类 super())
- 子类实例化时,会先初始化抽象父类的成员,(构造方法只是用来初始化父类的成员变量,而不是只是用来创建对象,创建对象需要包含7个完整的步骤)
- 但抽象类本身依然不能单独 new 出来
另外:
- 抽象类不一定要有抽象方法
- 但抽象方法一定要写成抽象类
不过根据抽象类的设计初衷,通常抽象类都会有抽象方法,否则没有存在的必要
抽象类的应用场景
如果你不知道具体是哪个子类,比如:
Father f = getOneAnimal(); // 可能返回 Son、Daughter、GrandSon...
你不知道它是谁,你没法强转
这时候抽象类就派上用场了。
抽象类的真正作用:统一调用,不用关心具体是谁
不管子类是谁,我都能统一调用同一个方法
abstract class Father {
public abstract void work();
}
class Son extends Father {
@Override
public void work() {
System.out.println("儿子敲代码");
}
}
class Daughter extends Father {
@Override
public void work() {
System.out.println("女儿画图");
}
}
写代码的时候就可以直接用父类调用方法:
Father f = new Son();
f.work();
f = new Daughter();
f.work();
- 不需要强转
- 不需要判断类型
- 不需要知道它是谁
这就是多态 + 抽象类的威力:
统一接口,自动适配不同子类
那强转和抽象类的区别是什么?
强转 = 你要“特殊功能”
你要调用子类独有方法,必须强转。
抽象类 = 你要“统一功能”
你要调用所有子类都必须实现的规范方法,
直接用父类引用调用即可,不用强转。
一句话终极总结
- 想调用子类独有功能 → 必须强转
- 想统一调用所有子类共同的行为 → 用抽象类 / 多态
它们不是替代关系,而是:
抽象类管 “通用规范”
强转管 “特殊需求”
有了抽象类能调用的是父类已经声明过、子类重写了的方法,不是子类自己独有的方法:
父类已经写了这个方法,编译器认为“有这个方法”,所以放行
运行时看右边(子类)
真正执行时,JVM去找子类的实现
这才叫多态
强转和抽象类的作用区分
- 抽象类的目的是子类对方法进行了重写,父类中的方法没有实际用处,因此父类中只定义该方法,将方法和类抽象,从而实现统一调用接口。
- 而强转则是为了实现子类中特有的功能,父类中并不存在该方法的定义,编译时无法在父类中找到,这时才去将父类强制类型转换成子类类型,从而编译的时候直接从子类中获取该子类独有的方法
抽象类有构造方法
在Java中,抽象类的构造方法不能直接通过new关键字实例化抽象类本身,但它的核心作用是供子类继承时调用,用于初始化抽象类中定义的成员变量。实际是用于初始化子类的构造方法,因为子类继承了父类的成员变量,在子类的构造方法中通过super()来初始化父类中的成员变量
1. 定义方式:抽象类的构造方法定义和普通类一致,可写无参或有参构造。
2. 子类调用要求:再子类的构造方法必须通过super()显式或隐式调用抽象类的额构造方法,否则编译报错
3. 与接口的区别:接口没有构造方法,因为接口不能定义成员变量,也不存在继承初始化的需求。
浙公网安备 33010602011771号