# 套件、載入

> 了解關鍵字 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
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yubin551.gitbook.io/java-note/basic_java_programming/package_import.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
