java继承extends_Java继承,第1部分:extends关键字

java继承extends

Java通过继承和组合支持类重用。 本教程分为两部分,教您如何在Java程序中使用继承。 在第1部分中,您将学习如何使用extends关键字从父类派生子类,调用父类的构造函数和方法以及重写方法。 在第2部分中,您将游览java.lang.Object ,这是Java的超类,其他所有类都从该类继承。

要完成对继承的学习,请务必查看我的Java技巧,其中解释了何时使用composition vs继承 。 您将了解为什么组合是继承的重要补充,以及如何使用组合来防止Java程序中的封装问题。

下载
下载本教程中示例应用程序的源代码。 由Jeff Friesen为JavaWorld创建。

Java继承:两个例子

继承是软件开发人员用来在类别之间建立is-a关系的一种编程结构。 继承使我们能够从更通用的类别中派生出更具体的类别。 较具体的类别较一般的类别的一种。 例如,支票帐户是一种您可以在其中进行存款和取款的帐户。 同样,卡车是一种用于运输大型物品的车辆。

继承可以分为多个级别,从而导致类别更加具体。 例如,图1显示了从车辆继承的汽车和卡车。 从汽车继承的旅行车; 和从卡车继承的垃圾车。 箭头从较具体的“子”类别(较低)指向较不具体的“父”类别(较高)。

jw继承p1 fig1 杰夫·弗里森

图1.一对继承层次结构植根于通用工具类别

此示例说明了单个继承 ,其中子类别从一个直接父类别继承状态和行为。 相反, 多重继承使子类别可以继承两个或多个直接父类别的状态和行为。 图2中的层次结构说明了多重继承。

jw继承p1 fig2 杰夫·弗里森

图2.气垫船乘以陆地车辆和水上车辆类别

类别按类别进行描述。 Java通过类扩展支持单一继承,其中一个类通过扩展该类直接从另一类继承可访问的字段和方法。 但是,Java不支持通过类扩展进行多重继承。

查看继承层次结构时,您可以通过菱形图案的存在轻松地检测到多个继承。 图2在车辆,陆地车辆,水上车辆和气垫船的背景下显示了这种模式。

extend关键字

Java通过extends关键字支持类扩展。 如果存在,则extends指定两个类之间的父子关系。 下面我使用extends来建立类VehicleCar之间的关系,然后建立AccountSavingsAccount之间的关系:

清单1. extends关键字指定父子关系
class Vehicle
{
   // member declarations
}
class Car extends Vehicle
{
   // inherit accessible members from Vehicle
   // provide own member declarations
}
class Account
{
   // member declarations
}
class SavingsAccount extends Account
{
   // inherit accessible members from Account
   // provide own member declarations
}

extends关键字在类名之后和另一个类名之前指定。 extends前的类名标识子级, extends后的类名标识父级。 extends后无法指定多个类名,因为Java不支持基于类的多重继承。

这些示例将is-a关系SavingsAccountCar 专门的VehicleSavingsAccount 专门的AccountVehicleAccount被称为基类父类超类CarSavingsAccount被称为派生类子类子类

期末课程

您可以声明一个不应扩展的类。 例如出于安全原因。 在Java中,我们使用final关键字来防止某些类被扩展。 只需在类标头前面加上final ,如final class Password 。 给定此声明,如果有人尝试扩展Password ,则编译器将报告错误。

子类从其父类和其他祖先继承可访问的字段和方法。 但是,它们从不继承构造函数。 而是,子类声明其自己的构造函数。 此外,他们可以声明自己的领域和方法,以区别于父母。 考虑清单2。

清单2. Account父类
class Account
{
   private String name;

   private long amount;

   Account(String name, long amount)
   {
      this.name = name;
      setAmount(amount);
   }

   void deposit(long amount)
   {
      this.amount += amount;
   }

   String getName()
   {
      return name;
   }

   long getAmount()
   {
      return amount;
   }

   void setAmount(long amount)
   {
      this.amount = amount;
   }
}

清单2描述了具有名称和初始金额的通用银行帐户类,它们都在构造函数中设置。 此外,它还允许用户进行存款。 (您可以通过存入负数进行提款,但是我们会忽略这种可能性。)请注意,在创建帐户时必须设置帐户名称。

代表货币价值

几分钱。 您可能更喜欢使用doublefloat来存储货币值,但是这样做可能会导致不准确。 为了获得更好的解决方案,请考虑BigDecimal ,它是Java标准类库的一部分。

清单3给出了一个SavingsAccount子类,该子类扩展了其Account父类。

清单3. SavingsAccount子类扩展了其Account父类
class SavingsAccount extends Account
{
   SavingsAccount(long amount)
   {
      super("savings", amount);
   }
}

SavingsAccount类很简单,因为它不需要声明其他字段或方法。 但是,它确实声明了一个构造函数,用于初始化其Account超类中的字段。 初始化发生在通过Java的super关键字调用Account的构造函数,然后是带有括号的参数列表。

何时何地调用super()

就像this()必须是构造函数中调用同一类中另一个构造函数的第一个元素一样, super()必须是构造函数中调用其父类中的构造函数的第一个元素。 如果违反此规则,编译器将报告错误。 如果编译器在方法中检测到super()调用,则还将报告错误。 仅在构造函数中调用super()

清单4用CheckingAccount类进一步扩展了Account

清单4. CheckingAccount子类扩展了其Account父类
class CheckingAccount extends Account
{
   CheckingAccount(long amount)
   {
      super("checking", amount);
   }

   void withdraw(long amount)
   {
      setAmount(getAmount() - amount);
   }
}

CheckingAccountSavingsAccount实质性多一点,因为它声明了withdraw()方法。 注意此方法对setAmount()getAmount()的调用, CheckingAccount继承自Account 。 您不能直接访问Accountamount字段,因为该字段被声明为private (请参见清单2)。

super()和无参数构造函数

如果未在子类构造函数中指定super() ,并且如果超类未声明no-argument构造函数,则编译器将报告错误。 这是因为当不存在super()时,子类构造函数必须调用no-argument超类构造函数。

类层次结构示例

我创建了一个AccountDemo应用程序类,可让您试用Account类的层次结构。 首先看一下AccountDemo的源代码。

清单5. AccountDemo演示了帐户类的层次结构
class AccountDemo
{
   public static void main(String[] args)
   {
      SavingsAccount sa = new SavingsAccount(10000);
      System.out.println("account name: " + sa.getName());
      System.out.println("initial amount: " + sa.getAmount());
      sa.deposit(5000);
      System.out.println("new amount after deposit: " + sa.getAmount());

      CheckingAccount ca = new CheckingAccount(20000);
      System.out.println("account name: " + ca.getName());
      System.out.println("initial amount: " + ca.getAmount());
      ca.deposit(6000);
      System.out.println("new amount after deposit: " + ca.getAmount());
      ca.withdraw(3000);
      System.out.println("new amount after withdrawal: " + ca.getAmount());
   }
}

清单5中的main()方法首先演示了SavingsAccount ,然后是CheckingAccount 。 假设Account.javaSavingsAccount.javaCheckingAccount.javaAccountDemo.java源文件位于同一目录中,请执行以下任一命令来编译所有这些源文件:

javac AccountDemo.java
javac *.java

执行以下命令以运行该应用程序:

java AccountDemo

您应该观察以下输出:

account name: savings
initial amount: 10000
new amount after deposit: 15000
account name: checking
initial amount: 20000
new amount after deposit: 26000
new amount after withdrawal: 23000

方法覆盖(和方法重载)

子类可以重写 (替换)继承的方法,以便改为调用子类的方法版本。 覆盖方法必须指定与被覆盖方法相同的名称,参数列表和返回类型。 为了演示,我在下面的Vehicle类中声明了print()方法。

清单6.声明要重写的print()方法
class Vehicle
{
   private String make;
   private String model;
   private int year;

   Vehicle(String make, String model, int year)
   {
      this.make = make;
      this.model = model;
      this.year = year;
   }

   String getMake()
   {
      return make;
   }

   String getModel()
   {
      return model;
   }

   int getYear()
   {
      return year;
   }

   void print()
   {
      System.out.println("Make: " + make + ", Model: " + model + ", Year: " +
                         year);
   }
}

接下来,我在Truck类中重写print()

清单7.在Truck子类中重写print()
class Truck extends Vehicle
{
   private double tonnage;

   Truck(String make, String model, int year, double tonnage)
   {
      super(make, model, year);
      this.tonnage = tonnage;
   }

   double getTonnage()
   {
      return tonnage;
   }

   void print()
   {
      super.print();
      System.out.println("Tonnage: " + tonnage);
   }
}

Truckprint()方法具有相同的名称,返回类型和参数列表Vehicleprint()方法。 还要注意, Truckprint()方法首先通过为super.加上前缀来调用Vehicleprint()方法super. 方法名称。 首先执行超类逻辑然后执行子类逻辑通常是一个好主意。

从子类方法调用超类方法

为了从覆盖的子类方法中调用超类方法,请在方法名称前加上保留字super和成员访问运算符。 否则,您将最终以递归方式调用子类的重写方法。 在某些情况下,子类将通过声明同名字段来掩盖非private超类字段。 您可以使用super和成员访问运算符来访问非private超类字段。

为了完成此示例,我摘录了VehicleDemo类的main()方法:

Truck truck = new Truck("Ford", "F150", 2008, 0.5);
System.out.println("Make = " + truck.getMake());
System.out.println("Model = " + truck.getModel());
System.out.println("Year = " + truck.getYear());
System.out.println("Tonnage = " + truck.getTonnage());
truck.print();

最后一行, truck.print(); ,调用truckprint()方法。 此方法首先调用Vehicleprint()来输出卡车的品牌,型号和年份; 然后输出卡车的吨位。 输出的这一部分如下所示:

Make: Ford, Model: F150, Year: 2008
Tonnage: 0.5

使用final阻止方法覆盖

出于安全性或其他原因,有时您可能需要声明一个不应重写的方法。 您可以为此使用final关键字。 为避免覆盖,只需在方法标头之前加上final ,就像final String getMake() 。 如果有人试图在子类中重写此方法,则编译器将报告错误。

方法重载与覆盖

假设您用以下代码替换了清单7中的print()方法:

void print(String owner)
{
   System.out.print("Owner: " + owner);
   super.print();
}

修改后的Truck类现在具有两个print()方法:前面的显式声明的方法和从Vehicle继承的方法。 void print(String owner)方法不会覆盖Vehicleprint()方法。 而是,它使它过载

您可以通过在子类的方法标头前面添加@Override批注来检测是否尝试重载而不是在编译时覆盖方法:

@Override
void print(String owner)
{
   System.out.print("Owner: " + owner);
   super.print();
}

指定@Override告诉编译器给定的方法将覆盖另一个方法。 如果有人尝试重载该方法,则编译器将报告错误。 没有此注释,编译器将不会报告错误,因为方法重载是合法的。

何时使用@Override

养成使用@Override为覆盖方法加前缀的习惯。 这种习惯将帮助您更快地发现超载错误。

翻译自: https://www.infoworld.com/article/2987426/java-101-inheritance-in-java-part-1.html

java继承extends

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值