第七章:继承家族的秘密
继承家族拜访
清晨,林景然跟随元始真人来到面向对象山脉的另一座山峰。这座山峰比对象帮所在的山峰更加高耸,山腰处云雾缭绕,山顶隐约可见一座古老的建筑群。
"这是继承峰,"元始真人指着山峰说道,“在这里居住着’继承家族’,他们世代传承武功,精通扩展和改良武学的奥秘。今天,你将学习Java武林中的继承概念。”
他们沿着蜿蜒的山路向上攀登。路上,元始真人解释道:“在面向对象编程中,继承是一种强大的机制,它允许我们基于现有的类创建新的类。通过继承,子类可以获得父类的属性和方法,同时还可以添加自己的特性或修改继承的行为。”
林景然若有所思:“所以,继承就像武功的传承,弟子继承师父的武功,但可以在此基础上发展自己的特点?”
"正是如此,"元始真人赞许道,“继承建立了类之间的’是一种’关系。例如,‘猫是一种动物’,‘轿车是一种车辆’。在Java中,我们使用extends
关键字来表示这种关系。”
他在空中画出一个简单的继承关系图:
父类(基类)
- ↑*
- |*
子类(派生类)
"父类提供基本功能,子类继承这些功能并可以扩展或修改它们,"元始真人解释,“这种机制促进了代码重用,减少了重复代码,并建立了类之间的层次结构。”
随着海拔的升高,云雾逐渐散去,一座古朴典雅的庄园出现在眼前。庄园大门上方悬挂着"继承家族"的牌匾,门前站着几位身着不同颜色长袍的武者,他们的武功招式看似相同,却又各有特色。
"准备好了吗?"元始真人问道,“在这里,你将深入了解继承的奥秘,学习如何扩展现有的类,以及如何重写方法来改变继承的行为。”
林景然深吸一口气,坚定地点头:“我已经准备好了。”
继承长老的教诲
进入继承家族的庄园,林景然被带到一个宽敞的大厅。大厅中央坐着一位白发苍苍的老者,他身着金色长袍,面容慈祥而威严,给人一种历经沧桑却充满智慧的感觉。
"这位是继承长老,继承家族的族长,"元始真人介绍道,“他精通继承的奥秘,将教你如何通过继承扩展现有的类。”
继承长老微微点头:“欢迎来到继承家族。我听说你已经学习了类和对象的基本概念,那么你已经具备了学习继承的条件。”
他站起身,走到大厅一侧的墙壁前。墙上挂着一幅巨大的家谱图,上面记录着继承家族的世代传承。
"继承是面向对象编程的核心概念之一,"继承长老开始讲解,“它允许一个类(子类)获得另一个类(父类)的属性和方法。这种机制促进了代码重用,减少了重复代码,并建立了类之间的层次结构。”
他指着家谱图上的一个分支:"在Java中,我们使用extends
关键字来表示继承关系。例如:
public class 子类 extends 父类 {
- // 子类的属性和方法*
}
通过这种方式,子类继承了父类的所有公开(public)和受保护(protected)的成员,包括属性和方法。"
继承长老拿出一个卷轴,展示了一个具体的例子:
// 父类
public class 武者 {
- protected String 姓名;*
- protected int 等级;*
- protected int 生命值;*
- protected int 力量;*
-
- public 武者(String 姓名, int 等级, int 生命值, int 力量) {*
-
this.姓名 = 姓名;*
-
this.等级 = 等级;*
-
this.生命值 = 生命值;*
-
this.力量 = 力量;*
- }*
-
- public void 攻击() {*
-
System.out.println(姓名 + "使用普通攻击,造成" + 力量 + "点伤害");*
- }*
-
- public void 显示信息() {*
-
System.out.println("武者:" + 姓名 + ",等级:" + 等级 + ",生命值:" + 生命值);*
- }*
}
// 子类
public class 战士 extends 武者 {
- private int 防御力;*
-
- public 战士(String 姓名, int 等级, int 生命值, int 力量, int 防御力) {*
-
super(姓名, 等级, 生命值, 力量); // 调用父类的构造方法*
-
this.防御力 = 防御力;*
- }*
-
- public void 盾牌格挡() {*
-
System.out.println(姓名 + "使用盾牌格挡,增加" + 防御力 + "点防御");*
- }*
-
- @Override*
- public void 显示信息() {*
-
super.显示信息(); // 调用父类的方法*
-
System.out.println("防御力:" + 防御力);*
- }*
}
"注意到super
关键字了吗?"继承长老问道,“它用于调用父类的构造方法和方法。在子类的构造方法中,如果父类没有无参构造方法,我们必须显式调用父类的构造方法,使用super(参数列表)
。”
他还指出了@Override
注解:“这个注解表示子类重写(覆盖)了父类的方法。重写允许子类提供父类方法的特定实现,这是多态的基础,我们稍后会详细讨论。”
林景然尝试理解这个例子:“所以,战士类继承了武者类的所有属性和方法,同时添加了自己的防御力属性和盾牌格挡方法,还重写了显示信息方法来包含防御力信息?”
"完全正确,"继承长老赞许道,“这就是继承的强大之处。我们可以重用父类的代码,同时根据需要添加新功能或修改现有功能。”
他接着创建了另一个子类:
public class 法师 extends 武者 {
- private int 魔法值;*
- private int 智力;*
-
- public 法师(String 姓名, int 等级, int 生命值, int 力量, int 魔法值, int 智力) {*
-
super(姓名, 等级, 生命值, 力量);*
-
this.魔法值 = 魔法值;*
-
this.智力 = 智力;*
- }*
-
- public void 施放法术(String 法术名) {*
-
int 伤害 = 智力 * 2;*
-
System.out.println(姓名 + "施放" + 法术名 + ",造成" + 伤害 + "点魔法伤害");*
-
魔法值 -= 10;*
- }*
-
- @Override*
- public void 攻击() {*
-
System.out.println(姓名 + "使用法杖攻击,造成" + (力量 + 智力 / 2) + "点伤害");*
- }*
-
- @Override*
- public void 显示信息() {*
-
super.显示信息();*
-
System.out.println("魔法值:" + 魔法值 + ",智力:" + 智力);*
- }*
}
"现在我们有了两个子类:战士和法师,它们都继承自武者类,但各自添加了特定的属性和方法,"继承长老解释,“这展示了继承如何帮助我们组织和扩展代码。”
林景然尝试创建和使用这些类的对象:
武者 普通武者 = new 武者(“张三”, 1, 100, 10);
战士 勇猛战士 = new 战士(“李四”, 5, 200, 15, 20);
法师 智慧法师 = new 法师(“王五”, 5, 150, 8, 100, 25);
普通武者.攻击();
普通武者.显示信息();
勇猛战士.攻击();
勇猛战士.盾牌格挡();
勇猛战士.显示信息();
智慧法师.攻击();
智慧法师.施放法术(“火球术”);
智慧法师.显示信息();
"通过这个例子,你可以看到继承如何帮助我们创建更加专业化的类,"继承长老总结道,“每个子类都继承了父类的基本功能,同时添加了自己的特性。这种方式使得代码更加模块化和可维护。”
方法重写与多态
学习了继承的基本概念后,继承长老带领林景然来到一个名为"重写道场"的地方。这个道场中,多位武者正在演练相似却又各具特色的武功招式。
"在Java中,子类可以重写(覆盖)父类的方法,提供自己的实现,"继承长老解释,“这是多态的基础,允许不同的对象对相同的消息做出不同的响应。”
他展示了方法重写的规则:
“重写方法必须具有与父类方法相同的名称、参数列表和返回类型(或其子类型)。重写方法的访问修饰符不能比父类方法更严格(例如,不能将父类的public方法重写为private)。重写方法不能抛出比父类方法更广泛的检查异常。”
继承长老演示了一个多态的例子:
武者[] 武者数组 = new 武者[3];
武者数组[0] = new 武者(“张三”, 1, 100, 10);
武者数组[1] = new 战士(“李四”, 5, 200, 15, 20);
武者数组[2] = new 法师(“王五”, 5, 150, 8, 100, 25);
for (武者 某武者 : 武者数组) {
- 某武者.攻击(); // 调用各自的攻击方法*
- 某武者.显示信息(); // 调用各自的显示信息方法*
}
"这就是多态的威力,"继承长老解释,“虽然我们使用相同的方法调用(攻击和显示信息),但根据对象的实际类型,会调用相应的实现。这使得代码更加灵活和可扩展。”
他特别强调了一点:“Java中的方法调用是基于对象的实际类型(运行时类型),而不是引用的类型(编译时类型)。这就是为什么上面的例子中,尽管引用类型都是武者,但调用攻击方法时会执行各自类的实现。”
林景然尝试理解这个概念:“所以,多态让我们能够编写更通用的代码,处理不同类型的对象,而不需要知道它们的具体类型?”
方法重写与多态(续)
"完全正确,"继承长老赞许道,“多态是面向对象编程的核心优势之一,它增强了代码的灵活性和可扩展性。通过继承和方法重写,我们可以创建通用的代码,处理不同类型的对象,而不需要为每种类型编写特定的代码。”
他进一步解释了多态的实际应用:“想象一个游戏系统,需要处理不同类型的角色。通过多态,我们可以创建一个通用的方法来处理所有角色,而不需要为每种角色类型编写特定的代码。这大大简化了系统设计,使代码更加简洁和可维护。”
林景然思考了一下:“所以,多态让我们能够’面向接口编程,而不是面向实现编程’?”
"你领悟得很快!"继承长老惊喜地说,“这正是面向对象设计的一个重要原则。通过依赖抽象(父类或接口)而不是具体实现(子类),我们可以创建更加灵活和可扩展的系统。”
他补充道:“方法重写是实现多态的关键机制。通过重写父类的方法,子类可以提供特定的实现,同时保持与父类相同的方法签名。这使得我们可以通过父类引用调用方法,但实际执行的是子类的实现。”
林景然尝试总结:“所以,继承建立了’是一种’关系,方法重写允许子类提供特定实现,而多态则让我们能够以统一的方式处理不同类型的对象。这三者共同构成了面向对象编程的核心机制。”
"非常准确的总结!"继承长老赞许道,“你已经很好地理解了这些概念的关系和重要性。”
超类宝库与Object类
离开重写道场,继承长老带领林景然来到一个名为"超类宝库"的神秘建筑。这是一座金碧辉煌的宝库,中央放置着一个巨大的水晶台座,台座上悬浮着一本散发着金光的古老书籍。
"这是’Object经’,记载着Java武林中最基础、最重要的类——Object类的奥秘,"继承长老解释,“在Java中,Object类是所有类的超类(父类)。如果一个类没有明确指定它的父类,那么它默认继承自Object类。”
他轻轻触碰那本悬浮的书籍,书页自动翻开,展示出Object类的核心方法:
"Object类提供了一些所有对象都共有的基本方法,"继承长老解释,"这些方法包括:
equals(Object obj)
:比较两个对象是否相等hashCode()
:返回对象的哈希码toString()
:返回对象的字符串表示getClass()
:返回对象的运行时类clone()
:创建并返回对象的副本finalize()
:对象被垃圾回收前调用wait()
,notify()
,notifyAll()
:用于线程同步"
他特别强调了前三个方法的重要性:“在实际开发中,我们经常需要重写equals()
、hashCode()
和toString()
方法,以提供适合特定类的实现。”
继承长老展示了如何重写这些方法:
public class 武器 {
- private String 名称;*
- private int 伤害值;*
-
- public 武器(String 名称, int 伤害值) {*
-
this.名称 = 名称;*
-
this.伤害值 = 伤害值;*
- }*
-
- // 重写equals方法*
- @Override*
- public boolean equals(Object obj) {*
-
if (this == obj) return true; // 自反性*
-
if (obj == null || getClass() != obj.getClass()) return false; // 类型检查*
-
*
-
武器 other = (武器) obj; // 类型转换*
-
return 伤害值 == other.伤害值 && 名称.equals(other.名称); // 属性比较*
- }*
-
- // 重写hashCode方法*
- @Override*
- public int hashCode() {*
-
return Objects.hash(名称, 伤害值); // 使用Objects工具类计算哈希码*
- }*
-
- // 重写toString方法*
- @Override*
- public String toString() {*
-
return "武器{名称='" + 名称 + "', 伤害值=" + 伤害值 + "}";*
- }*
}
"重写equals()
方法时,我们需要遵循一些规则,"继承长老解释,“它应该是自反的(x.equals(x)返回true)、对称的(如果x.equals(y)返回true,那么y.equals(x)也应返回true)、传递的(如果x.equals(y)返回true且y.equals(z)返回true,那么x.equals(z)也应返回true)和一致的(多次调用返回相同结果)。”
他接着说:“重写equals()
方法时,通常也应该重写hashCode()
方法,确保相等的对象具有相同的哈希码。这对于使用哈希表的集合类(如HashMap和HashSet)尤为重要。”
林景然尝试理解这些概念:“所以,Object类提供了所有对象共有的基本行为,而我们可以根据需要重写这些方法,提供更适合特定类的实现?”
"正是如此,"继承长老点头,“这体现了Java的一个重要设计理念:通过继承提供通用功能,通过重写允许定制行为。”
他补充道:“理解Object类及其方法对于深入掌握Java至关重要。特别是在使用集合框架和进行对象比较时,正确重写equals()
和hashCode()
方法是确保程序正确性的关键。”
实战:角色继承系统
学习了继承的基本概念和Object类后,继承长老给林景然布置了一个实际任务:“现在,请你设计一个角色继承系统,展示继承和多态的应用。”
任务要求设计一个基础的角色
类和多个子类(如战士
、法师
、弓箭手
等),实现以下功能:
- 基础角色类包含所有角色共有的属性和方法
- 子类添加特定的属性和方法,并重写必要的方法
- 使用多态展示不同角色的行为差异
- 正确重写Object类的方法
林景然思考片刻,开始设计这个系统:
首先,他定义了基础的角色
类:
public class 角色 {
- protected String 名称;*
- protected int 等级;*
- protected int 生命值;*
- protected int 最大生命值;*
- protected int 经验值;*
-
- public 角色(String 名称, int 等级) {*
-
this.名称 = 名称;*
-
this.等级 = 等级;*
-
this.最大生命值 = 等级 * 100;*
-
this.生命值 = 最大生命值;*
-
this.经验值 = 0;*
- }*
-
- public void 攻击(角色 目标) {*
-
int 伤害 = 计算伤害();*
-
目标.受到伤害(伤害);*
-
System.out.println(名称 + "攻击了" + 目标.名称 + ",造成" + 伤害 + "点伤害");*
- }*
-
- protected int 计算伤害() {*
-
return 等级 * 10; // 基础伤害计算*
- }*
-
- public void 受到伤害(int 伤害) {*
-
生命值 = Math.max(0, 生命值 - 伤害);*
-
if (生命值 == 0) {*
-
System.out.println(名称 + "被击败了!");*
-
}*
- }*
-
- public void 获得经验(int 经验) {*
-
经验值 += 经验;*
-
System.out.println(名称 + "获得了" + 经验 + "点经验");*
-
检查升级();*
- }*
-
- protected void 检查升级() {*
-
int 升级所需经验 = 等级 * 100;*
-
if (经验值 >= 升级所需经验) {*
-
经验值 -= 升级所需经验;*
-
等级++;*
-
最大生命值 = 等级 * 100;*
-
生命值 = 最大生命值;*
-
System.out.println(名称 + "升级了!当前等级:" + 等级);*
-
检查升级(); // 递归检查,处理一次获得多级的情况*
-
}*
- }*
-
- public void 显示状态() {*
-
System.out.println("角色:" + 名称);*
-
System.out.println("等级:" + 等级);*
-
System.out.println("生命值:" + 生命值 + "/" + 最大生命值);*
-
System.out.println("经验值:" + 经验值);*
- }*
-
- @Override*
- public String toString() {*
-
return "角色{名称='" + 名称 + "', 等级=" + 等级 + ", 生命值=" + 生命值 + "/" + 最大生命值 + "}";*
- }*
-
- @Override*
- public boolean equals(Object obj) {*
-
if (this == obj) return true;*
-
if (obj == null || getClass() != obj.getClass()) return false;*
-
角色 other = (角色) obj;*
-
return 等级 == other.等级 && 名称.equals(other.名称);*
- }*
-
- @Override*
- public int hashCode() {*
-
return Objects.hash(名称, 等级);*
- }*
}
然后,他创建了几个子类:
public class 战士 extends 角色 {
- private int 力量;*
- private int 防御力;*
-
- public 战士(String 名称, int 等级, int 力量, int 防御力) {*
-
super(名称, 等级);*
-
this.力量 = 力量;*
-
this.防御力 = 防御力;*
-
this.最大生命值 = 等级 * 150; // 战士有更高的生命值*
-
this.生命值 = 最大生命值;*
- }*
-
- @Override*
- protected int 计算伤害() {*
-
return 等级 * 10 + 力量 * 5; // 战士的伤害受力量影响*
- }*
-
- @Override*
- public void 受到伤害(int 伤害) {*
-
int 实际伤害 = Math.max(1, 伤害 - 防御力); // 防御力减免伤害*
-
super.受到伤害(实际伤害);*
- }*
-
- public void 盾牌格挡() {*
-
System.out.println(名称 + "举起盾牌进行格挡,防御力临时提高" + (防御力 / 2));*
- }*
-
- @Override*
- public void 显示状态() {*
-
super.显示状态();*
-
System.out.println("力量:" + 力量);*
-
System.out.println("防御力:" + 防御力);*
- }*
}
实战:角色继承系统(续)
public class 法师 extends 角色 {
- private int 智力;*
- private int 魔法值;*
- private int 最大魔法值;*
-
- public 法师(String 名称, int 等级, int 智力) {*
-
super(名称, 等级);*
-
this.智力 = 智力;*
-
this.最大魔法值 = 等级 * 50 + 智力 * 10;*
-
this.魔法值 = 最大魔法值;*
-
this.最大生命值 = 等级 * 80; // 法师生命值较低*
-
this.生命值 = 最大生命值;*
- }*
-
- @Override*
- protected int 计算伤害() {*
-
return 等级 * 5 + 智力 * 3; // 法师的基础攻击较弱*
- }*
-
- public void 施放法术(角色 目标, String 法术名) {*
-
int 魔法消耗 = 20;*
-
if (魔法值 >= 魔法消耗) {*
-
魔法值 -= 魔法消耗;*
-
int 法术伤害 = 等级 * 10 + 智力 * 8; // 法术伤害很高*
-
目标.受到伤害(法术伤害);*
-
System.out.println(名称 + "施放" + 法术名 + ",对" + 目标.名称 + "造成" + 法术伤害 + "点伤害");*
-
} else {*
-
System.out.println(名称 + "魔法值不足,无法施放法术");*
-
}*
- }*
-
- public void 恢复魔法(int 恢复量) {*
-
魔法值 = Math.min(最大魔法值, 魔法值 + 恢复量);*
-
System.out.println(名称 + "恢复了" + 恢复量 + "点魔法值,当前魔法值:" + 魔法值);*
- }*
-
- @Override*
- public void 显示状态() {*
-
super.显示状态();*
-
System.out.println("智力:" + 智力);*
-
System.out.println("魔法值:" + 魔法值 + "/" + 最大魔法值);*
- }*
}
public class 弓箭手 extends 角色 {
- private int 敏捷;*
- private int 命中率;*
- private int 箭矢数量;*
-
- public 弓箭手(String 名称, int 等级, int 敏捷, int 命中率) {*
-
super(名称, 等级);*
-
this.敏捷 = 敏捷;*
-
this.命中率 = 命中率;*
-
this.箭矢数量 = 20; // 初始箭矢数量*
-
this.最大生命值 = 等级 * 100; // 标准生命值*
-
this.生命值 = 最大生命值;*
- }*
-
- @Override*
- protected int 计算伤害() {*
-
return 等级 * 8 + 敏捷 * 4; // 弓箭手的伤害受敏捷影响*
- }*
-
- @Override*
- public void 攻击(角色 目标) {*
-
if (箭矢数量 > 0) {*
-
箭矢数量--;*
-
// 计算是否暴击*
-
boolean 暴击 = Math.random() * 100 < 命中率;*
-
int 伤害 = 计算伤害();*
-
if (暴击) {*
-
伤害 *= 2;*
-
System.out.println("暴击!");*
-
}*
-
目标.受到伤害(伤害);*
-
System.out.println(名称 + "射出一箭,对" + 目标.名称 + "造成" + 伤害 + "点伤害,剩余箭矢:" + 箭矢数量);*
-
} else {*
-
System.out.println(名称 + "箭矢用尽,无法攻击");*
-
}*
- }*
-
- public void 补充箭矢(int 数量) {*
-
箭矢数量 += 数量;*
-
System.out.println(名称 + "补充了" + 数量 + "支箭矢,当前箭矢数量:" + 箭矢数量);*
- }*
-
- public void 快速射击(角色 目标) {*
-
System.out.println(名称 + "使用快速射击...");*
-
for (int i = 0; i < 3 && 箭矢数量 > 0; i++) {*
-
攻击(目标);*
-
}*
- }*
-
- @Override*
- public void 显示状态() {*
-
super.显示状态();*
-
System.out.println("敏捷:" + 敏捷);*
-
System.out.println("命中率:" + 命中率);*
-
System.out.println("箭矢数量:" + 箭矢数量);*
- }*
}
设计完成后,林景然测试了这个角色继承系统:
// 创建不同类型的角色
角色 普通角色 = new 角色(“路人甲”, 1);
战士 勇猛战士 = new 战士(“铁手”, 5, 20, 15);
法师 智慧法师 = new 法师(“星辰”, 5, 25);
弓箭手 灵敏弓手 = new 弓箭手(“疾风”, 5, 22, 30);
// 展示多态
角色[] 角色数组 = {普通角色, 勇猛战士, 智慧法师, 灵敏弓手};
for (角色 某角色 : 角色数组) {
- 某角色.显示状态(); // 调用各自的显示状态方法*
- System.out.println();*
}
// 战斗模拟
勇猛战士.攻击(普通角色);
勇猛战士.盾牌格挡();
智慧法师.施放法术(勇猛战士, “火球术”);
智慧法师.恢复魔法(50);
灵敏弓手.快速射击(智慧法师);
灵敏弓手.补充箭矢(10);
// 经验和升级
勇猛战士.获得经验(500); // 足够升级
勇猛战士.显示状态();
系统运行良好,不同类型的角色展示了各自特有的行为,同时共享了基础角色类的通用功能。多态机制使得可以通过基类引用调用方法,但执行的是各个子类的特定实现。
继承长老对林景然的设计非常满意:“你很好地应用了继承和多态的概念,创建了一个结构清晰、功能丰富的角色系统。特别是你正确地重写了Object类的方法,并展示了如何通过多态处理不同类型的对象。”
林景然思考了一下:“通过这个实战练习,我更深入地理解了继承和多态的实际应用。继承让我们能够重用代码并建立类之间的层次结构,而多态则提供了处理不同对象的统一接口,使代码更加灵活和可扩展。”
"正是如此,"继承长老点头,“继承和多态是面向对象编程的核心机制,掌握它们将大大提升你的编程能力。”
继承家族之悟
黄昏时分,林景然和元始真人站在继承峰的高处,俯瞰整个面向对象山脉。夕阳的余晖洒在山峰上,给这片神秘的领域增添了几分庄严感。
"今天,你学习了面向对象编程的第二个核心概念:继承,"元始真人总结道,“继承建立了类之间的’是一种’关系,允许子类获得父类的属性和方法,同时添加自己的特性或修改继承的行为。”
林景然点头:“我理解了继承的强大之处,它促进了代码重用,减少了重复代码,并建立了类之间的层次结构。特别是通过方法重写和多态,我们可以创建更加灵活和可扩展的系统。”
"继承是一把双刃剑,"元始真人提醒道,“虽然它提供了强大的代码重用机制,但过度使用或不当使用继承可能导致紧密耦合和脆弱的设计。在实际开发中,我们需要谨慎使用继承,有时组合可能是更好的选择。”
他接着说:“还有一个重要的概念是’里氏替换原则’,它指出子类对象应该能够替换父类对象使用,而不影响程序的正确性。这是设计良好继承层次结构的关键原则。”
林景然若有所思:“所以,继承不仅仅是语法机制,还涉及到设计原则和最佳实践。”
"正是如此,"元始真人赞许道,“在面向对象设计中,我们需要考虑类之间的关系和交互,确保系统既灵活又可维护。”
他指着远处的另一座山峰:“明天,你将拜访’多态剑客’,深入学习多态的奥秘,以及接口的概念和应用。这将是你面向对象之旅的又一个重要里程碑。”
林景然恭敬地行礼:“弟子明白,期待明天的学习。”
夜间总结
回到住处,林景然拿出他的"武功笔记",记录下今天学到的知识:
-
继承的概念和语法:
- 继承建立了类之间的"是一种"关系
- 使用
extends
关键字表示继承关系:public class 子类 extends 父类 { ... }
- 子类继承父类的所有公开(public)和受保护(protected)的成员
- 继承促进代码重用,减少重复代码,建立类层次结构
-
父类和子类:
- 父类(基类)提供基本功能
- 子类(派生类)继承这些功能,可以添加新功能或修改现有功能
- 子类可以访问父类的protected成员,但不能访问private成员
-
super关键字:
- 用于调用父类的构造方法:
super(参数列表);
- 用于调用父类的方法:
super.方法名(参数列表);
- 在子类构造方法中,如果父类没有无参构造方法,必须显式调用父类的构造方法
- 用于调用父类的构造方法:
-
方法重写(覆盖):
- 子类提供父类方法的特定实现
- 重写方法必须具有相同的名称、参数列表和返回类型(或其子类型)
- 重写方法的访问修饰符不能比父类方法更严格
- 使用
@Override
注解标记重写方法(推荐但非必需)
-
多态:
- 允许不同的对象对相同的消息做出不同的响应
- 方法调用基于对象的实际类型(运行时类型),而不是引用的类型(编译时类型)
- 通过父类引用可以调用子类重写的方法
- 增强代码的灵活性和可扩展性
-
Object类:
- Java中所有类的超类(父类)
- 提供所有对象共有的基本方法
- 重要方法包括
equals()
、hashCode()
、toString()
等 - 重写
equals()
方法时通常也应重写hashCode()
方法
-
继承的最佳实践:
- 谨慎使用继承,避免过度使用
- 遵循里氏替换原则
- 考虑组合作为继承的替代方案
- 设计清晰的类层次