第十一章
繼承
11.1 簡介繼承
繼承圖
添加子類別方法
public class SavingsAccount extends BankAccount
{
public SavingsAccount(double rate)
{
interestRate = rate;
}
public void addInterest()
{
double interest = getBalance() * interestRate / 100;
deposit(interest);
}
private double interestRate;
}
子類別物件Layout
SavingsAccount 的個體變數 balance 是繼承自 BankAccount 類別, 個體變數 interestRate 則是新增的。
Syntax 11.1: 繼承
|
class SubclassName extends SuperclassName
{
methods
instance fields
}
|
例:
|
public class SavingsAccount extends BankAccount
{
public SavingsAccount(double rate)
{
interestRate = rate;
}
public void addInterest()
{
double interest = getBalance() * interestRate / 100;
deposit(interest);
}
private double interestRate;
}
|
用途:
To define a new class that inherits from an existing class, and define
the methods and instance fields that are added in the new class.
|
11.2 繼承階層
- 類別, 子類別(subclass), 子類別的子類別等等形成階層是常見的
- 例如: Swing 階層。 Swing UI 元件部分階層圖如下,
- 設計類別階層時, 將共用的資料和方法放在父類別(superclass)。
例如 JComponent 有方法 getWidth 和
getHeight。
- 比較特殊的放在子類別。
例如 AbstractButton 具有方法以設定或取得按鈕的文字和圖示(icon)
- 以下用簡單的銀行帳戶階層說明。
銀行帳戶階層
例如銀行提供下列兩種帳戶:
- 支票帳戶沒有利息, 每月基本的來往次數是免費的, 額外的來往每次都要付費。
- 儲蓄帳戶有利息, 每月複利計息。
類別階層設計
- 所有帳戶都提供 getBalance, deposit, 和 withdraw 方法。
- 支票帳戶必須記錄來往次數。
- 支票帳戶需要 deductFees 方法, 扣除月費, 並重設來往次數。
- 支票帳戶的 deposit 和 withdraw 方法必須重新設計, 以使計算來往次數。
- 儲蓄帳戶需要 addInterest 方法計息。
11.3 繼承個體資料和方法
繼承方法
- 覆寫(override)方法: 就父類別中的方法, 提供不同的實作。
- 繼承方法: 不提供父類別中的方法新的實作。
- 添加方法: 提供父類別所沒有的方法
繼承個體資料
- 繼承資料: 父類別所有個體資料項都自動繼承
- 添加個體資料: 提供父類別沒有的新資料
- 不能覆寫(override)個例資料
CheckingAccount 類別
繼承的個體資料是私有的
啟用父類別的方法
Syntax 11.2: 呼用父類別的方法
|
super.methodName(parameters)
|
例:
|
public void deposit(double amount)
{
transactionCount++;
super.deposit(amount);
} |
用途:
To call a method of the superclass instead of the method of the current
class |
11.4 父類別的建構
public class CheckingAccount extends BankAccount
{
public CheckingAccount(double initialBalance)
{
// construct superclass
super(initialBalance);
// initialize transaction count
transactionCount =0;
}
...
}
- 藉由傳參數, 使用父類別 BankAccount 的建構式, 設定起初的存款金額 initialBalance。
- BankAccount 的建構式必須是子類別欲建構式的第一個敘述
Syntax 11.3: 呼用父類別的建構式
|
ClassName(parameters)
{
super(parameters);
. . .
} |
例:
|
public CheckingAccount(double initialBalance)
{
super(initialBalance);
transactionCount =0;
}
|
用途:
To invoke a constructor of the superclass. Note that this statement
must be the first statement of the subclass constructor. |
11.5 子類別換成父類別
多型
- 一般性的方法:
public void transfer(double amount, BankAccount other)
{
withdraw(amount);
other.deposit(amount);
}
- 適用於任一種銀行帳戶 (普通, 支票, 或儲蓄帳戶)
- 假如要把款項從一帳戶轉入支票帳戶:
momsAccount.transfer(1000, harrysChecking);
- 型式為 BankAccount 的參數 other, 可以代入其子類別 CheckingAccount 物件的參考 harrysChecking
- 注意多型(polymorphism):
other.deposit(amount)
呼用 CheckingAccount.deposit (並收取來往費用)
- 為什麼不乾脆宣告參數為 Object?
- Object 類別並沒有 deposit 方法
File AccountTest.java
/**
This program tests the BankAccount class and
their subclasses.
*/
public class AccountTest
{
public static void main(String[] args)
{
SavingsAccount momsSavings
= new SavingsAccount(0.5);
CheckingAccount harrysChecking
= new CheckingAccount(100);
momsSavings.deposit(10000);
momsSavings.transfer(harrysChecking, 2000);
harrysChecking.withdraw(1500);
harrysChecking.withdraw(80);
momsSavings.transfer(harrysChecking, 1000);
harrysChecking.withdraw(400);
// simulate end of month
momsSavings.addInterest();
harrysChecking.deductFees();
System.out.println("Mom's savings balance = $"
+ momsSavings.getBalance());
System.out.println("Harry's checking balance = $"
+ harrysChecking.getBalance());
}
}
File BankAccount.java
/**
A bank account has a balance that can be changed by
deposits and withdrawals.
*/
public class BankAccount
{
/**
Constructs a bank account with a zero balance
*/
public BankAccount()
{
balance = 0;
}
/**
Constructs a bank account with a given balance
@param initialBalance the initial balance
*/
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
/**
Deposits money into the bank account.
@param amount the amount to deposit
*/
public void deposit(double amount)
{
balance = balance + amount;
}
/**
Withdraws money from the bank account.
@param amount the amount to withdraw
*/
public void withdraw(double amount)
{
balance = balance - amount;
}
/**
Gets the current balance of the bank account.
@return the current balance
*/
public double getBalance()
{
return balance;
}
/**
Transfers money from the bank account to another account
@param other the other account
@param amount the amount to transfer
*/
public void transfer(BankAccount other, double amount)
{
withdraw(amount);
other.deposit(amount);
}
private double balance;
}
File CheckingAccount.java
/**
A checking account that charges transaction fees.
*/
public class CheckingAccount extends BankAccount
{
/**
Constructs a checking account with a given balance
@param initialBalance the initial balance
*/
public CheckingAccount(int initialBalance)
{
// construct superclass
super(initialBalance);
// initialize transaction count
transactionCount = 0;
}
public void deposit(double amount)
{
transactionCount++;
// now add amount to balance
super.deposit(amount);
}
public void withdraw(double amount)
{
transactionCount++;
// now subtract amount from balance
super.withdraw(amount);
}
/**
Deducts the accumulated fees and resets the
transaction count.
*/
public void deductFees()
{
if (transactionCount > FREE_TRANSACTIONS)
{
double fees = TRANSACTION_FEE *
(transactionCount - FREE_TRANSACTIONS);
super.withdraw(fees);
}
transactionCount = 0;
}
private int transactionCount;
private static final int FREE_TRANSACTIONS = 3;
private static final double TRANSACTION_FEE = 2.0;
}
File SavingsAccount.java
/**
An account that earns interest at a fixed rate.
*/
public class SavingsAccount extends BankAccount
{
/**
Constructs a bank account with a given interest rate
@param rate the interest rate
*/
public SavingsAccount(double rate)
{
interestRate = rate;
}
/**
Adds the earned interest to the account balance.
*/
public void addInterest()
{
double interest = getBalance() * interestRate / 100;
deposit(interest);
}
private double interestRate;
}
11.6 存取控制層次
- public
- private
- protected (子類別和套件都可存取)
- 套件存取 (預設方式, 不用存取修飾詞)
建議使用的存取控制層次
- 個體資料項: 使用 private
- 例外(exception): public static final 常數
- 方法: public 或 private
- 類別: public 或 package
- 不用 protected
- 注意意外的套件存取 (如忘了用forgetting public 或
private)
11.7 Object: 無所不包的父類別
- 所有類別延伸自 Object
- 最有用的方法:
- String toString()
- boolean equals(Object otherObject)
- Object clone()
Object 類別是每一 Java 類別的父類別
覆寫 toString 方法
覆寫 equals 方法
指到兩個內容相同的物件的兩個參考
指到同一物件的兩個參考
覆寫 clone 方法
- 複製物件參考得到同一物件的兩個參考:
BankAccount account2 = account1;
- 有時需要複製物件, 可使用 clone 方法:
BankAccount account2 = (BankAccount)account1.clone();
- 必須 cast 回值, 因為回值的型式是 Object
- 界定 clone 方法:
public Object clone()
{
BankAccount cloned = new BankAccount();
cloned.balance = balance;
return cloned;
}
- Warning: This approach doesn't work with inheritance--see Advanced
Topic 11.6