Java 類別的存取權限(Access Modifiers)

Published February 15, 2025 by 徐培鈞
程式語言

在 Java 開發中,存取權限(Access Modifiers)決定了一個類別(class)或其成員(屬性與方法)能被哪些地方存取。

這就像是房子的門鎖,你可以選擇讓所有人都能進入(public),只讓家人進來(protected),只讓自己進入(private),或是讓同一社區的人進入(預設)。

了解這些存取權限,可以幫助我們保護程式的內部結構,確保安全性與封裝性(Encapsulation)。

Java 中的 package 是什麼?

package(封裝、套件)是 Java 用來管理類別和避免名稱衝突的一種方式。

在 Java 中,每個 .java 檔案只能屬於一個 package,而且 package 的宣告必須放在 .java 檔案的第一行(import 之前)。

這表示該檔案內的所有類別都屬於同一個 package,不能同時屬於多個 package

如何宣告 package

每個 .java 檔案的開頭都可以加上 package 宣告,格式如下:

package mypackage;  // 這個類別屬於 mypackage
public class MyClass {
    public void sayHello() {
        System.out.println("Hello from MyClass!");
    }
}

一個 .java 檔案只能有一個 package

mypackage/MyClass.java

package mypackage;  // 指定這個檔案屬於 mypackage

public class MyClass {
    public void sayHello() {
        System.out.println("Hello from MyClass!");
    }
}

這個檔案屬於 mypackage,所以它裡面的所有類別都在 mypackage 這個範圍內。

錯誤示範:一個 .java 檔案不能有多個 package

package package1;
package package2;  // ❌ 錯誤!一個檔案只能有一個 package

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello!");
    }
}

🔴 錯誤訊息:

Only a single package declaration is allowed

Java 不允許一個 .java 檔案同時屬於 package1package2


package 的目錄結構

package(封裝、套件)是 Java 用來管理類別和避免名稱衝突的一種方式。

你可以把它想像成資料夾系統,每個 package 就像是一個資料夾,裡面可以存放相關的 Java 類別。

例如,以下 package 結構:

graph TD;
    A[src] --> B[mypackage]
    A[src] --> C[anotherpackage]
    
    %% mypackage 內容
    B --> B1[MyClass.java]
    B --> B2[Helper.java]
    B --> B3[DataModel.java]

    %% anotherpackage 內容
    C --> C1[Test.java]
    C --> C2[Utils.java]
    C --> C3[Config.java]
  • mypackage 內的 MyClass.java 屬於 mypackage
  • anotherpackage 內的 Test.java 屬於 anotherpackage

多個 .java 檔案可以屬於同一個 package

雖然 一個 .java 檔案只能有一個 package,但多個 .java 檔案可以屬於同一個 package。這在大型專案中很常見。

例如:

src/
└── mypackage/
    ├── Aaa.java
    ├── Bbb.java

Aaa.java

package mypackage;

public class Aaa {
    public void sayHello() {
        System.out.println("Hello from Aaa!");
    }
}

Bbb.java

package mypackage;

public class Bbb {
    public void sayHi() {
        System.out.println("Hi from Bbb!");
    }
}

Test.java

package mypackage;  // 同一個 package,Aaa 和 Bbb 都可存取

public class Test {
    public static void main(String[] args) {
        Aaa obj1 = new Aaa();
        obj1.sayHello();

        Bbb obj2 = new Bbb();
        obj2.sayHi();
    }
}

AaaBbb 都屬於 mypackage,所以它們可以直接互相存取,不需要 import

Java 內建的 package

Java 內建很多標準 package,例如:

  • java.util:提供工具類,例如 ArrayListHashMap
  • java.io:處理輸入輸出,例如 FileBufferedReader
  • java.net:處理網路請求,例如 URLSocket

你可以直接 import 這些標準類別來使用:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        System.out.println(list);
    }
}

如何在不同 package 引用類別?

範例:兩個不同 package 的類別

mypackage/MyClass.java(定義類別)

package mypackage;  // 類別屬於 mypackage

public class MyClass {
    public void sayHello() {
        System.out.println("Hello from MyClass!");
    }
}

anotherpackage/Test.java(引用 mypackage 內的 MyClass

package anotherpackage;

import mypackage.MyClass;  // 必須 import 來引用其他 package 內的類別

public class Test {
    public static void main(String[] args) {
        MyClass obj = new MyClass();  // ✅ OK
        obj.sayHello();
    }
}

⚠️ 注意!如果沒有 import mypackage.MyClass;Test.java 會找不到 MyClass,因為它們不在同一個 package


Java 類別的四種存取權限

Java 中的類別存取權限有四種:

可用在頂層類別?✅ 可以
存取範圍任何地方(跨 package 也可存取)
可用在頂層類別?✅ 可以
存取範圍只能在相同的 package 內存取
可用在頂層類別?❌ 不能
存取範圍只能用在內部類別,外部無法存取
可用在頂層類別?❌ 不能
存取範圍只能在相同的 package 或子類別內存取

注意: 頂層類別(定義在 .java 檔案內的主要類別)只能是 public 或預設(package-private),不能是 privateprotected


public(公開)

特性

  • ✅ 可以被任何地方存取(不同 package 也可以)。
  • ✅ 必須與 .java 檔案名稱相同。

範例

MyClass.java(定義一個 public 類別)

public class MyClass {
    void sayHello() {
        System.out.println("Hello!");
    }
}

Test.java(存取 public 類別,來自不同的檔案或 package)

public class Test {
    public static void main(String[] args) {
        MyClass obj = new MyClass(); // OK,public 類別可存取
        obj.sayHello();
    }
}

MyClass 可以在任何地方使用,不管是不是同一個 package。


預設(package-private)

特性

  • ✅ 只能在相同 package 內存取。
  • ✅ 不需要與 .java 檔案名稱相同。

範例

MyClass.java(沒有 public,預設為 package-private)

class MyClass {  // 預設存取權限(package-private)
    void sayHello() {
        System.out.println("Hello!");
    }
}

Test.java(與 MyClass.java 在同一個 package)

public class Test {
    public static void main(String[] args) {
        MyClass obj = new MyClass(); // OK,因為在同一個 package
        obj.sayHello();
    }
}

MyClass 可以在同一個 package 內存取,但不同 package 無法存取。

🚫 錯誤示範:不同 package 嘗試存取 package-private 類別

import mypackage.MyClass; // ❌ 錯誤!

public class AnotherTest {
    public static void main(String[] args) {
        MyClass obj = new MyClass(); // ❌ 無法存取!
    }
}

🔴 錯誤訊息:

MyClass is not public in mypackage; cannot be accessed from outside package

private(私有)

特性

  • ❌ 不能用在「頂層類別」。
  • ✅ 可以用在「內部類別(Nested Class)」。

🚫 錯誤示範:private 不能用在頂層類別

private class MyClass {  //  錯誤!
    void sayHello() {
        System.out.println("Hello!");
    }
}

🔴 錯誤訊息:

modifier 'private' not allowed here

正確用法:private 內部類別

public class OuterClass {
    private static class InnerClass { // OK
        void sayHello() {
            System.out.println("我是內部類別!");
        }
    }

    public static void main(String[] args) {
        InnerClass obj = new InnerClass();
        obj.sayHello();
    }
}

protected(受保護)

特性

  • ❌ 不能用在「頂層類別」。
  • ✅ 只能在相同 package 或子類別中存取。

🚫 錯誤示範:protected 不能用在頂層類別

protected class MyClass {  // ❌ 錯誤!
    void sayHello() {
        System.out.println("Hello!");
    }
}

🔴 錯誤訊息: modifier 'protected' not allowed here

正確用法:protected 內部類別

public class OuterClass {
    protected static class InnerClass { // OK
        void sayHello() {
            System.out.println("我是受保護的內部類別!");
        }
    }
}

protected 方法允許子類別存取

class Animal {
    protected void makeSound() {
        System.out.println("動物發出聲音");
    }
}

class Dog extends Animal {
    void bark() {
        makeSound(); // ✅ OK
        System.out.println("汪汪!");
    }
}

總結

package 幫助我們組織 Java 類別,而存取修飾詞決定了類別之間的可見性。透過正確使用這些概念,我們可以提升程式碼的安全性、可維護性與結構化!

這樣的存取控制讓 Java 的程式碼更易讀、更安全、更容易維護,並且符合物件導向的設計原則!