> For the complete documentation index, see [llms.txt](https://yubin551.gitbook.io/java-note/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yubin551.gitbook.io/java-note/basic_java_programming/package_import.md).

# 套件、載入

> 了解關鍵字 package 及 import 的Java意義及使用方式。

## 為什麼需要套件？ Why Package?

Java是物件導向的程式語言，我們很清楚寫Java就是在設計類別。想想看，如果一個很大的專案、很大的系統，甚至是好幾個團隊一起共同開發的程式，會有多少個類別？

不說別的，光是安裝好JDK，Java提供你使用的類別就有成千上萬個(我也沒數過)。

這些類別如果散落一地，那程式勢必會很凌亂，且類別間彼此負責的部份不容易劃分。更慘的是，如果你開發一個類別叫做Human，那你所有的共同開發者都不能創造一個叫Human名稱的類別，想起來就很不方便。

因此Java設計了一個機制來管理(存放)類別。

## 套件 Package

一個套件可以存放多個類別，套件被設計成與檔案系統的目錄相對應。

對應到檔案系統上就是，一個目錄可以存放多個類別檔案。

應用上，相似功能的類別可以放在一起，方便管理。或是同一個團隊的類別放在一起，方便資源的存取，套件與存取權限關係重大，清楚的套件劃分可以更容易找出程式錯誤或保護團隊智慧財產。

舉個現實中可能會遇到的例子：

yubin跟tina是團隊的工程師，一起負責同一個專案。我們很清楚物件導向就是把真實世界抽象化成類別，但每個人從不同的角度去思考的結果不一樣。設計類別的方式也不盡相同，所以他們決定先各自開發自己的類別，隨後整合的時候再做進一步討論。

yubin 負責的Human.java：

```java
package yubin;

public class Human {
    String name;
    int age, height, weight;
    public Human(String str) {
        name = str;
    }// end of constructor(String)
    public void setValue(int a, int h, int w) {
        age = a;
        height = h;
        weight = w;
    }// end of setValue(int,int)
}// end of class Human
```

tina 負責的Human.java：

```java
package tina;

public class Human {
    String name;
    int age,height;
    public Human(String str){
        name = str;
    }// end of constructor(String)
    public void setValue(int a,int h){
        age = a;
        height = h;
    }// end of setValue()
}//end of class Human
```

整合測試用的Test.java：

```java
package run;

public class Test {
    public static void main(String[] args) {
        tina.Human h1 = new tina.Human("小婷");
        h1.setValue(18, 162);
        yubin.Human h2 = new yubin.Human("小木");
        h2.setValue(22, 178, 63);
    }// end of main(String[])
}// end of class Test
```

好的，我們可以看到我們的套件關鍵字：package

```java
package 套件名稱;
```

利用package就可以設定某個類別屬於某個套件。記得必須寫在最外層的class之外，且需置於最頂層(通常放在第一行)。

然而package的結構與檔案目錄相對應，因此若上面三個檔案設了不同package，但放在同一個目錄，編譯會發生錯誤，因為package的設定與該檔案所在位置是對應的。

以上面3個檔案及所屬套件來看，檔案結構必須是這樣： ![](/files/-MLRnkwplAffqcnnFDv0)

package目錄就是對應到檔案系統的目錄(資料夾)。

看完檔案系統的結構後，來討論程式的部份。

這個Test.java會分別創造tina套件裡的Human與yubin套件裡的Human物件出來。

```java
package run;

public class Test {
    public static void main(String[] args) {
        tina.Human h1 = new tina.Human("小婷");
        h1.setValue(18, 162);
        yubin.Human h2 = new yubin.Human("小木");
        h2.setValue(22, 178, 63);
    }// end of main(String[])
}// end of class Test
```

我們可以看到，雖然身處不同套件(也就是不同目錄)，但依然可以存取到該類別，創造物件。

要做到這樣需要滿足兩個條件： 1. 目標類別可以被存取 2. 正確的指到該類別

第一點，攸關目標類別的『存取修飾子』設定，仔細看兩個Human類別在最外層的類別定義：

```java
public class Human{
    // code...
}
```

其中關鍵字public就是存取修飾子，代表這這個類別，可以被其他套件的類別存取。Human類別中的建構子、物件方法也是一樣的意思，詳細存取修飾子會在下一章節討論。

第二點，正確的指到該類別。就是說，雖然身處不同套件中，但類別間彼此還是可以用『完整類別路徑』去做存取。

```java
套件名稱.類別名稱
```

還是靠那個點運算子『.』，如果套件裡面還有套件，那就繼續點下去。

```
套件名稱1.套件名稱2.套件名稱3.套件名稱4.套件名稱n.類別名稱
```

利用『完整類別路徑』就可以存取到相應的類別。但是不覺得麻煩嗎？想建個物件都要打一堆東西(套件名稱又多又長的時候會抓狂)。因此Java就提供了一個機制，讓你可以載入該類別到你撰寫的程式中，就不用一直使用完整類別路徑來使用該類別。

## import

關鍵字import，讓你可以把你的目標類別載入你的程式中，你就可以直接使用該類別，彷彿這個目標類別就在你身邊一樣。

舉個例子，假設tina和yubin討論結果，Human類別不需要體重(weight)的欄位，因此決定採用tina套件中的Human.java，那在套件run的Test.java測試程式中，就不用每次都打完整類別路徑，可以利用關鍵字import。

```java
package run;
import tina.Human;

public class Test {
    public static void main(String[] args) {
        Human h1 = new Human("小婷");
        h1.setValue(18, 162);
        Human h2 = new Human("小木");
        h2.setValue(22, 178);
    }// end of main(String[])
}// end of class Test
```

import 用法：

```java
import 完整類別路徑;
```

上述Test程式中，在第二行import了tina套件中的Human類別，所以在程式中就可以直接使用Human，這個Human類別被定義在tina套件裡。

這是import關鍵字最主要的功能：把目標套件的某個類別載入程式中。

此外完整路徑的寫法，可以搭配萬用字元『\*』：

```java
import tina.*;    // 表示把tina套件中『所有類別』import進來
```

當然，就算import了某個套件，其他套件還是一樣可以用完整類別路徑做存取：

```java
package run;
import tina.Human;

public class Test {
    public static void main(String[] args) {
        Human h1 = new Human("小婷");
        h1.setValue(18, 162);
        Human h2 = new Human("小木");
        h2.setValue(22, 178);
        yubin.Human h3 = new yubin.Human("阿杉");  
        h3.setValue(10, 100, 100);
        // 物件h3 雖然也叫Human但跟h1、h2是完全不一樣的東西
    }// end of main(String[])
}// end of class Test
```

上述程式中，物件h3使用的是yubin套件中的Human，這樣是沒問題的。所以就算有類別不會被使用也不用急著刪掉，說不定之後還能派上用場(程式都是工程師的心血阿)。

## 宣告順序

這個章節我們提到了package、import的使用，但在java的類別定義中，這是有規範好的先後順序。

```java
package 自身套件;
import 目標套件.目標類別;
class 類別名{
    // code...
}
```

這三個定義順序**不能互換**，畢竟編譯器是由上往下編譯，不知道package怎麼import東西，沒有先知道import了哪些東西怎麼去解析class裡面的程式碼。

## 預設套件 Default Package

在講這個章結前，其實我們有寫過一些程式了，但我們都沒有做package的宣告。

是因為我們都直接在根目錄下放置我們的主程式，檔案結構如下： ![](/files/-MLRnkxGYh8xPXfV0Ejq)

沒有放在某個套件中，自然不用宣告package是什麼，在Java中這種沒有存在於任何套件的都被視為在預設套件中(default package)。

※注意，此處題到的根目錄，指的是程式編譯的根目錄，並不是硬碟C槽。

## 預設已經被import的套件

現在我們知道可以利用import來存取各式各樣的類別了，但回過頭來想，其實我們早就已經用過了！？ 還用的很自然XD

來看一下到底是什麼神奇的東西：

```java
System.out.println("Hello Java");
```

哇塞，這就是我們的第一個程式就說過的println()方法，我們都知道它可以印出東西，也知道System是物件，out是System的一個static欄位，println()是out這個物件的一個方法。

但，這個System是你寫的嗎？沒有在程式裡，也沒有用完整類別路徑存取，怎麼能動？

嗯，當然又是熱心的Java在搞鬼，在 java.lang 套件裡面，定義了很多常常常常會用到的類別，而且都是Java語言的必要基礎類別，所以Java預設都會幫你`import java.lang.*;`

程式設計師不用自己import，也不用特地用完整類別路徑去存取，直接呼叫就可以。

```java
import java.lang.*; // Java會自動幫你加上這行(你看不到)，不寫也沒關係
class Test {
    public static void main(String[] args) {
        java.lang.System.out.println("Hello Java"); //完整類別路徑
        System.out.println("Hello Java");  // 一般寫法
    }// end of main(String[])
}// end of class Test
```

執行結果：

```java
Hello Java
Hello Java
```
