目錄

2023 興大物件導向程式設計期中考歷屆題

前言

希望你在閱讀這是考古題詳解的時候,並不只是「知道」正確答案是什麼。有時候我們也能從其它錯誤選項學到一些重要觀念。

我在撰寫本篇時,會盡可能把每個選項都做解釋,若發現題目、詳解有任何錯誤也請不吝指教。祝大家考試順利。

簡介、查閱說明與更新記錄

此篇文章為 2023 年 4 月 28 日考的物件導向程式設計的考古題。

  • 2024/04/16:發佈文章
  • 2024/04/21:
    • 補全 Q16 中對 equals 的解釋。
    • Q1tahtthat

Q1

Given the java statement
int number1 = input.nextInt();
in which input is Scanner, which of the following occurs if the user does not enter a valid int value?

a. A complication error occurs.
b. The program continues executing and assigns the value 0 to number1.
c. A runtime logic error occurs.
d. None of the above

A1

正解:c. A runtime logic error occurs.

詳解:

a. A complication error occurs.

因為題目說 input 已經是一個 Scanner 物件,所以 int number1 = input.nextInt(); 並沒有語法上的錯誤,也可以通過編譯。

b. The program continues executing and assigns the value 0 to number1.
c. A runtime logic error occurs.

Java 並不會在使用者亂輸入時直接回傳 input.nextInt()0,而是會報錯。(如果只要亂輸入就回傳 0 而不報錯,出現 bug 時會很難處理)

我們把 b 跟 c 兩個選項放在一起看。因為我們在編譯階段並不會知道使用者會輸入一個 invalid 的 int value,所以很顯然要執行程式時才能得知。

當 Java 在執行時 (runtime) 發現使用者沒有輸入有效的 int 值,會報錯,所以答案是 c。

Q2

Java requires a _________ call for every object that’s created.

a. constructor
b. destructor
c. parameterless
d. parameterized

A2

正解:a. constructor

Java 在創建物件 (object) 的時候會呼叫該 class 的建構子 (constructor)。

其它選項:

b. destructor

仔細觀察 destructor,會發現它跟正解 constructor 長得很像。而且 de- 字根的開頭似乎暗示我們它跟 constructor 是反義。

事實上 destructor 的中文是解構子,正好是建構子 (constructor) 的相反。如果 constructor 會在物件被創建時被呼叫,那麼我們可以很合理的猜測 destructor 會在物件被銷毀時被呼叫。

在某些程式語言中(像是 C++)當我們使用完記憶體中的物件時需要手動釋放它。這時就要手動呼叫 destructor

不過在 Java 中因為在存在垃圾回收器 (Garbage Collector,簡稱 GC),所以我們並不能手動銷毀物件。物件會在使用結束時被自動銷毀。

c. parameterless

這是指沒有參數的 constructor,在 Java 中如果你不定義任何 constructor,編譯器會自動提供一個無參數的默認 constructor。但如果你至少定義了一個 constructor,就不會有默認 constructor 了。

d. parameterized

這是指帶有參數的 constructor

Q3

Reference-type variables (called references) store ________ in memory.

a. the value of an object
b. a copy of an object
c. the location of an object
d. the size of an object

A3

正解:c. the location of an object

Reference-type variables(參考類型的變數)儲存的是對象的記憶體位置。

舉個例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Dog {
    String name;

    Dog(String name) { // constructor
        this.name = name;
    }
}

public class MainApp {
    public static void main(String[] args) {
        Dog myDog = new Dog("Ruby");
        System.out.println(myDog.name); // 輸出: Ruby
    }
}

在這個例子中,myDog 是一個參考類型的變數,它儲存的是一個 Dog 類型物件的位置。當 new Dog("Ruby") 被調用時,它在記憶體中創建了一個 Dog 類型的實例,並且 myDog 儲存了這個物件的參考。

也就是說 myDog 儲存的是一個物件的記憶體位置。任何對 myDog 物件的操作實際上都是在對它儲存的記憶體位置間接操作。

myDog 示意圖

如圖,myDog 實際上儲存的是記憶體的位置,在那個記憶體位置你可以找到我們創建完的 Dog 物件。

Q4

Which of the following code segments does not increment val by 3:

a. val += 3;

b.

1
2
3
val = val + 1;
val = val + 1;
val = val + 1;

c.

1
2
c = 3;
val = val + (c == 3 ? 2 : 3);

d. All of the above increment val by 3.

A4

正解:c 選項

詳解:

a. val += 3;

這裡用到了 += 這個運算子,val += 3 等效 val = val + 3。所以它有把 val 變為原值加上 3

b.

1
2
3
val = val + 1;
val = val + 1;
val = val + 1;

每一行 val = val + 1; 都會把 val 變為原值加上 1。因為共有三行,所以最終 val 會是原值加上 3

c.

1
2
c = 3;
val = val + (c == 3 ? 2 : 3);

這裡用到了一個三元運算子,它可以用來做一些簡單的判斷,以下舉例:

1
2
3
4
5
6
// 三元運算子的用法
條件 ? 當條件為 True 的回傳值 : 當條件為 False 的回傳值

// 本題 c 選項中的三元運算子
// c == 3 嗎?若是就回傳 2 : 否則回傳 3;
c == 3 ? 2 : 3;

因為這個選項有先宣告一個 c,並且賦值為 3。所以很顯然 c == 3 的值是 True

所以 c == 3 ? 2 : 3 會回傳 2

1
2
3
4
c = 3;
val = val + (c == 3 ? 2 : 3);
// 相當於
val = val + 2;

val 會變為原值加 2,而不是原值加 3,故選 c.

Q5

What is the size in bits of an int?

a. 8
b. 16
c. 32
d. 64

A5

正解:c. 32

在 Java 中,int 的大小是 4bytes,一個 byte 等於八個 bits,故 int 的大小是 32 bits,故選 c.

Q6

To exit out of a loop completely, and resume the flow of control at the next state ment after the loop, use a _______.

A6

這題跟 2022 年 Test1 第 15 題一模一樣,傳送門點我

正解:b. break statement

詳解:

a. continue:進入「下一圈迴圈」

1
2
3
4
5
6
for(int i = 0; i < 10; i++) {
    if(i % 2 == 0) {
        continue;
    }
    System.out.println(i);
}

這個程式會印出 1, 3, 5, 7, 9。
因為每個偶數都會符合 i % 2 == 0,然後 continue 進到下一圈,而不印出數字。

b. break:如題目所述,會跳出整個迴圈

c. return:會回傳值,讓整個方法都結束

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class MathTool {
    static int calculateAPlusToB(int a, int b) { // 計算 a 加到 b
        int sum = 0;
        for(int i = a; i <= b; i++) {
            sum += i;
            if(i == b) {
                return sum;
            }
        }
        System.out.println("我最喜歡線性代數了")
    }
}

在以上這個例子中,當我們呼叫 MathTool(1, 10),程式會在 i == 10 的時候回傳 1 加到 10 的總和。因為已經 return,整個 method 已經結束,所以就不會出印出 "我最喜歡線性代數了"

Q7

boolean values can be displayed as the words true and false with the _________ format specifier.

a. %bool
b. %b
c. %true
d. %boolean

A7

這題跟 2022 年 Test1 第 14 題一模一樣,傳送門點我

正解:b. %b

詳解:背起來就對了

Q8

Any field declared with keyword _________ is constants.
a. Static
b. const
c. constant
d. final

A8

正解:d. final

詳解:

a. Static

當變數或方法前面出現 Static,就代表這個變數/方法是屬於整個 class,而非單一個物件。

1
2
3
4
5
6
7
8
class MathTool {
    static public int staticATimesB(int a, int b) {
        return a * b;
    }
    public int aTimesB(int a, int b) {
        return a * b;
    }
}

在這個例子中,雖然 staticATimesBaTimesB 方法的參數、回傳值都一樣,但是一個有被加上 static 的關鍵字,使用方法就會改變。

static 靜態方法的使用方法是類名.方法名

1
2
// 印出 6
System.out.println(MathTool.staticATimesB(2, 3));

而非 static 方法的使用方法是被創建的物件名.方法名

1
2
MathTool tl = new MathTool();
System.out.prinln(tl.staticATimesB(2, 3));

b. const

這是個誤導選項。const 在其它程式語言(例如 C/C++)中常用來宣告常數。但在 Java 中並沒有這個關鍵字,Java 是用 final 關鍵字來宣告常數。

c. constant

這和 b 選項一樣也不是 Java 的關鍵字,這只是一個普通的英文單字,意思是「常數、不變的值」。

d. final

這是正解,在 Java 中想要宣告一個不變的值時,我們會在前面加上 final。你可以這樣記憶:「既然已經是最後一次修改 (final) 了,這個變數就不會再變了。」

Q9

Overloaded methods always have the same ________.

a. method name
b. return type
c. number of parameters
d. order of the parameters

A9

正解:a. method name

關於重載 (overload) 的用法,先前在 2022 年 Test1 第 5 題也有提及過,傳送門點我

詳解:

在重載的時候,method 的名稱一定是一樣的(不然怎麼叫 “重載” 呢?名稱若不同,就是不同的方法而已)

舉例說明 Overload 的用法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Dog {
    int age;
    String name;
    boolean gender; // true 為公;false 為母
    Dog () { // 什麼都不輸入時
        this.age = 0; // 預設狗狗剛出生,0 歲
        this.name = "Ruby"; // 預設叫作 Ruby
        this.gender = false; // 預設是隻母狗
    }
    Dog (int age, String name) { // 只有輸入年齡、名字時
        this.age = age;
        this.name = name;
        boolean gender = false; // 預設是隻母狗
    }
    Dog (int age, String name, boolean gender) { // 同時輸入年齡、名字、性別時
        this.age = age;
        this.name = name;
        this.gender = gender;
    }
}

上面例子中你可以看到與 Class 名稱相同的 Constructor 多達三個!天啊!這麼多 Constructor,我在宣告物件時 Java 怎麼會知道要用誰呢?

答案就是靠你傳入的變數的數量還有類型

1
2
3
Dog myDog = new Dog(); // 使用無參數的 Constructor
Dog hisDog = new Dog(2, "小黑"); // 使用兩個參數的 Constructor
Dog herDog = new Dog(5, "小白", true); // 使用三個參數的 Constructor

Q10

A set of named constants that start with the value 0 for the first constant and increment by 1 for each subsequent constant can be declared as a(n) ________.

a. class
b. enum
c. enumeration
d. None of the above.

A10

正解:b. enum

詳解:

事實上我認為題目中的 start with the value 0 for the first constant and increment by 1 for each subsequent constant 有誤導的問題。我會在 b 選項的詳解中提到這一點。

a. class

class 用來定義類。在 Java 中一個類可以包含變數、方法、建構子等,但它並不是這題的最佳答案。

b. enum

在 Java 中,enum 是一種特殊的類型,用來宣告一組具名的常數,舉例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public enum Day {
    SUNDAY,
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
}

public class MainApp {
    public static void main(String[] args) {
        Day today = Day.MONDAY;
        if(today == Day.SATURDAY || today == Day.SUNDAY){
            System.out.println("好耶放假");
        } else {
            System.out.println("哭啊要上班上課");
        }
    }
}

當我們宣告出 Day 這個 enum 之後,我們就可以很明確的用 Day.MONDAY 代表禮拜一、Day.TUESDAY 代表禮拜二…。這可以增加程式的可讀性,讓程式更好維護管理。

你可能此時會想:

喔 ~ enum 是這樣用啊…但…題目說的 value start from 0… 哪來的 value??

我認為題目想問的可能是序數,也就是順序。我們可以用 .ordinal() 方法取得某個 enum 中的某個常量的順序是第幾個:

1
2
3
4
5
6
7
8
public enum Color {
    RED,    // 序數為 0
    GREEN,  // 序數為 1
    BLUE    // 序數為 2
}

// 印出 RED 的序數
System.out.println(Color.RED.ordinal()); // 輸出將是 0

這是我對題目的理解,我有可能是錯的。若有人知道題目真正所指的意思是什麼,可以在下方留言為我指點迷津。但目前就我的理解,題目出得不太好。

c. enumeration

這個選項跟 b 看起來很像,但 enumeration 並不是 Java 的一個關鍵字。這個單詞的意義是「枚舉」。可以說是小小的誘答選項。

Q11

Which expression adds 1 to the element of array arrayName at index i?

a. ++arrayName[i]
b. arrayName++[i]
c. arrayName[i++]
d. None of the above

A11

正解:a. ++arrayName[i]

詳解:

a. ++arrayName[i]

++ 運算子的使用方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int x = 5;
int y;

// 第一種:後置
y = x++;
// 等效於:
y = x; // 先把 x 賦值給 y
x = x + 1; // 讓 x 加一

// 第二種:前置
y = ++x;
// 等效於:
x = x + 1; // 先讓 x 加一
y = x; // 把 x 賦值給 y

++ 運算子放在變數的前方或後方,影響的是他在一條 statement 中的優先順序,但是當我們單獨把 x++++x 寫成一行則沒有影響。(同一條 statement 中只有它自己,沒有優先順序的問題)

1
2
3
int x = 5;
x++;
// 或 ++x,效果一樣

所以 a 選項等效於:

1
2
3
4
// a. 選項
++arrayName[i];
// 等效於
arrayName[i] = arrayName[i] + 1;

b. arrayName++[i]

語法錯誤

c. arrayName[i++]

會先取得 arrayName[i] 的值,然後讓 i = i + 1

1
2
3
4
5
int i = 0;
int a = arrayName[i++];

// 則 i 會變成 1
// a 則為 arrayName[0] 的值

Q12

An exception object’s ________ method returns the exception’s error message.

a. String
b. Message
c. Error
d. toString

A12

正解:d. toString

在許多程式語言中(包括 Java 和 JavaScript)當發生異常時,會建立一個異常物件(exception object)來封裝有關錯誤的資訊。

異常物件通常會包含各種屬性,其中之一是描述異常的錯誤訊息。異常物件的 toString 方法負責以字串的型式回傳錯誤訊息。故答案為 d 選項。

Q13

Which of the following tasks cannot be performed using an enhanced for loop?

a. Calculating the product of all the values in an array.
b. Displaying all even element values in an array.
c. Comparing the elements in an array to a specific value.
d. Incrementing the value stored in each element of the array.

A13

正解:d. Incrementing the value stored in each element of the array.

詳解:

要解這題,先舉個例子說明什麼是 enhanced for loop:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class MainApp {
  public static void main(String[] args) {
      
    // 創一個陣列作舉例
    int[] numbers = {3, 9, 5, -5};
    
    // 傳統的 for loop
    for (int i = 0; i < numbers.length; i++) {
        System.out.println(numbers[i]);  
    }
    
    // enhanced for loop
    for (int x: numbers) {
      System.out.println(x);
    }
  }
}

在傳統的 for 迴圈中,我們使用 numbers.length 取得陣列的長度,並且宣告一個 indexer i 負責指向陣列中的第 i 個元素。

在 enhanced for loop 中,則是使用 int x: numbers 的語法,直接告訴 Java:

「請你把 numbers 中的每個元素拿出來,並用每個元素各執行一遍迴圈中的程式碼。對了!取出來的那一個元素就暫且叫它 x 吧!」

在傳統的 for 迴圈中我們是直接使用 numbers[i] 取得陣列的某個元素,所以我們也可以對陣列中的元素做修改(eg. numbers[i] = numbers[i] + 3;:把第 i 個元素變為原值加三)

但 enhanced for loop 需要 Java 直接把整個 for loop 的每一個元素取出來,在上述例子中,我們把取出來的元素宣告為 x。這個 x 實際上是陣列中元素的複製品,所以我們對 x 做操作並不能修改到原陣列 numbers 中的值。

a. Calculating the product of all the values in an array.

可以,計算 product 並不需要修改原陣列的值。

b. Displaying all even element values in an array.

可以,判斷是否為偶數、列印也不需要修改原陣列的值。

c. Comparing the elements in an array to a specific value.

可以,進行比較只需要取值,不需要修改原陣列。

d. Incrementing the value stored in each element of the array.

不行,增加陣列的值(比如講陣列中每個元素的值 + 5)需要修改到原陣列,不能使用 enhanced for loop。

Q14

Which statement below initializes array items to contain 3 rows and 2 columns?

a. int[][] items = {{2, 4}, {6, 8}, {10, 12}};
b. int[][] items = {{2, 6, 10}, {4, 8, 12}};
c. int[][] items = {2, 4}, {6, 8}, {10, 12};
d. int[][] items = {2, 6, 10}, {4, 8, 12};

A14

在講 row 及 column 時建議都用英文溝通。因為中文有地區差異:有些地區 row 翻譯為column 翻譯為;我們則是 row 翻譯為column 翻譯為

正解:a 選項。

詳解:

首先要知道三點:

  1. Java 中二維 int 陣列的類型是 int[][]
  2. 宣告二維陣列的時候是先看 rowcolumn
  3. 陣列中是以 , 分隔項。

圖解:

3 Row 2 Columns 的示意圖

如圖,a 選項有 3 rows 2 columns。

從圖中可以看到,a 選項裡面用 , 分隔的話,總共只有 3 項(圖中第二行畫斜線方塊處),其中每一項就是一個 row

每一個 row 裡面再以 , 分隔,都有兩項,也就是兩個 columns

a. int[][] items = {{2, 4}, {6, 8}, {10, 12}};

正解,有 3 rows 2 columns.

b. int[][] items = {{2, 6, 10}, {4, 8, 12}};

錯誤,有 2 rows 3 columns.

c. int[][] items = {2, 4}, {6, 8}, {10, 12};

語法錯誤,最外層只應該有一對 {},不能直接寫成三對 {}

d. int[][] items = {2, 6, 10}, {4, 8, 12};

語法錯誤,最外層只應該有一對 {},不能直接寫成兩對 {}

Q15

Which method call converts the value in variable stringVariable to an integer?

a. Convert.toInt(stringVariable)
b. Convert.parseInt(stringVariable)
c. Integer.parseInt(stringVariable)
d. Integer.toInt(stringVariable)

A15

正解:c. Integer.parseInt(stringVariable)

詳解:

  1. Convert 並非 Java 中的標準類別,要用 Integer 類。
  2. Integer 類別中不存在 toInt() 這個方法,要用 parseInt() 方法。

你這樣到底解釋了什麼?

沒解釋什麼,這種已經定義好的用法在實務上會在需要的時候翻閱說明文檔或是上網查。作為考試的話,請你背起來。

Q16

Class Arrays provides method _________ for comparing arrays to determine whether they have the same contents.

a. Compare
b. compares
c. equal
d. equals

A16

正解:d. equals

詳解:

假設今天有兩個 Array ab,我們想要比較兩個陣列是否**「相等」**。

當我們在做 a==b 的比較時,實際上是在比較 ab 儲存的記憶體位置是否相等(也就是說:是不是指向同一個陣列)。所以 a!=b

如果想要知道兩個不同的陣列的元素是否全等,則該使用 equals 方法,舉例說明使用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import java.util.Arrays;

public class MainApp {
    public static void main(String[] args) {
        int[] arrayOne = {1, 2, 3, 4, 5};
        int[] arrayTwo = {1, 2, 3, 4, 5};
        int[] arrayThree = {-1, -2, -3, -4, -5};

        boolean resultOne = Arrays.equals(arrayOne, arrayTwo); // 這會返回 true,因為內容完全相同。
        boolean resultTwo = Arrays.equals(arrayOne, arrayThree); // 這會返回 false,因為有元素不同。
    }
}

Q17

Which of the following class members should usually be private?

a. Methods.
b. Constructors.
c. Variables (or fields).
d. All of the above.

A17

這題跟 2022 年 Test1 第 2 題一模一樣,傳送門點我

正解:c. Variables (or fields).

一個程式中的變數 (Variables) 或資料成員 (fields 又稱為 member variable) 在一般情況下應該是 private (也就是不能直接被其它 class 中成員讀取)

舉例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Person {
    private int age; // member variable
    private int weight; // member variable

    Person(int a, int w) { // Constructor
        if(0 <= a && a <= 120) { // 輸入的年齡介於 0 與 120 歲之間
            this.age = a; // 把輸入的年齡存起來
        } else { // 輸入的年齡竟然是負的或是超過 120 歲
            this.age = 18; // 亂填的就當作 18 歲
        }
        this.w = w;
    }

    public void sayMyInfo() { // Methods
        System.out.printf("你好!我已經 %d 歲了,我有 %d 公斤重。", this.age, this.weight);
    }
}

通常我們不會希望其它人可以直接修改一個 Person 物件的 ageweight,因為這樣可能會造成一些不可預測的後果(千年人妖)。在這個例子中,我們會在 Constructor 裡面檢查輸入的年齡有沒有在合理範圍,若超出範圍我們就認定是 18 歲(不錯吧)

a. Methods.

Methods:方法不一定要是 private,如果像例子中的 sayMyInfo 需要給使用者調用,那就會設為 public

b. Constructors.

Constructors:Constructors 是在創造物件時會調用的方法,通常不會設為 private

Q18

Static class variables:

a. are final.
b. are public.
c. are private.
d. are shared by all objects of a class.

A18

這題跟 2022 年 Test1 第 8 題一模一樣,傳送門點我

Static 相關的說明在這篇文章的 A8 也有提及,就不重覆貼了。

Q19

Using public set methods helps provide data integrity if:

a. The instance variables are public.
b. The instance variables are private.
c. The methods perform validity checking.
d. Both b and c.

A19

這題跟 2022 年 Test1 第 7 題一模一樣,傳送門點我

正解:d. Both b and c.

不論是 b 或是 c,透過 setter 設定變數都有助於提供更完整的數據。

透過 A6 中的範例,你可以看到如果我們透過 setter 設定變數的值,就可以確保變數的值一定是我們希望的範圍。所以當 methods 提供 validity checking、instance variables 被設成 private 時,set methods helps provide data integrity.

Q20

Instance variables declared final do not or cannot:

a. Cause syntax errors if used as a left-hand value.
b. Be initialized.
c. Be modified after they are initialized.
d. None of the above.

A20

正解:c. Be modified after they are initialized.

詳解:

final 相關的說明在這篇文章的 A8 也有提及。

a. Cause syntax errors if used as a left-hand value.

被標成 final 的變數只有在一種情況下能被賦值,那就是還沒初始化的時候:

1
2
3
final int a;
a = 5; // 正確,初始化
a = 3; // 出錯,a 被初始化後即不能再改變數值

b. Be initialized.

被標成 final 的變數當然可以被 initialized(初始化)。你可以想想:如果一個變數是常量、又不能初始化,那它是不是沒什麼功用?

c. Be modified after they are initialized.

被標成 final 的變數是常量,被初始化後就不能改變數值了。

手寫題

接下來的部分是手寫題,需要手寫程式碼,且不能用電腦的編譯器檢查語法錯誤。

Q1(手寫)

Write a JAVA application code that generates n random numbers in the range 10-100.
[Note: Only import the Scanner and SecureRandom classes.]

A1(手寫)

要產生範圍是 10 ~ 100 的隨機數,首先要知道:

1
2
SecureRandom sr = new SecureRandom();
sr.nextInt(91);

會產生 0~90 的隨機數(含 90,不含 91)

然後再把產生的數字加上 10:

1
10 + sr.nextInt(91);

即為介於 10 ~ 100 的隨機數

參考解答:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// file name: MainApp.java
import java.util.Scanner;
import java.security.SecureRandom;
public class MainApp {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        SecureRandom sr = new SecureRandom();
        int n = input.nextInt(); // 使用者輸入需產生 n 個數
        int[] numbers = new int[n]; // 宣告一個 n 格長的 int 陣列
        for(int i = 0; i < n; i++) {
            numbers[i] = 10 + sr.nextInt(91);
        }
    }
}

Q2-1(手寫)

What will the following program do?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Q1 {
    public static void main(String[] args) {
        InnerClass in = new InnerClass();
        InnerClass in1 = new InnerClass();
        InnerClass2 in2 = new InnerClass2();
        InnerClass2 in3 = new InnerClass2();
    }
    static class InnerClass {
        static int var4 = 0;
        InnerClass() {
            var4++;
            System.out.println("static variable : " + var4);
        }
    }
    static class InnerClass2 {
        int var5 = 0;
        InnerClass2() {
            var5++;
            System.out.println("non-static variable : " + var5);
        }
    }
}

A2-1(手寫)

分析一下程式碼:

  1. 因為 InnerClass 中的 var4static 的變數,所以整個 InnerClass 會共用一個變數。
  2. InnerClass2 中的 var5 則不是 static 的變數,所以每一個 InnerClass2 型態的物件都會有自己的 var5,值不會互相影響。
  3. InnerClassInnerClass2Constructor 中都會把 var4var5 的值加一,並且印出一行字。

所以答案為:

1
2
3
4
static variable : 1
static variable : 2
non-static variable : 1
non-static variable : 1

Q2-2(手寫)

What will the following program do?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Q2 {
    public static void main(String args[]) {
        star();
        star(7);
        star('@', 9);
    }
    public void star() {
        star(5);
    }
    public static void star(int n) {
        for(int i = 0; i < n; i++) 
            System.out.print("*");
        System.out.println();
    }
    public static void star(char ch, int n) {
        for(int i = 0; i < n; i++)
            System.out.print(ch);
        System.out.println();
    }
}

A2-2(手寫)

分析一下程式碼:

  1. star 這個 method 有被重載,分為無參數、(int n)、(char ch, int n) 三個版本。
  2. 無參數的 star() 會呼叫 star(5)。

所以答案為:

1
2
3
*****
*******
@@@@@@@@@

Q2-3(手寫)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Q3 {
    public static void main(String args[]) {
        int a[] = {2, 7, 6, 3, 8, 4};
        int b[] = {2, 7, 6, 3, 8, 4};
        int c[] = a;

        if(a==b)
            System.out.println("a=b");
        else
            System.out.println("a!=b");

        if(a==c)
            System.out.println("a=c");
        else
            System.out.println("a!=c");
    }
}

A2-3(手寫)

這題要用到 pass-by-valuepass-by-reference 的觀念。

當我們宣告兩個陣列 ab,並且分別將 {2, 7, 6, 3, 8, 4} 值傳給它們,它們是兩個不同的陣列,儲存著相同的值。

當我們宣告陣列 int c[] = a;,則是把 a 陣列的記憶體位置存在 c,也就是說,ca 指向的是在記憶體中的同一個位置,我們也可以說 ca 的參考。

圖解:a, b, c 陣列跟記憶體的關係

圖解:a, b, c 陣列跟記憶體的關係。

另外看看這段程式碼:

1
2
3
4
if(a==b)
    System.out.println("a=b");
else
    System.out.println("a!=b");

當我們在做 a==b 的比較時,實際上是在比較 ab 儲存的記憶體位置是否相等(也就是說:是不是指向同一個陣列)。所以 a!=b

如果想要比較的是陣列中的值有沒有相同,則需要用到本篇文章 Q16 中提到的 equals 方法。

所以此題答案為:

1
2
a!=b
a=c

Q2-4(手寫)

What will the following program do?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public enum Q4 {
    // declare constants of enum type
    RED(50), // light is red for 50 seconds 
    GREEN(40), // light is green for 40 seconds
    YELLOW(5); // light is yellow for 5 seconds

    private final int duration;
    // enum type constructor
    TrafficLight(int durationSeconds) {
        duration = durationSeconds;
    }

    // accessor for duration
    public int getDuration() {
        return duration;
    }
}

public class EnumTest {
    public static void main(String[] args) {
        System.out.printf("Light\tDuration%n%n");

        for(TrafficLight light : TrafficLight.values()) {
            System.out.printf("%s\t%d%n", light, light.getDuration());
        }
    }
}

A2-4(手寫)

如果你把程式碼丟到 IDE 裡面試著編譯執行的話,應該會跑出以下訊息:

1
2
3
4
5
6
7
ERROR!
/tmp/7dzOv7AQ0H/EnumTest.java:9: error: invalid method declaration; return type required
    TrafficLight(int durationSeconds) {
    ^
1 error

=== Code Exited With Errors ===

知道錯誤在哪裡嗎?你猜猜看。







enum 的名稱是 Q4,但 constructor 的名稱卻是 TrafficLight,根本編譯不過…

那我們就回答「編譯錯誤」嗎?很抱歉,看起來是有正解。所以這裡請你假設 enum 的名稱不是 Q4,而是 TrafficLight


在這題會用到一些剛剛沒看到過的 enum 用法。首先,enum 也是可以有自己的變數和方法、constructor 的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public enum TrafficLight {
    // declare constants of enum type
    RED(50), // light is red for 50 seconds 
    GREEN(40), // light is green for 40 seconds
    YELLOW(5); // light is yellow for 5 seconds

    private final int duration;
    // enum type constructor
    TrafficLight(int durationSeconds) {
        duration = durationSeconds;
    }

    // accessor for duration
    public int getDuration() {
        return duration;
    }
}

在這裡比較好的理解方法是將 enum 宣告時列舉以外的部分(像是 constructor、或是屬性和方法)看成一般的 class 宣告。

而每一個 enum 中的枚舉則像是用這個 class 創建出的物件(像是 RED, GREEN, YELLOW)。每一個 enum 的枚舉元素創建的時候都會呼叫一次 constructor,並且每一個 enum 中的枚舉元素都具有自己的屬性、方法。

示意圖:帶有 Constructor 的 enum。

示意圖:帶有 Constructor 的 enum。

由圖中可知,RED, GREEN, YELLOW 就像是物件一樣,都有各自的 duration 屬性和 getDuration() 方法。

因此我們把上面這段程式碼拆成兩個部分來看:

  1. Constructor、屬性和方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public enum TrafficLight {
    private final int duration;
    // enum type constructor
    TrafficLight(int durationSeconds) {
        duration = durationSeconds;
    }

    // accessor for duration
    public int getDuration() {
        return duration;
    }
}

當你把程式拆成兩個部分,並且把第 1 個部分看成是一般的 class 宣告,一切都變得很清楚:

  • duration 是一個 privatefinal 的常量
  • TrafficLight 是 Constructor
  • getDuration 是用來讓外部程式能夠取得 private 變數 durationgetter 接口。

  1. enum 列舉元素
1
2
3
4
5
public enum TrafficLight {
    // declare constants of enum type
    RED(50), // light is red for 50 seconds 
    GREEN(40), // light is green for 40 seconds
    YELLOW(5); // light is yellow for 5 seconds

剛剛說到,我們可以把 enum 中枚舉的元素都當作是用 TrafficLight 這個 class 創建出來的元素,所以:

  • RED 是一個 TrafficLight 物件,我們傳入 50 給它的 Constructor
  • GREEN 是一個 TrafficLight 物件,我們傳入 40 給它的 Constructor
  • YELLOW 是一個 TrafficLight 物件,我們傳入 5 給它的 Constructor

此時我們再回頭看題目中 EnumTest 的部分:

1
2
3
4
5
6
7
8
9
public class EnumTest {
    public static void main(String[] args) {
        System.out.printf("Light\tDuration%n%n");

        for(TrafficLight light : TrafficLight.values()) {
            System.out.printf("%s\t%d%n", light, light.getDuration());
        }
    }
}

這個程式先印出 "Light\tDuration%n%n",其中 \t 以反斜線 \ 開頭,是一個跳脫字元,代表按一下鍵盤上的 Tab 鍵。%n 則是換行。

接著看到 for(TrafficLight light : TrafficLight.values()) {} 這行:

  • TrafficLight.values() 會從 TrafficLight 這個枚舉中取出每一個 enum 的元素(eg. RED, GREEN, YELLOW
  • TrafficLight light 代表我們宣告 light 會是以 TrafficLight 這個 class 建立的物件(像 RED, GREEN, YELLOW 都是這種物件)

所以這個 for 迴圈會把 TrafficLight 的枚舉拿出來,各進行一次迴圈:

1
2
3
for(TrafficLight light : TrafficLight.values()) {
    System.out.printf("%s\t%d%n", light, light.getDuration());
}

進行迴圈時,light 會分別在第一次循環、第二次循環、第三次循環擔任 RED, GREEN, YELLOW 的角色。

還記得我們剛剛說,應該把 RED, GREEN, YELLOW 都看作是 TrafficLight 型別的物件嗎?既然它們是 TrafficLight 型別的物件,它們就會具有 getDuration() 函數可以使用。

所以看到 System.out.printf("%s\t%d%n", light, light.getDuration()); 這行:

  • %s 代表字串,light 代表了枚舉本身,舉例,在第一次 for 循環時,%s 會印出 RED
  • \t 剛剛說過了,是跳脫字元,代表 Tab
  • %d 是十進制整數,對映到 light.getDuration()
  • light.getDuration() 會回傳這個枚舉的 duration 屬性,舉例 RED 會回傳 50
  • %n 剛剛說過了是換行。

所以這題的參考答案是:

1
2
3
4
5
Light   Duration

RED     50
GREEN   40
YELLOW  5

答案中看起來像表格,主因是我們在輸出時都有 \t進行對齊。

Q2-5(手寫)

What will the following program do?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Q5 {
    public static void main(String[] args) {
        int[] a = {99, 22, 11, 3, 11, 55, 44, 88, 2, -1};

        int result = 0;

        for(int i = 0; i < a.length; i++) {
            if(a[i] > 30) {
                result += a[i];
            }
        }
        
        System.out.printf("Result is: %d%n", result);
    }
}

A2-5(手寫)

這個程式會把 a 陣列中每個 > 30 的值加總起來。

答案 = 99+55+44+88 = 286