Java 初學者指南:搞懂 this 與 static,不再霧裡看花【Thinking in Java筆記(4-2)】
在學 Java 的時候,有些關鍵字像是 this
跟 static
,常常出現在各種範例中,但說真的,很多人一開始只是照抄能跑就好,根本沒搞懂為什麼要這樣寫。本篇文章會用簡單易懂的方式帶你弄懂這兩個關鍵字,還會搭配一些實際的比喻,讓你讀完以後,不會只是“懂用”,而是真的“懂它為什麼這樣設計”
this
關鍵字是什麼?它不是你以為的那個“這個”
多個物件在呼叫同一個方法時怎麼分辨誰是誰?
想像你有兩根香蕉(是的,香蕉),它們各自有皮要被剝:
public class BananaPeel {
public static void main(String[] args) {
Banana a = new Banana(),
b = new Banana();
a.peel(1);
b.peel(2);
}
}
這邊 a.peel(1)
到底是誰在呼叫 peel
?其實 Java 編譯器會偷偷幫你轉成:
Banana.peel(a, 1);
Banana.peel(b, 2);
雖然你不能真的這樣寫,但概念就是:它偷偷傳入了「是誰在叫這個方法」的資訊。這個資訊,就叫 this
所以 this
是什麼?它就是「目前這個物件的自己」。在方法內,如果你寫 this.doSomething()
,就是在說「嘿,我自己做這件事」
方法裡呼叫其他方法要用 this
嗎?
不一定。例如:
public class Apricot {
void pick() { /* ... */ }
void pit() { pick(); /* 或 this.pick(); 都可以 */ }
}
如果你很懶(像我),你可以省略 this
。Java 會自動幫你補上。但如果你想要讓語意更明確,或者要回傳整個物件本身時,就非 this
不可
回傳自己:鏈式操作的關鍵
在 Java 中有一種常見的寫法叫做 method chaining(方法鏈式調用),我們來透過 this
關鍵字理解它的運作
先看一般的版本:
public class Leaf {
int i = 0;
Leaf increment() {
i++;
return this;
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increment();
x.increment();
x.increment();
x.print();
}
}
這種寫法每次呼叫 increment()
都是獨立的動作,一步一步執行,最後印出結果。每次呼叫 increment()
,裡面其實都有 return this;
,只是我們沒有拿它來做後續的動作
那如果我們要把這三次加法串在一起呢?就變成下面這樣:
public class Leaf {
int i = 0;
Leaf increment() {
i++;
return this;
}
void print() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
Leaf x = new Leaf();
x.increment().increment().increment().print();
}
} /* Output:
i = 3
*/
這段可以拆成三個步驟來理解:
- 每次呼叫
increment()
方法,物件裡的i
會加 1 - 然後這個方法會回傳
this
,也就是目前這個物件本身 - 所以你可以接著繼續呼叫
increment()
或其他方法,形成類似「鏈條」的連續呼叫
這樣的寫法叫做 method chaining(方法鏈式調用),常見於像是設定值或建構者模式裡
如果用生活比喻來說,就像你到便利商店結帳時說:「給我一個袋子、沒有會員、載具、line pay」,你不是每講一句就結一次帳,而是一次把所有事情連續講完
透過 this
回傳自己,我們就能做到這樣的連續指令,讓語意更清楚、程式更有彈性
把自己傳給別人用?也可以!
class Person {
public void eat(Apple apple) {
Apple peeled = apple.getPeeled();
System.out.println("Yummy");
}
}
class Peeler {
static Apple peel(Apple apple) {
// ... remove peel
return apple; // Peeled
}
}
class Apple {
Apple getPeeled() { return Peeler.peel(this); }
}
public class PassingThis {
public static void main(String[] args) {
new Person().eat(new Apple());
}
} /* Output:
Yummy
*/
在這裡,Apple 物件在呼叫 getPeeled()
方法時,使用 this
將自己作為參數傳給 Peeler.peel()
方法。這樣做的意思是:「我要叫 Peeler 來處理我自己」
這個 this
代表的就是目前這顆 Apple 物件,也就是正在被呼叫方法的那一個實例。等於是 Apple 自己主動把自己丟進削皮機,請求處理。這種方式讓物件本身能夠參與流程,不只是被動的資料容器
就像在說:「師傅,這就是我,麻煩幫我剝皮一下,謝謝」
建構子裡的 this()
:喚起你家另一位建構子的召喚術
在 Java 中,一個類別可以有多個建構子(constructor),這些建構子的用途是用來建立物件並初始化變數的。不過如果你發現每個建構子裡都有一堆重複的程式碼,那就該思考怎麼讓它們彼此合作
Java 提供了 this()
的語法,讓你可以在一個建構子裡直接呼叫同一個類別的另一個建構子,避免重複碼,提升維護性
以下這個範例展示了四種不同的建構子,它們彼此之間透過 this(...)
做協同:
public class Flower {
int petalCount = 0;
String s = "initial value";
Flower(int petals) {
petalCount = petals;
print("Constructor w/ int arg only, petalCount= "
+ petalCount);
}
Flower(String ss) {
print("Constructor w/ String arg only, s = " + ss);
s = ss;
}
Flower(String s, int petals) {
this(petals);
//! this(s); // Can't call two!
this.s = s; // Another use of "this"
print("String & int args");
}
Flower() {
this("hi", 47);
print("default constructor (no args)");
}
void printPetalCount() {
//! this(11); // Not inside non-constructor!
print("petalCount = " + petalCount + " s = "+ s);
}
public static void main(String[] args) {
Flower x = new Flower();
x.printPetalCount();
}
} /* Output:
Constructor w/ int arg only, petalCount= 47
String & int args
default constructor (no args)
petalCount = 47 s = hi
*/
在上述範例中,我們看到一個建構子裡用 this(...)
去呼叫同一類別的其他建構子,這樣的寫法讓我們可以重複利用邏輯,而不是每個建構子裡都寫一遍重複的程式碼。這就像是有一間廚房,你可以叫不同廚師幫你準備不同部分的菜,而不是每次都自己重頭做起
這樣的做法在程式設計中叫做「建構子串接」(constructor chaining),能讓程式碼更有彈性,也更容易維護。
不過使用上有幾個重要規則:
- 一次只能呼叫一個
- 必須放在建構子的第一行
- 不能在普通方法中使用
想像你在蓋房子,一個建構子就像是不同的裝修團隊。你可以選擇只找粉刷牆壁的,也可以找包含粉刷、鋪地板、裝燈的團隊。如果你用 this(...)
,就像是在粉刷團隊裡打電話叫來鋪地板的同事幫忙,大家分工合作,避免重複做事
static
是什麼?沒有物件的世界也需要方法
初學者常會搞混兩種用法:
- 一種是先
new
出來一個物件再呼叫方法,例如:
Dog myDog = new Dog();
myDog.bark();
這是物件導向的方式,你是叫一隻特定的狗來叫。
- 另一種是直接透過類別來呼叫方法,例如:
Math.sqrt(4);
這就是使用 static
方法,不需要建立 Math
的物件。你只是問「全世界的數學大師們,4 的平方根是多少?」
static
的意思是:「這個方法或變數不是某個物件獨有的,而是整個類別共享的」你不需要 new
出一個物件就能用它,因為它根本不屬於任何一個物件。打個比方:你班導的手機號碼是公開的,全班都能打,不需要你是某個特定同學才能聯絡到老師;但如果是你朋友的手機號碼,那你就得是他的朋友(也就是他的實例)才會知道。所以 static
的東西,是對所有人開放的,不用先建立關係(物件)
以下是一個簡單範例:
如果 this
是“我自己”,那 static
就是“我們全體”。它不屬於某一個物件,而是屬於整個類別
public class StaticExample {
static int staticValue = 5;
static void staticMethod() {
System.out.println("Static method called. staticValue = " + staticValue);
}
public static void main(String[] args) {
StaticExample.staticMethod();
}
} /* Output:
Static method called. staticValue = 5
*/
你不需要創造一個 StaticExample
的物件,也就是不用 new StaticExample()
,就能直接用 StaticExample.staticMethod()
呼叫這個方法。因為這個方法是 static
,它屬於類別本身,而不是某個物件實例。就好像教室牆上貼了一張公用時間表,每個人都能直接看,不用先請出某個特定的同學
小結
這篇文章幫你從各種角度看清楚了 this
和 static
的不同角色。
this
是物件的自我意識,只有當你真的有建立出一個物件實例時,它才存在。它幫你指向「現在我是哪個物件」,讓你能夠做出鏈式調用、把自己傳出去、或在建構子內協調自己的初始化流程static
則像是工具櫃裡的萬用工具,它根本不需要你先創建某個物件。任何人只要知道類別名稱,就能直接拿來用。它沒有this
,因為它不屬於任何物件,只有一份給大家共用
懂了這兩個關鍵字,你就不會再在物件與類別的迷霧裡打轉。下次看到 this
和 static
,不只是「哦,我看過這個」,而是「我知道為什麼它要這樣設計」。這才是會寫程式,而不是只會複製程式碼
Comments ()