Java Tutorial 第二堂(3)方法、類別與套件
|
Java Tutorial 第二堂(2)容器與流程語法 << 前情 雖然在 Java Tutorial 第二堂(1)中談過,Java 中並非每個值都是物件,不過 Java 基本上是個以物件導向為主要典範的語言,任何程式都以定義類別為出發點,即使只是個「哈囉!世界!」也不例外: public class HelloWorld {
public static void main(String[] args) {
System.out.println("哈囉!世界!");
}
}
不過這看來不太像物件導向,若只是這樣的需求,似乎也不需要動用到物件導向,如果 Java 具有其他語言中函式(Function)的概念,不是可以更簡單一些,呼叫個 基本上,無論採用何種典範,關鍵在於架構程式時應思考的幾個重點,像是…
只有在腦海中清楚地思考過這幾個重點,才能在程式語言中採用適當的機制來加以實現,或者是在程式語言不支援時,想辦法自行實作類似機制,像是在 JavaScript 中,即使沒有名稱空間及套件機制,仍有開發者依各自需求實現各種風格的機制,來解決對應的問題。 幸運地,Java 中對於這幾個思考重點,在實作時提供的機制算是為完整,提供了像是靜態方法、類別(Class)與套件等支援。 靜態方法不管你願不願意,想撰寫第一個可執行的 Java 程式,就一定得接觸靜態方法,因為程式進入點就規定一定得是 ... Integer max1 = a > b ? a : b; ... Integer max2 = x > y ? x : y; ... 可以使用靜態方法來封裝程式片段,將流程中引用不同數值或變數的部份設計為參數,例如: /* 存為 Math.java */
class Math {
static Integer max(Integer a, Integer b) {
return a > b ? a : b;
}
}
靜態方法得定義在類別之中,如此一來,就可以在其他地方透過類別名稱來呼叫靜態方法,例如 ... Integer max1 = Math.max(a, b); ... Integer max2 = Math.max(x, y); ... 就某些程度上,包括靜態方法的類別充當了名稱空間,就像是 Python 中模組之作用,而類別中的靜態方法,就像是 Python 中的函式,而函式是一種抽象,對流程的抽象,因此如上定義了 Java 中有一些 API,就是以這樣的概念來實現,像是 那麼 Java 中的靜態方法,就只是函式的概念嗎?不!不只是這樣的,這篇文章稍後,馬上就可以看到靜態方法的其他應用… 類別如果只是將類別拿來當作靜態方法的名稱空間,並不是什麼物件導向,那類別的應用場合呢?…嗯…在你想要表達一組相關聯的數據時,例如,若你想表達帳戶資料,而帳戶有名稱、帳號與餘額,為了易於操作,可定義類別將它們視為一個整體: /* 存為 Account.java */
class Account {
String name;
String number;
Integer balance;
}
這麼一來,就使用 /* 存為 Bank.java */
class Bank {
static Account account(String name, String number, Integer balance) {
Account acct = new Account();
acct.name = name;
acct.number = number;
acct.balance = balance;
return acct;
}
static void deposit(Account acct, Integer amount) {
if(amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
acct.balance += amount;
}
static void withdraw(Account acct, Integer amount) {
if(amount > acct.balance) {
throw new RuntimeException("balance not enough");
}
acct.balance -= amount;
}
static String toStr(Account acct) {
return String.format("Account(%s, %s, %d)",
acct.name, acct.number, acct.balance);
}
}
當中是有關於帳戶建立、存款、提款等函式,你會這麼使用: public class Main {
public static void main(String[] args) {
Account acct = Bank.account("Java", "001", 100);
Bank.deposit(acct, 500);
Bank.withdraw(acct, 200);
System.out.println(Bank.toStr(acct));
}
}
實際上, class Account {
private String name;
private String number;
private Integer balance;
Account(String name, String number, Integer balance) {
this.name = name;
this.number = number;
this.balance = balance;
}
void deposit(Integer amount) {
if(amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
this.balance += amount;
}
void withdraw(Integer amount) {
if(amount > this.balance) {
throw new RuntimeException("balance not enough");
}
this.balance -= amount;
}
String toStr() {
return String.format("Account(%s, %s, %d)",
this.name, this.number, this.balance);
}
}
在 我們希望客戶端必須透過 如此定義之後,客戶端在使用上就容易得多了… public class Main {
public static void main(String[] args) {
Account acct = new Account("Java", "001", 100);
acct.deposit(500);
acct.withdraw(200);
System.out.println(acct.toStr());
}
}
是的!容易使用!在討論物件導向時,大家總是愛談可重用性(Reusability),然而要談到重用性的話,函式的重用性還高上許多,在考量物件導向時,易用性(Usability)其實才是它的重點。 套件假設現在你有一些 .java 與編譯完成的檔案,別人同樣也有一堆 .java 與 .class 檔案,你們的檔案現在得放在同一專案中,那麼檔案名稱衝突是有可能發生的,最好是為你們的 .java、.class 檔案分別開設目錄;另一方面,只使用外部類別充當名稱空間,也不是好的作法,當多個名稱空間階層時,就會有許多不便。 使用 Java 時,你可以在原始碼開頭使用 package tw.codedata.bank;
public class Account {
private String name;
private String number;
private Integer balance;
public Account(String name, String number, Integer balance) {
this.name = name;
this.number = number;
this.balance = balance;
}
public void deposit(Integer amount) {
if(amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
this.balance += amount;
}
public void withdraw(Integer amount) {
if(amount > this.balance) {
throw new RuntimeException("balance not enough");
}
this.balance -= amount;
}
public String toString() {
return String.format("Account(%s, %s, %d)",
this.name, this.number, this.balance);
}
}
使用 注意,程式中宣告了 在類別中還定義了 假設你在先前的 Main.java 中宣告套件: package tw.codedata;
import tw.codedata.bank.Account;
public class Main {
public static void main(String[] args) {
Account acct = new Account("Java", "001", 100);
acct.deposit(500);
acct.withdraw(200);
System.out.println(acct);
}
}
因為類別全名為 練習 7:運用類別與套件來組織程式 在練習用的檔案中,有個 exercises/exercise7/Bank 目錄,這個目錄符合 Gradle 架構,裏頭草草寫了一些類別與靜態方法,以及執行結果輸出的程式碼,請利用這邊介紹的類別與套件等語法,來重新組織當中可重用的程式碼,讓它們可以位於 最後,你完成的程式在實體架構上,應該會像是以下的圖片示意(如果不知道怎麼完成實作,記得參考練習用檔案中 solutions/exercise7/Bank 的成果 ): 再看靜態方法實際上,Java 的靜態方法並非只是將外部類別作為名稱空間,常見的運用之一是將靜態方法用來隱藏物件實作與建構細節,像是 Guava 的 類似的應用還有實現單例(Singleton),例如 Java 中的 public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
// 略 ...
}
建構式被設為 第二堂時間差不多到了,休息一下,接下來的第三堂課要來認識 Java 的 IDE、社群、文件以及更多的 API … |

Java 學習之路





