基本概念
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
优点:通过继承可以快速创建新的类,实现代码的重用,提高程序的可维护性,节省大量创建新类的时间,提高开发效率和开发质量。
如何实现
Java中通过extends关键字申明一个类是从另一个类继承而来:
class 父类{
... //字段、方法
}
class 子类 extends 父类{
... //字段、方法
}
继承类型
Java不支持多继承,但支持多重继承

继承的特点
- 子类拥有父类非 private 的字段、方法。(注意对于这点的理解,子类其实继承了父类的private字段,但是不能直接访问,这是由封装性决定的,可以去看封装性中四种权限修饰符的权限表)
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。(即重写override,这是运行时多态的条件之一,下面会写运行时多态的实现)
继承中的构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
重写父类的方法
子类继承了父类中的所有成员及方法,但在某种情况下,子类中该方法所表示的行为与其父类中该方法所表示的行为不完全相同,例如,在父类语言中定义了说话这个方法,而在子类中说话的方法是不同的:外国人说英文,中国人说中文,这时我们就需要重写或隐藏父类的该方法。
重写规则:重写的方法具有与其所重写的方法相同的名称、参数数量、类型和返回值。
重写可以实现运行时多态。
继承的例子
下面用用一个例子来具体说明java中的继承。
这里以银行卡作为背景,首先定义一个存储卡类,其属性为余额,方法为取钱,取钱时判断余额是否足够,具体定义如下:
public class Acount {
private double balance; //余额
public Acount(double initMoney){
balance = initMoney; //设置初始余额
}
public void withdraw(double amt){
if(balance >= amt){ //如果当前余额大于取款
System.out.print("您已取出" + amt + "元,");
balance -= amt;
System.out.println("目前余额为" + balance + "元。");
}
else{
System.out.println("卡上余额不足,请及时充值");
}
}
public double getBalance(){
return this.balance;
}
public void setBalance(double money){
this.balance = money;
}
}
定义一个信用卡类,该类继承自存储卡,并增加属性透支额度,在信用卡类中重写了父类的取钱方法,在取钱时除了判断余额是否足够,在余额不足时还可以考虑透支额度,具体实现如下:
public class CheckAcount extends Acount { //信用卡类,继承自普通卡
private double protectedBy; //透支额度
private double overdraw = 0; //当前透支
public CheckAcount(double initMoney, double protectedBy){
super(initMoney); //super调用超类的构造器
this.protectedBy = protectedBy;
}
public void withdraw(double amt){
if(this.getBalance() >= amt){
System.out.print("您已取出" + amt + "元,");
this.setBalance(this.getBalance() - amt);
System.out.println("目前余额为" + this.getBalance() + "元。");
}
else{
if(this.getBalance() != 0){ //如果余额没用完
amt -= this.getBalance(); //把余额用掉
this.setBalance(0);
}
this.overdraw += amt;
if(this.protectedBy >= this.overdraw){
System.out.println("您的余额已不足,目前透支" + this.overdraw + "元,请及时充值");
}
else{ //余额和透支额度都不足
this.overdraw -= amt; //记得把刚刚的钱减掉
System.out.println("您的余额已不足,透支达到额度,取钱操作失败,请及时充值");
}
}
}
public double getProtectedBy(){
return protectedBy;
}
public void setProtectedBy(double protectedBy){
this.protectedBy = protectedBy;
}
}
下面是测试程序:
public class AcountTest {
public static void main(String[] args) {
Acount guest = new Acount(10000); //设置余额10000
guest.withdraw(5000); //取5000,剩5000
guest.withdraw(5000); //取5000,剩0
guest.withdraw(5000); //余额不足
System.out.println("");
CheckAcount guest2 = new CheckAcount(10000, 10000); //设置余额10000,透支额度10000
guest2.withdraw(5000); //取5000,余额5000
guest2.withdraw(3000); //取3000,余额2000
guest2.withdraw(5000); //取5000,余额不够,取出所有余额,再透支3000
guest2.withdraw(5000); //取5000,透支8000
guest2.withdraw(5000); //取5000,透支达到额度,取不了钱
guest2.withdraw(2000); //取2000,透支10000
guest2.withdraw(2000); //取2000,透支达到额度,取不了钱
System.out.println("");
Acount guest3 = new CheckAcount(10000, 10000); //父类指向子类对象,上溯造型
//guest3.getProtectedBy(); 编译错误,编译器认为guest3是Acount类
guest3.withdraw(15000); //运行时多态,调用了子类中的withdraw方法
}
}
运行结果:

这里说明一下测试程序中的上溯造型,这个术语缘于继承关系图的传统画法:将基类至于顶部,而向下发展的就是派生类。
简单来说,上溯造型就是把子类向上转换为父类类型Acount guest3 = new CheckAcount();
,这样转型后只能使用父类中已经声明过的方法而不能通过转换后的父类调用子类特有的方法。如果你想使用子类中存在而父类中不存在的方法,编译不能通过,由此可见,上溯造型是安全的类型转换。
可以从结果中看到guest3调用了CheckAcount类中的withdraw方法,这里面就有个对象的类型识别问题,也就是运行时多态。
多态也是面向对象的三大特性之一,多态可以分为编译时多态和运行时多态,编译时多态主要指方法的重载,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
运行时多态有三个条件:
- 继承
- 重写
- 上溯造型