[Learn] Java 基礎:class、method 與 constructor
/ 12 min read
Updated:Table of Contents
看 Java 程式碼時,一個 class 裡常會同時出現 public、private、static、final、constructor 與各種 method
這篇會用一個學生資料的範例,練習看懂以下內容
class如何描述一種物件constructor如何建立物件method如何定義物件的行為static如何表示所有物件共用的成員final如何限制再次賦值、override 或繼承package如何組織程式碼- access modifier 如何限制可以存取成員的範圍
先建立整體概念
可以先用下面這條主線理解 Java class
class 定義一種物件 ↓field 保存物件的狀態 ↓constructor 建立物件並設定初始狀態 ↓method 讀取或改變物件的狀態例如學生會有學號、姓名與分數,這些資料適合放在 field
建立學生時必須提供基本資料,所以交給 constructor
學生可以增加分數、判斷是否及格,這些行為則寫成 method
建立 package 與檔案
先建立以下目錄結構
src/└── com/ └── example/ ├── app/ │ └── Main.java └── school/ └── Student.javaJava 通常會讓 package 名稱對應目錄路徑
com.example.school對應com/example/schoolcom.example.app對應com/example/app- package 名稱通常全部使用小寫
public class Student通常放在Student.java
先在 Student.java 寫入以下內容
package com.example.school;
public class Student { public static final int PASSING_SCORE = 60;
private static int studentCount = 0;
private final String id; private String name; private int score;
public Student(String id, String name) { this(id, name, 0); }
public Student(String id, String name, int score) { if (score < 0 || score > 100) { throw new IllegalArgumentException("score 必須介於 0 到 100"); }
this.id = id; this.name = name; this.score = score; studentCount++; }
public void addScore(int points) { if (points < 0) { throw new IllegalArgumentException("points 不可小於 0"); }
score = Math.min(100, score + points); }
public boolean hasPassed() { return score >= PASSING_SCORE; }
public String getSummary() { return id + " / " + name + " / " + score + " / 通過=" + hasPassed(); }
public static int getStudentCount() { return studentCount; }}接著在另一個 package 的 Main.java 使用 Student
package com.example.app;
import com.example.school.Student;
public class Main { public static void main(String[] args) { Student amy = new Student("S001", "Amy", 55); Student ben = new Student("S002", "Ben");
amy.addScore(15);
System.out.println(amy.getSummary()); System.out.println(ben.getSummary()); System.out.println("目前學生數:" + Student.getStudentCount()); System.out.println("及格分數:" + Student.PASSING_SCORE); }}在專案根目錄編譯並執行
javac -d out src/com/example/school/Student.java src/com/example/app/Main.javajava -cp out com.example.app.Main執行結果如下
S001 / Amy / 70 / 通過=trueS002 / Ben / 0 / 通過=false目前學生數:2及格分數:60class 是物件的定義
class 用來定義資料與行為
public class Student { private String name; private int score;}這段程式只定義了 Student 這種型別,還沒有建立真正的學生物件
使用 new 才會建立 instance
Student amy = new Student("S001", "Amy", 55);Student ben = new Student("S002", "Ben");amy 與 ben 都是 Student instance,但各自保存自己的 name 與 score
在 class 內宣告的變數稱為 field,也常稱為 member variable
private final String id;private String name;private int score;field 描述物件需要保存的狀態
constructor 負責建立物件
constructor 的名稱必須與 class 相同,而且不能寫回傳型別,連 void 也不能寫
public Student(String id, String name, int score) { this.id = id; this.name = name; this.score = score;}執行 new Student(...) 時,Java 會呼叫參數相符的 constructor
this.id 代表目前物件的 field,右邊的 id 則是傳入 constructor 的 parameter
同一個 class 可以有多個參數不同的 constructor,這稱為 constructor overloading
public Student(String id, String name) { this(id, name, 0);}this(id, name, 0) 會呼叫另一個 constructor,並把預設分數設為 0
這種寫法可以集中初始化與驗證邏輯,避免兩個 constructor 各自維護一份相似程式碼
如果完全沒有宣告 constructor,compiler 會自動提供一個無參數 constructor
但只要自己宣告了任何 constructor,compiler 就不會再自動補上無參數版本
method 定義物件可以做什麼
method 是一段有名稱、可以接收參數並可能回傳結果的行為
public boolean hasPassed() { return score >= PASSING_SCORE;}可以把 method declaration 拆成以下部分
| 部分 | 範例 | 用途 |
|---|---|---|
| access modifier | public | 決定誰可以呼叫 |
| 回傳型別 | boolean | 說明回傳值的型別 |
| method 名稱 | hasPassed | 描述這個行為 |
| parameter | () | 接收呼叫者提供的資料 |
| method body | { ... } | 實際執行的程式碼 |
沒有回傳值時使用 void
public void addScore(int points) { score = Math.min(100, score + points);}呼叫 instance method 時,要透過某個物件
amy.addScore(15);boolean passed = amy.hasPassed();如果 method 會改變 field,它改變的是接收這次呼叫的那個物件
amy.addScore(15) 不會改到 ben 的分數
static 屬於 class 本身
一般 field 與 method 屬於每一個 instance,static 成員則屬於 class 本身,由所有 instance 共用
private static int studentCount = 0;
public static int getStudentCount() { return studentCount;}每次建立 Student 都會讓共用的 studentCount 加一
呼叫 static method 時,建議使用 class 名稱
int count = Student.getStudentCount();static method 沒有特定的目前物件,因此不能直接使用 this,也不能直接存取 name、score 等 instance field
main 也是 static method,JVM 不需要先建立 Main 物件就能把它當成程式進入點
public static void main(String[] args) { // 程式從這裡開始執行}final 代表不能再改變某種關係
final 放在不同位置時,限制的內容也不同
| 位置 | 意義 |
|---|---|
| variable 或 field | 初始化後不能再次賦值 |
| method | subclass 不能 override |
| class | 不能被其他 class 繼承 |
學號建立後不應改變,所以使用 final field
private final String id;final field 可以在宣告時賦值,也可以在每條 constructor 路徑中完成賦值
所有學生共用且不應變動的及格分數,適合宣告為 constant
public static final int PASSING_SCORE = 60;Java constant 通常使用 static final,名稱採全大寫並以底線分隔單字
需要禁止繼承時,可以把整個 class 宣告為 final
public final class ScoreCalculator {}需要注意的是,final reference 不能改成指向另一個物件,但它所指向的物件不一定不可變
final int[] scores = {60};scores[0] = 100; // 可以修改陣列內容// scores = new int[] {80}; // 無法再次指定 reference因此 final 不等於 immutable
package 用來組織與區分 class
package 可以避免 class 名稱衝突,也能把用途相近的 class 放在同一個命名空間
package com.example.school;package declaration 要放在原始碼開頭,並位於 import 與 class declaration 之前
另一個 package 要使用 Student 時,可以先 import
package com.example.app;
import com.example.school.Student;也可以不用 import,直接寫完整名稱
com.example.school.Student amy = new com.example.school.Student("S001", "Amy");實務上通常使用 import,讓程式碼保持簡潔
package 不只是資料夾分類,也會影響沒有寫 access modifier 的 class 與 member 可以被誰使用
access modifier 控制可見範圍
Java 常見的四種存取層級如下
| 修飾方式 | 同一個 class | 同一個 package | 不同 package 的 subclass | 其他 class |
|---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
| 不寫 modifier | 可以 | 可以 | 不可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
不寫 modifier 時通常稱為 package-private,而不是 default
這裡的 private field 只能由 Student 自己管理
private String name;private int score;外部程式不能直接寫入分數,只能透過公開的 method
amy.addScore(15);// amy.score = 200; // 無法編譯,score 是 private這樣可以把分數範圍檢查集中在 Student 內部,避免物件進入不合理的狀態
protected 主要用在繼承設計
不同 package 的 subclass 可以透過繼承關係存取 protected member,但一般外部物件不行
另外,最外層的 class 只能宣告為 public 或省略 modifier
private 與 protected 可以用在 nested class,但不能直接用在最外層 class
如何選擇 access modifier
可以從最小必要範圍開始選擇
- field 優先使用
private - 要提供給其他 package 使用的 API 才設為
public - 只在同一個 package 協作時可以省略 modifier
- 確定要提供 subclass 擴充時才使用
protected
這不是要求所有 field 都要機械式地產生 getter 與 setter
如果分數只能增加而不能任意指定,提供 addScore 會比公開 setScore 更能表達規則
public void addScore(int points) { if (points < 0) { throw new IllegalArgumentException("points 不可小於 0"); }
score = Math.min(100, score + points);}class 的公開 method 應該描述允許外部執行的行為,而不只是暴露內部 field
結論
- class 把狀態與行為組合成一種型別
- constructor 在建立 instance 時設定有效的初始狀態
- method 定義物件或 class 可以執行的行為
- static member 屬於 class,instance member 屬於各自的物件
- final 對 variable、method 與 class 有不同限制,不代表物件一定 immutable
- package 同時負責命名空間、程式碼組織與部分存取邊界
- access modifier 應從最小必要權限開始選擇,避免直接暴露內部狀態
先把這幾個概念連起來,就能讀懂多數 Java class 的基本結構,也能開始把資料與規則寫成自己的 class
參考資料
- https://docs.oracle.com/javase/specs/jls/se26/html/jls-6.html
- https://docs.oracle.com/javase/specs/jls/se26/html/jls-7.html
- https://docs.oracle.com/javase/specs/jls/se26/html/jls-8.html
- https://docs.oracle.com/en/java/javase/26/docs/specs/man/javac.html
- https://docs.oracle.com/en/java/javase/26/docs/specs/man/java.html