第 9 章 管理類別檔案

在 Java 中每一個定義好的類別,在編譯過後都會以一個副檔名為 .class 的檔案儲存下來,在程式規模逐漸擴大之後,所需的類別將是以成千成萬的方式來計算,這麼多的類別檔案如果只是堆在一個目錄下加以管理,很容易發生名稱相同的衝突,要搜尋某個類別會是件麻煩事,管理這麼多散落一地似的類別檔案更是困難。

在這個章節中,您會認識到類別檔案的管理方式,像是各種內部類別所生成的類別檔案、使用「套件」(package)以階層方式來管理類別,您會認識到 “import” 的實質意義只是讓編譯器瞭解如何尋找目標類別,並且會學到 J2SE 5.0 中新增的靜態 import(static import)功能。


9.1 內部類別

在類別中您還可以再定義類別,稱之為「內部類別」(Inner class)或「巢狀類別」(Nested class)。非靜態的內部類別可以分為三種:「成員內部類別」(Member inner class)、「區域內部類別」(Local inner class)與「匿名內部類別」(Anonymous inner class)。內部類別的主要目的,都是對外部隱藏類別的存在性。

9.1.1 成員內部類別、區域內部類別

使用內部類別有幾個好處,其一是內部類別可以直接存取其所在類別中的私用(private)成員;其二是當某個 Slave類別完全只服務於一個 Master 類別時,您可以將之設定為內部類別,如此使用 Master 類別的人就不用知道 Slave 的存在;再者是像在「靜態工廠」(Static factory)模式中,對呼叫靜態方法的物件隱藏返回物件的實作細節或產生方式。

先來介紹成員內部類別,基本上是在一個類別中直接宣告另一個類別,例如:

  1. public class OuterClass {
  2. // 內部類別
  3. private class InnerClass {
  4. // ....
  5. }
  6. }

成員內部類別同樣也可以使用 “public”、”protected” 或 “private” 來修飾其存取權限,範例 9.1 簡單示範成員內部類別的使用。

範例 9.1 PointDemo.java

  1. public class PointDemo {
  2. // 內部類別
  3. private class Point {
  4. private int x, y;
  5. public Point() {
  6. }
  7. public void setPoint(int x, int y) {
  8. this.x = x;
  9. this.y = y;
  10. }
  11. public int getX() { return x; }
  12. public int getY() { return y; }
  13. }
  14. private Point[] points;
  15. public PointDemo(int length) {
  16. points = new Point[length];
  17. for(int i = 0; i < points.length; i++) {
  18. points[i] = new Point();
  19. points[i].setPoint(i*5, i*5);
  20. }
  21. }
  22. public void showPoints() {
  23. for(int i = 0; i < points.length; i++) {
  24. System.out.printf("Point[%d]: x = %d, y = %d%n",
  25. i, points[i].getX(), points[i].getY());
  26. }
  27. }
  28. }

程式中假設 Point 類別只服務於 PointDemo 類別,外界不必知道 Point 類別的存在,只要知道如何操作 PointDemo 的實例就可以了,就像範例 9.2 所示。

範例 9.2 PointShow.java

  1. public class PointShow {
  2. public static void main(String[] args) {
  3. PointDemo demo = new PointDemo(5);
  4. demo.showPoints();
  5. }
  6. }

執行結果:

  1. Point[0]: x = 0, y = 0
  2. Point[1]: x = 5, y = 5
  3. Point[2]: x = 10, y = 10
  4. Point[3]: x = 15, y = 15
  5. Point[4]: x = 20, y = 20

在檔案管理方面,成員內部類別在編譯完成之後,所產生的檔案名稱為「外部類別名稱\$內部類別名稱.class」,所以範例 9.1 在編譯過後會產生兩個檔案:PointDemo.class 與 PointDemo$Point.class。

區域內部類別的使用與成員內部類別類似,區域內部類別定義於一個方法中,類別的可視範圍與生成之物件僅止於該方法之中。

內部類別還可以被宣告為 “static”,不過由於是 “static”,它不能存取外部類別的方法,而必須透過外部類別所生成的物件來進行呼叫,被宣告為 static的內部類別,事實上也可以看作是另一種名稱空間的管理方式,例如:

  1. public class Outer {
  2. public static class Inner {
  3. ....
  4. }
  5. ....
  6. }

您可以如以下的方式來使用 Inner 類別:

  1. Outer.Inner inner = new Outer.Inner();

良葛格的話匣子 有關內部類別的實際應用,建議看一下「靜態工廠模式」,對於使用靜態工廠類別的物件,它不需要知道返回物件的實例是如何產生的,只需要知道如何操作返回的物件,所以您可以宣告一個介面,並在靜態工廠中定義一個實作該介面的內部類別,由該內部類別產生實例,這是內部類別的一個應用場合。

另外也可以看一下「Iterator 模式」,一個實例是您在 Java SE 的 API 文件中找不到實作 java.util.Iterator 介面的類別,因為實作 Iterator 介面的類別是定義在集合類別(像是 java.util.ArrayList)之中,您只需要知道如何操作 Iterator 介面就可以了。

9.1.2 匿名內部類別

內部匿名類別可以不宣告類別名稱,而使用 “new” 直接產生一個物件,內部匿名類別可以是繼承某個類別或是實作某個介面,內部匿名類別的宣告方式如下:

  1. new [類別或介面()] {
  2. // 實作
  3. }

一個使用內部匿名類別的例子如範例9.3所示,您直接繼承 Object 類別定義一個匿名類別,重新定義 toString() 方法,並使用匿名類別直接產生一個物件。

範例 9.3 AnonymousClassDemo.java

  1. public class AnonymousClassDemo {
  2. public static void main(String[] args) {
  3. Object obj =
  4. new Object() {
  5. public String toString() { // 重新定義toString()
  6. return "匿名類別物件";
  7. }
  8. };
  9. System.out.println(obj);
  10. }
  11. }

使用 System.out.println() 時如果傳入的是物件,會呼叫物件的 toString() 方法得到 String 實例,所以範例的輸出如下所示:

  1. 匿名類別物件

注意如果要在內部匿名類別中使用外部的區域變數,變數在宣告時必須為 “final”,例如下面的陳述是無法通過編譯的:

  1. ....
  2. public void someMethod() {
  3. int x = 10; // 沒有宣告final
  4. Object obj =
  5. new Object() {
  6. public String toString() {
  7. return String.valueOf(x); // x不可在匿名類別中使用
  8. }
  9. };
  10. System.out.println(obj);
  11. }
  12. ....

在進行編譯時,編譯器會回報以下的錯誤:

  1. local variable x is accessed from within inner class; needs to be declared final

您要在宣告區域變數x時加上”final”才可以通過編譯:

  1. ....
  2. public void someMethod() {
  3. final int x = 10; // 宣告final
  4. Object obj =
  5. new Object() {
  6. public String toString() {
  7. return String.valueOf(x); // x可在匿名類別中使用
  8. }
  9. };
  10. System.out.println(obj);
  11. }
  12. ....

為什麼要加上 “final” 宣告呢?原因在於區域變數 x 並不是真正被拿來於內部匿名類別中使用,x 會被匿名類別複製作為資料成員來使用,由於真正在匿名類別中的x是複製品,即使您在內部匿名類別中對 x 作了修改(例如 x=10 的指定),也不會影響真正的區域變數x,事實上您也通不過編譯器的檢查,因為編譯器會要求您加上 “final” 關鍵字,這樣您就知道不能在內部匿名類別中改變 x 的值,因為即使能改變也沒有意義。

在檔案管理方面,內部匿名類別在編譯完成之後會產生「外部類別名稱\$編號.class」,編號為 1、2、3…n,每個編號 n 的檔案對應於第 n 個匿名類別,所以範例 9.3編 譯完成後,會產生 AnonymousClassDemo.class與AnonymousClassDemo$1.class 兩個檔案。

9.2 package 與 import

隨著程式架構越來越大,類別個數越來越多,您會發現管理程式中維護類別名稱也會是一件麻煩的事,尤其是一些同名問題的發生,例如在程式中,您也許會定義一個 Point 類別,但另一個與您合作開發程式的開發人員並不曉得已經有這個類別名稱的存在,他可能也定義了一個 Point 類別,於是編譯過後他的 Point 類別檔案會覆蓋您的 Point 類別檔案,問題就這麼發生了。

9.2.1 設定套件(package)

Java 提供「套件」(package)來管理類別,套件被設計與檔案系統結構相對應,如果您的套件設定為onlyfun.caterpillar,則該類別應該在 Classpath 可以存取到的路徑下的 onlyfun 目錄下之 caterpillar 目錄找到,沒有設定套件管理的類別會歸為「預設套件」(default package)。

為了要能建立與套件相對應的檔案系統結構,您在編譯時可以加入 “-d” 參數,並指定產生的類別檔案要儲存在哪一個目錄之下,實際使用範例來進行說明,在 Java 中定義套件時,要使用關鍵字 “package”,使用方法如範例 9.4。

範例 9.4 PackageDemo.java

  1. package onlyfun.caterpillar;
  2. public class PackageDemo {
  3. public static void main(String[] args) {
  4. System.out.println("Hello! World!");
  5. }
  6. }

在編譯時您使用以下的指令指定編譯過後的類別檔案儲存目錄,’.’表示建立在目前的工作位置:

  1. javac -d . PackageDemo.java

編譯完成之後,在目前的工作位置中會出現 onlyfun 目錄,而 onlyfun 目錄下會有個 caterpillar 目錄,而 caterpillar 目錄下則有一個 PackageDemo.class 檔案,在編譯完成之後,”package” 的設定會成為類別名稱的一部份,也就是完整的類別名稱是 onlyfun.caterpillar.PackageDemo,所以在執行時要這麼下指令以指定類別名稱:

  1. java onlyfun.caterpillar.PackageDemo

執行結果會出現”Hello! World!”。再來舉個例子,假設您如範例 9.5 建立了onlyfun.caterpillar.Point2D 類別。

範例 9.5 Point2D.java

  1. package onlyfun.caterpillar;
  2. public class Point2D {
  3. private int x;
  4. private int y;
  5. public Point2D() {
  6. }
  7. public Point2D(int x, int y) {
  8. this.x = x;
  9. this.y = y;
  10. }
  11. public int getX() { return x; }
  12. public int getY() { return y; }
  13. }

先用以下的指令來編譯 Point2D 類別:

  1. javac -d . Point2D.java

在編譯過後,在 onlyfun 目錄的 caterpillar 目錄下會有 Point2D.class 檔案,而 “package” 所設定的名稱就成為類別名稱的一部份,也就是完整的類別名稱是 onlyfun.caterpillar.Point2D,除非您改變套件名稱並重新編譯類別,否則的話無法改變這個名稱,為了要使用 onlyfun.caterpillar.Point2D 類別,方法之一是使用「完全描述」(Fully qualified)名稱,也就是完整的指出「套件加類別」名稱,如範例 9.6 所示。

範例 9.6 Point2DDemo.java

  1. public class Point2DDemo {
  2. public static void main(String[] args) {
  3. onlyfun.caterpillar.Point2D p1 = new
  4. onlyfun.caterpillar.Point2D(10, 20);
  5. System.out.printf("p1: (x, y) = (%d, %d)%n",
  6. p1.getX(), p1.getY());
  7. }
  8. }

執行結果如下所示:

  1. p1: (x, y) = (10, 20)

設定了套件名稱的類別,必須放置在對應的目錄中,例如若 Point2D 的套件設定為 onlyfun.caterpillar,則最後編譯完成的 .class 檔案必須放在 onlyfun 目錄的 caterpillar 目錄下,否則編譯時會有以下的錯誤發生:

  1. bad class file: .\Point2D.class
  2. class file contains wrong class: onlyfun.caterpillar.Point2D
  3. Please remove or make sure it appears in the correct subdirectory of the classpath.
  4. Point2D p1 = new Point2D(10, 20);
  5. ^
  6. 1 error

如果您在編譯時沒有使用 “-d” 並指定 .class 檔案產生的目標目錄,則編譯完成之後,您也要自己建立對應於套件的目錄結構,然後將 .class 放入對應的目錄之下。

良葛格的話匣子 在命名套件時,可以使用組織的網域名稱來作為開頭命名,通常是倒過來命名,例如網域如果是 openhome.cc,則命名套件時可以用 cc.openhome,之後再加上您自己設定的套件名稱,這麼一來同名的衝突機會可以更少。

9.2.2 import 的意義

如果您有使用 “package” 來為您的類別設定套件管理,則編譯過後 “package” 所設定的名稱就成為類別名稱的一部份,在範例 9.6中,您使用「完全描述」(Fully qualified)名稱來指定使用的類別,當然這個方法要打一長串的文字,因而使用上不是很方便,您可以使用 “import” 關鍵字,告知編譯器您所要使用的類別是位於哪一個套件,如此您可以少打一些字,讓編譯器多作一些事,例如範例 9.6 可以改寫為範 例9.7。

範例 9.7 Point2DDemo2.java

  1. import onlyfun.caterpillar.Point2D;
  2. public class Point2DDemo2 {
  3. public static void main(String[] args) {
  4. Point2D p1 = new Point2D(10, 20);
  5. System.out.printf("p1: (x, y) = (%d, %d)%n",
  6. p1.getX(), p1.getY());
  7. }
  8. }

雖然在範例 9.7 中新建 onlyfun.caterpillar.Point2D 物件時,只指定了 Point2D 名稱,但編譯器從一開頭的 “import” 設定得知,完整的類別名稱應該是 onlyfun.caterpillar.Point2D,因而可以順利編譯,執行結果與範例 9.6 是相同的。

在使用 “import” 時可以指定類別的完整描述,如果您會使用到某個套件下的許多類別,在使用 “import” 指定時,可以於套件指定後加上 ‘*‘,這表示您會使用到該套件下的某些類別,編譯器會自己試著找出類別,例如範例 9.7 還可以再改為範例 9.8 的寫法。

範例 9.8 Point2DDemo3.java

  1. import onlyfun.caterpillar.*;
  2. public class Point2DDemo3 {
  3. public static void main(String[] args) {
  4. Point2D p1 = new Point2D(10, 20);
  5. System.out.printf("p1: (x, y) = (%d, %d)%n",
  6. p1.getX(), p1.getY());
  7. }
  8. }

編譯器在處理這個程式時,會先試著在現行工作路徑下找有無 Point2D 類別,如果找不到的話,編譯器會試著組合 “import” 上的設定來找尋 Point2D 類別,就範例 9.8 而言,會將 onlyfun.caterpillar 與 Point2D 組合在一起,然後試著找到 onlyfun.caterpillar.Point2D 類別。

您也許會發現無法編譯範例 9.8,可能出現以下的錯誤訊息:

  1. bad class file: .\Point2D.java
  2. file does not contain class Point2D
  3. Please remove or make sure it appears in the correct subdirectory of the classpath.

這不是程式撰寫有誤,而是因為您 “import” 時使用了 ‘*‘,並且您的 Point2D.java 原始檔案也在同一個目錄,照之前編譯器尋找類別順序的說明,編譯器會先找到 Point2D.java,但發現當中有設定套件,而 Point2D.java 沒有在對應的目錄(onlyfun/caterpillar)下,所以編譯器認定這是個錯誤。

將原始碼與編譯完成的類別檔放在一起容易發生這類的問題,事實上將原始碼與編譯完成的檔案放在一起並不是一個好的管理方式,您可以建一個專門放原始碼 .java 檔案的目錄 src,並建一個專門放 .class 檔案的目錄 classes,編譯時這麼下指令:

  1. javac -d ./classes ./src/*.java

這麼一來產生的 .class 就會儲存在 classes 目錄下,可以至 classes 目錄下直接執行程式:

  1. java Point2DDemo3

或者直接在執行編譯時的工作目錄下,以指定 Classpath 的方式如下執行程式:

  1. java -cp ./classes Point2DDemo3

但要注意的是,如果您使用 “import” 之後,出現類別名稱有同名衝突時,編譯器就不知道如何處理了,例如:

  1. import java.util.Arrays;
  2. import onlyfun.caterpillar.Arrays;
  3. public class SomeClass {
  4. ....
  5. }

在這個例子中,編譯器從 “import” 上發現有兩個可能的 Arrays 類別,它不確定若遇到 Arrays 時您要使用的是 java.util.Arrays,或是 onlyfun.caterpillar.Arrays,編譯器只好回報錯誤訊息:

  1. java.util.Arrays is already defined in a single-type import
  2. import onlyfun.caterpillar.Arrays;
  3. ^
  4. 1 error

這個時候您就要考慮換一下類別名稱了(如果您有權更動那些類別的話),或者是不使用 “import”,直接使用完整描述;在 “import” 時儘量不因為貪圖方便而使用 ‘*‘,也可以減少這種情況的發生。

良葛格的話匣子 使用 “import” 就是在告知編譯器您的類別位於哪一個套件下,而編譯器尋找類別最先是根據 Classpath 的設定,所以您也要瞭解 Classpath 的設定,建議您也看看官方網站上的 Classpath 設定文章,您對套件的瞭解會更深入:

Java SE 平台的 .class 檔案是儲存在 JRE 安裝目錄的 /lib 下的 rt.jar 中,而額外的第三方(Third- party)元件可以放 /lib/ext 中,以及您自己設定的 Classpath。

9.2.3 public 與套件

一個類別在定義時可以使用 “public” 加以修飾,一個 .java 檔案中可以定義數個類別,但只能有一個被宣告為 “public”,沒有被宣告為 “public” 的類別只能被同一個套件中的類別之實例呼叫使用,例如將範例 9.5 中 onlyfun.caterpillar.Point2D 類別上的 “public” 拿掉並重新編譯,接著再編譯 Point2DDemo.java 檔案時,會出現以下的錯誤,因為 Point2DDemo(預設套件)與 onlyfun.caterpillar.Point2D 不在同一個套件:

  1. Point2DDemo.java:3: onlyfun.caterpillar.Point2D is not public in onlyfun.caterpillar; cannot be accessed from outside package
  2. onlyfun.caterpillar.Point2D p1 = new

類別成員也可以宣告為 “public”,宣告為 “public” 的類別成員可以被其它物件呼叫使用,如果宣告類別時不使用 “public”、”protected” 或 “private” 設定權限,則預設為「套件存取範圍」,只有同一個套件中的類別可以呼叫這些類別成員,例如範例 9.5 中將 getX()、getY() 上的 “public” 拿掉並重新編譯,接著再編譯 Point2DDemo.java 時,會出現以下的錯誤,因為 Point2DDemo(預設套件)與 onlyfun.caterpillar.Point2D 不在同一個套件:

  1. Point2DDemo.java:7: getX() is not public in onlyfun.caterpillar.Point2D; cannot be accessed from outside package
  2. p1.getX(), p1.getY());
  3. ^

類別上的權限設定會約束類別成員上的權限設定,所以如果類別上不宣告 “public”,而類別成員上設定了 “public”,則類別成員同樣的也只能被同一個套件的類別存取,也就是說如果您這麼撰寫程式:

  1. package onlyfun.caterpillar;
  2. class SomeClass {
  3. // ...
  4. public void someMethod() {
  5. // ....
  6. }
  7. }

其效果等同於:

  1. package onlyfun.caterpillar;
  2. class SomeClass {
  3. // ...
  4. void someMethod() {
  5. // ....
  6. }
  7. }

由這邊的討論,可以再來看看預設建構方法的權限。首先要知道的是,當您在 Java 中定義一個類別,但沒有定義建構方法時,編譯器會自動幫您產生一個預設建構方法,也就是說,如果您這麼寫:

  1. package onlyfun.caterpillar;
  2. public class Test {
  3. ....
  4. }

則編譯器會自動加上預設建構方法,也就是相當於這麼寫:

  1. package onlyfun.caterpillar;
  2. public class Test {
  3. public Test() {
  4. }
  5. ....
  6. }

如果您自行定義建構方法,則編譯器就不會幫您加上預設建構方法,所以當您這麼定義時:

  1. package onlyfun.caterpillar;
  2. public class Test {
  3. public Test(int i) {
  4. ...
  5. }
  6. ....
  7. }

則在建構時,就必須指明使用哪個建構方法,簡單的說,您就不能使用以下的方式來建構:

  1. Test test = new Test();

有時會建議即使沒有用到,在定義自己的建構方法的同時,也加上個沒有參數的建構方法,例如:

  1. package onlyfun.caterpillar;
  2. public class Test {
  3. public Test() { // 即使沒用到,也先建立一個空的建構方法
  4. }
  5. public Test(int i) {
  6. ...
  7. }
  8. ....
  9. }

要注意的是,在繼承時,如果您沒有使用 super() 指定要使用父類別的哪個建構方法,則預設會尋找父類別中無參數的建構方法。

預設建構方法的存取權限是跟隨著類別的存取權限而設定,例如:

  1. package onlyfun.caterpillar;
  2. public class Test {
  3. }

由於類別宣告為 public,所以預設建構方法存取權限為 public。如果是以下的話:

  1. package onlyfun.caterpillar;
  2. class Test {
  3. }

則預設建構方法存取權限為套件存取權限,也就是編譯器會自動為您擴展為:

  1. package onlyfun.caterpillar;
  2. class Test {
  3. Test() {
  4. }
  5. }

在這邊整理一下 private、protected、public 與 default 與類別及套件的存取關係:

表 9.1 權限設定為套件的關係

存取修飾 同一類別 同一套件 子類別 全域
private OK
(default) OK OK
protected OK OK OK
public OK OK OK OK

9.2.4 import 靜態成員

在 J2SE 5.0 後新增了 “import static” 語法,它的作用與 “import” 類似,都是為了讓您可以省一些打字功夫,讓編譯器多作一點事而存在的。”import static” 是使用時的語法,原文的文章或原文書中介紹這個功能時,大都用 “static import” 描述這個功能,編譯器訊息也這麼寫,但為了比較彰顯這個功能的作用,這邊稱之為「import 靜態成員」。

使用 “import static” 語法可以讓您 “import” 類別或介面中的靜態成員,一個實際的例子如範例 9.9 所示。

範例 9.9 HelloWorld.java

  1. import static java.lang.System.out;
  2. public class HelloWorld {
  3. public static void main(String[] args) {
  4. out.println("Hello! World!");
  5. }
  6. }

在範例中您將 java.lang.System 類別中的 out 靜態成員 “import” 至程式中,編譯時編譯器遇到 out 名稱,可以從 “import static” 上知道 out 是 System 中的靜態成員,因而自動展開為 System.out 並加以編譯。

再來看一個例子,Arrays 類別中有很多的靜態方法,為了使用方便,您可以使用 “import static” 將這些靜態方法 “import” 至程式中,如範例 9.10 所示。

範例 9.10 ImportStaticDemo.java

  1. import static java.lang.System.out;
  2. import static java.util.Arrays.sort;
  3. public class ImportStaticDemo {
  4. public static void main(String[] args) {
  5. int[] array = {2, 5, 3, 1, 7, 6, 8};
  6. sort(array);
  7. for(int i : array) {
  8. out.print(i + " ");
  9. }
  10. }
  11. }

編譯器遇到範例 9.10 中的 sort() 方法,可以從 “import static” 上知道 sort() 方法是 java.util.Arrays 上的靜態成員,執行結果如下:

  1. 1 2 3 5 6 7 8

如果您想要 “import” 類別下所有的靜態成員,也可以使用 ‘*‘ 字元,例如將範例 9.10 中的 “import static” 改為以下也是可行的:

  1. import static java.util.Arrays.*;

“import static” 語法可以讓您少打一些字,但是您要注意名稱衝突問題,對於名稱衝突編譯器可能透過以下的幾個方法來解決:

  • 成員覆蓋

    如果類別中有同名的資料成員或方法,則優先選用它們。

  • 區域變數覆蓋

如果方法中有同名的變數名或參數名,則選用它們。

  • 重載(Overload)方法上的比對

    嘗試使用重載機制判斷,也就是透過方法名稱及參數列的比對來選擇適當的方法。

如果編譯器無法判斷,則會回報錯誤,例如若您定義了如下的類別:

  1. package onlyfun.caterpillar;
  2. public class Arrays {
  3. public static void sort(int[] arr) {
  4. // ....
  5. }
  6. }

然後如下撰寫程式:

  1. import static java.lang.System.out;
  2. import static java.util.Arrays.sort;
  3. import static onlyfun.caterpillar.Arrays.sort;
  4. public class ImportStaticDemo2 {
  5. public static void main(String[] args) {
  6. int[] array = {2, 5, 3, 1, 7, 6, 8};
  7. sort(array);
  8. for(int i : array) {
  9. out.print(i + " ");
  10. }
  11. }
  12. }

由於從 java.util.Arrays.sort 與 onlyfun.caterpillar.Arrays.sort 的兩行 “import static” 上都可以找到 sort,編譯器無法辦別要使用哪一個 sort() 方法,因而編譯時會出現以下的錯誤:

  1. ImportStaticDemo2.java:9: reference to sort is ambiguous, both method sort(int[]) in onlyfun.caterpillar.Arrays and method sort(int[]) in java.util.Arrays match
  2. sort(array);
  3. ^
  4. 1 error

9.3 接下來的主題

每一個章節的內容由淺至深,初學者該掌握的深度要到哪呢?在這個章節中,對於初學者我建議至少掌握以下幾點內容:

  • 會定義成員內部類別
  • 會使用匿名內部類別
  • 知道內部類別編譯過後的 .class 檔名稱命名方式
  • 會使用套件整理類別並瞭解其與實體目錄之關係
  • 知道”public”與套件的權限關係
  • 瞭解”import”、”import static” 的真正目的

一個程式的撰寫的過程中,如何避免程式執行時的錯誤,往往佔了程式開發時程的絕大多數時間,對於避免程式執行時的錯誤,Java 提供了例外處理機制,事實上到目前為止,您可能已經遇過不少 Exception 的實例了,下一個章節將說明這些例外是什麼意義,以及如何處理這些實例。