Java 學習筆記 08 – 類別與物件 Part 2(定義 / 類別成員 / 封裝)


Posted by vickyh1315 on 2024-05-03

Part 1 提到過程導向與物件導向比較,及類別、物件之間的關係,這篇會進一步探討:

  • 如何定義類別、宣告物件
  • 物件導向特性一:封裝
  • 修飾子、靜態成員、方法重寫
  • 物件在記憶體的配置方法
  • 參考型別變數

前面提到,物件導向是一種程式設計方法論,是以定義類別、建立物件的方式,將程式加以組合,降低互相的耦合性,認為物件具有屬性與方法,且屬性的狀態能夠被改變。
當在定義類別名稱,通常採大寫字母字首命名,屬性跟方法也要一起定義進去;由於類別本身就是用來建構物件,Java 有預設的建構子,如果自己想要客製建構程序,就一併定義進去。

class Book {
    // 屬性
    String name;
    int year;
    // 建構子(系統預設定義,也能撰寫自定義屬性與邏輯條件,但一自己定義建構子,Java 就預設不做)
    Book () {
    }
    // 建構子(可以客製帶入引數)
    Book (String name, int year) {
        this.name = name;
        this.year = year;
    }
    // 方法
    void printInfo() {
        System.out.printf("Name: %s, published year: %d", name, year);
    }
}

物件導向三大特性之一 — 封裝

當我們使用 new 關鍵字建立物件(new Book()/new Book("name",2024)),系統判斷帶入參數,選擇對應的建構子(Constructor)Book()/Book(String name, int year) 初始化、建立物件,如此一來,想建立多個物件,就不用一個個指定屬性及方法。

建構子的命名與自己類別名稱相同,使用 new 建構當下,便會執行建構子;類別本身就有不具引數的預設建構子,預設建構子不寫任何內容,但可以重寫加入定義條件,也能撰寫帶引數的建構子,自定義初始屬性與邏輯。

修飾子

考慮安全性,物件的狀態不隨便被外部更動,所以在定義類別時,會透過權限的概念,只允許類別存取特定類別的資料、方法,保護建構好的物件不會被隨意修改,這就是封裝(Encapsulation)的概念,而其中是以存、取修飾子來實現。

查到很多種修飾子的中文說法,有前置修飾子、權限修飾子、控制修飾子等翻譯,而修飾子有 public、private、protected、default 四種。

修飾子 同檔類別讀取 同 package 類別讀取 不同 package 類別讀取
public O O O
private O X X
protected O O 僅子類別可
default O O X

備註:

  1. protected 修飾子標註屬性、方法可讓同檔案、繼承子類別存取。
  2. default 為預設修飾子,可省略不寫。
// public 類別
public class publicClass() {
    public String attr1;
    publicClass() {}
}

// default 類別 (可省略修飾子)
class defaultClass() {
    public String attr1;
    defaultClass() {}
}

getter & setter

適當的封裝可以保護物件細節操作,而外部類別需要物件的資料,只能以公開接入方法取得、編輯內部屬性,慣例上會以 get屬性名()set屬性名() 來命名。

class Book {
    String name;
    int year;
    Book (String name, int year) {
        this.name = name;
        this.year = year;
    }
    // getter
    public int getYear() {
        return this.year;
    }
    // setter
    public void setter(year) {
        this.year = year;
    }
    // printInfo 慣例上用 toString 來寫
    @Override
    public void toString() {
        System.out.printf("Name: %s, published year: %d", name, year);
    }
}

@Override 重寫

原本的 printInfo() 在這裡改用 toString() 方法表達:
這裡要先帶到 所有類別都是繼承於 Object 這個類別 的觀念,toString() 來自於 Object 類別中的定義(java.lang.Object),預設 toString() 輸出是:

package.class@hashCode

由於產生的雜湊碼實用性不高,實務上會拿它重寫,印出方便辨識的物件屬性或其他資訊。

static 靜態成員

類別定義的屬性、方法依照目前範例,都在建立物件後才能呼叫,而靜態成員的存在,則不需建立就能用。

在型態前面加上 static,就表示靜態成員,當一個屬性或方法只要用了 public static 宣告,就可透過類別來呼叫,靜態類別在記憶體配置屬於 global(下面提到),所以每個靜態成員都是唯一的,且不能被覆寫。

public static void main(String[] args) {
    ...
}

這個方法異常熟悉對吧!
每個類別的 main 函式是類別建構的起點,在物件建立前就要執行,也就是為什麼 main 方法得是靜態成員。

Stack & Heap

討論變數時候提到,當宣告一個基本型態變數 & 指定常值,Java 會根據型別為它分配記憶體空間,詳細的說,是 JVM 在記憶體的 Stack 區域分配一塊空間給這個變數儲存常值。

int num = 10;

image
image

JVM 的記憶體配置空間分成 Global、Stack、Heap,其中 Global 放置全域資料,類別共用的靜態成員都儲存在這,類別一定義就建立,不因為產生新類別重複建立。
Stack 中文為堆疊,儲存基本型態變數
Heap 中文為堆積,用來儲存參考型別物件變數

參考型別變數

宣告物件是以參考位址的方式建立,JVM 先在 Stack 區域開一塊空間配置記憶位址給變數,new 建立物件時,根據位址再去 Heap 放物件本體。

討論型別時提到,基本型別包含 byte、short、int、long、float、double、boolean、char 八種。

至於參考型別,則是基本型別以外,又細分可以分四類:

  • 類別:包含自定義類別(自己寫的)、官方類別(String、BigDecimal、Scanner等)、第三方開發類別
  • 陣列(Array)
  • 介面(interface,大小寫要注意)
  • 列舉(enum)

參考型別屬於物件,同樣需要實例化才能使用,語法是

類別/介面名稱 自定義變數名稱 = new 類別物件();
public class DemoClass {

    public static void main(String[] args) {

        /* 通常參考型別變數需要傳入參數並使用構造函數(建構子)實例化
         * 但 String 算是例外,可以直接指定變數,Java 會自動建立實例
         */
        BigDecimal bd = new BigDecimal("1.0");
        String str = new String("test");        String str = "test";
    }

}

這篇記錄了類別如何定義、封裝、修飾子、靜態成員、方法重寫、記憶體儲存位置、參考型別變數,下篇會提到常見的類別們。


#java







Related Posts

Vue.js 學習旅程Mile 13 – List Rendering 列表渲染篇:v-for

Vue.js 學習旅程Mile 13 – List Rendering 列表渲染篇:v-for

Week3:hw4 判斷迴文

Week3:hw4 判斷迴文

 React-[入門篇]- React 起手式 |CRA建立React專案

React-[入門篇]- React 起手式 |CRA建立React專案


Comments