從現實世界到代碼世界:理解類與對象

在編程世界中,我們經常需要描述現實中的事物。比如學生、汽車、手機等。Java 作為一種面向對象的編程語言,提供了類和對象的概念來幫助我們實現這一點。

類就像是一個模板,它定義了某一類事物的共同特徵和行為。例如,"學生"這個類可以包含姓名、年齡、學號等特徵,以及學習、考試等行為。而對象則是根據這個模板創建出來的具體實例。比如,根據"學生"類可以創建出"張三"、"李四"等具體的學生對象。

讓我們通過一個簡單的例子來理解類和對象的概念:

複製

// 定義一個學生類
class Student {
    // 特徵(屬性)
    String name;
    int age;
    String studentId;

    // 行為(方法)
    void study() {
        System.out.println(name + "正在學習");
    }

    void takeExam() {
        System.out.println(name + "正在考試");
    }
}

// 創建對象並使用
public class Main {
    public static void main(String[] args) {
        // 創建學生對象
        Student student1 = new Student();
        student1.name = "張三";
        student1.age = 18;
        student1.studentId = "2023001";

        // 調用對象的方法
        student1.study();  // 輸出:張三正在學習
        student1.takeExam();  // 輸出:張三正在考試

        // 創建另一個學生對象
        Student student2 = new Student();
        student2.name = "李四";
        student2.age = 19;
        student2.studentId = "2023002";

        student2.study();  // 輸出:李四正在學習
    }
}

在這個例子中,Student 就是一個類,它定義了學生應該有哪些特徵和行為。而 student1 和 student2 則是根據 Student 類創建出來的兩個具體的對象,它們各自有自己的屬性值。

類的組成:成員變量與成員方法

一個完整的類通常由兩部分組成:成員變量和成員方法。

成員變量

成員變量(也稱為屬性或字段)用於描述類的特徵。它們是在類中定義的變量,用於存儲對象的數據。例如,在 Student 類中,name、age 和 studentId 都是成員變量。

成員變量可以分為兩種:

  1. 實例變量:屬於對象的變量,每個對象都有自己的一份拷貝。
  2. 類變量(靜態變量):屬於類的變量,所有對象共享同一份拷貝。使用 static 關鍵字修飾。

複製

class Student {
    // 實例變量
    String name;
    int age;
    String studentId;

    // 類變量(靜態變量)
    static String schoolName = "陽光大學";
}

成員方法

成員方法用於描述類的行為。它們是在類中定義的函數,用於操作對象的數據或執行特定的功能。

成員方法也可以分為兩種:

  1. 實例方法:屬於對象的方法,需要通過對象來調用。
  2. 類方法(靜態方法):屬於類的方法,可以直接通過類名來調用。使用 static 關鍵字修飾。

複製

class Student {
    String name;
    int age;
    String studentId;
    static String schoolName = "陽光大學";

    // 實例方法
    void study() {
        System.out.println(name + "正在學習");
    }

    // 類方法(靜態方法)
    static void showSchoolName() {
        System.out.println("學校名稱:" + schoolName);
    }
}

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.name = "張三";

        // 調用實例方法
        student.study();  // 輸出:張三正在學習

        // 調用類方法
        Student.showSchoolName();  // 輸出:學校名稱:陽光大學
    }
}

類與對象的內存分配

理解類和對象在內存中的分配情況,有助於我們更好地理解它們的工作原理。

當我們定義一個類時,Java 虛擬機並不會為其分配內存空間。只有當我們使用 new 關鍵字創建對象時,才會在內存中為對象分配空間。

對象在內存中的分配情況如下:

  1. 當創建一個對象時,會在堆(Heap)內存中為該對象分配空間,用於存儲對象的實例變量。
  2. 類的信息(包括類變量和方法)存儲在方法區(Method Area)中,所有對象共享這些信息。
  3. 引用變量(用於指向對象的變量)存儲在棧(Stack)內存中。

Java21天學習計劃 - 第五天:類與對象基礎_成員變量

讓我們通過一個例子來理解這個過程:

複製

class Student {
    String name;
    int age;
    static String schoolName = "陽光大學";
}

public class Main {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "張三";
        student1.age = 18;

        Student student2 = new Student();
        student2.name = "李四";
        student2.age = 19;
    }
}

在這個例子中:

  1. 當程序啓動時,Student 類的信息(包括靜態變量 schoolName)被加載到方法區。
  2. 當執行 Student student1 = new Student(); 時,在堆內存中為 Student 對象分配空間,並將對象的地址賦給棧內存中的 student1 變量。
  3. 類似地,創建 student2 對象時,也會在堆內存中分配新的空間。
  4. student1 和 student2 有各自的 name 和 age 實例變量,但共享同一個 schoolName 靜態變量。

構造方法與方法重載

構造方法

構造方法是一種特殊的方法,用於在創建對象時初始化對象。它具有以下特點:

  1. 構造方法的名稱必須與類名相同。
  2. 構造方法沒有返回類型,包括 void。
  3. 構造方法在創建對象時自動調用。

如果我們沒有顯式地定義構造方法,Java 會為我們提供一個默認的無參構造方法。

複製

class Student {
    String name;
    int age;

    // 無參構造方法
    Student() {
        System.out.println("調用了無參構造方法");
    }

    // 有參構造方法
    Student(String n, int a) {
        name = n;
        age = a;
        System.out.println("調用了有參構造方法");
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用無參構造方法創建對象
        Student student1 = new Student();

        // 使用有參構造方法創建對象
        Student student2 = new Student("張三", 18);
        System.out.println("姓名:" + student2.name + ",年齡:" + student2.age);
    }
}

方法重載

方法重載是指在同一個類中,可以定義多個同名但參數列表不同的方法。參數列表不同包括參數的個數不同、類型不同或順序不同。

構造方法也可以重載,這使得我們可以通過不同的方式來創建對象。

複製

class Student {
    String name;
    int age;
    String studentId;

    // 無參構造方法
    Student() {
        name = "未知";
        age = 0;
        studentId = "未知";
    }

    // 有一個參數的構造方法
    Student(String n) {
        name = n;
        age = 0;
        studentId = "未知";
    }

    // 有兩個參數的構造方法
    Student(String n, int a) {
        name = n;
        age = a;
        studentId = "未知";
    }

    // 有三個參數的構造方法
    Student(String n, int a, String id) {
        name = n;
        age = a;
        studentId = id;
    }

    // 普通方法的重載
    void study() {
        System.out.println(name + "正在學習");
    }

    void study(String course) {
        System.out.println(name + "正在學習" + course);
    }
}

public class Main {
    public static void main(String[] args) {
        Student student1 = new Student();
        Student student2 = new Student("張三");
        Student student3 = new Student("李四", 19);
        Student student4 = new Student("王五", 20, "2023003");

        student3.study();  // 輸出:李四正在學習
        student4.study("Java編程");  // 輸出:王五正在學習Java編程
    }
}

封裝與訪問修飾符

封裝的概念

封裝是面向對象編程的三大特性之一(另外兩個是繼承和多態)。它指的是將對象的狀態(成員變量)和行為(成員方法)捆綁在一起,並對外部隱藏對象的內部實現細節,只通過公共的接口來訪問對象。

封裝的主要目的是:

  1. 提高代碼的安全性:防止外部代碼隨意修改對象的內部狀態。
  2. 提高代碼的可維護性:將對象的實現細節隱藏起來,使得修改對象的內部結構時不會影響到外部代碼。
  3. 簡化代碼的使用:只需要關心對象提供的公共接口,而不需要了解其內部實現。

訪問修飾符

Java 提供了四種訪問修飾符來控制類、成員變量和成員方法的訪問權限:

  1. private:私有的,只能在本類中訪問。
  2. default(默認,不寫修飾符):包級私有的,只能在本包中訪問。
  3. protected:受保護的,只能在本類、子類和本包中訪問。
  4. public:公共的,可以在任何地方訪問。

以下是訪問修飾符的權限對比表:

表格

複製

修飾符

本類

同包

子類

其他包

private

×

×

×

default

×

×

protected

×

public

封裝的實現

要實現封裝,我們通常需要:

  1. 使用 private 修飾符來修飾成員變量,使其只能在本類中訪問。
  2. 提供公共的 getter 和 setter 方法來訪問和修改成員變量,在這些方法中可以添加數據驗證等邏輯。

複製

class Student {
    // 使用 private 修飾成員變量
    private String name;
    private int age;
    private String studentId;

    // getter 方法:用於獲取成員變量的值
    public String getName() {
        return name;
    }

    // setter 方法:用於設置成員變量的值
    public void setName(String name) {
        // 可以在這裏添加數據驗證
        if (name != null && !name.isEmpty()) {
            this.name = name;
        } else {
            System.out.println("姓名不能為空");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        // 可以在這裏添加數據驗證
        if (age >= 0 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("年齡必須在0到120之間");
        }
    }

    public String getStudentId() {
        return studentId;
    }

    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }

    // 構造方法
    public Student(String name, int age, String studentId) {
        setName(name);  // 調用 setter 方法進行數據驗證
        setAge(age);    // 調用 setter 方法進行數據驗證
        this.studentId = studentId;
    }

    public void study() {
        System.out.println(name + "正在學習");
    }
}

public class Main {
    public static void main(String[] args) {
        Student student = new Student("張三", 18, "2023001");

        // 通過 getter 方法獲取成員變量的值
        System.out.println("姓名:" + student.getName());
        System.out.println("年齡:" + student.getAge());

        // 通過 setter 方法修改成員變量的值
        student.setAge(200);  // 輸出:年齡必須在0到120之間
        student.setAge(20);
        System.out.println("修改後的年齡:" + student.getAge());
    }
}

this 關鍵字

在 Java 中,this 關鍵字表示當前對象的引用。它可以用於以下幾種情況:

  1. 區分成員變量和局部變量:當成員變量和局部變量同名時,可以使用 this 關鍵字來引用成員變量。
  2. 調用本類的其他構造方法:使用 this(參數列表) 的形式,可以在一個構造方法中調用本類的其他構造方法。
  3. 返回當前對象:在實例方法中,可以使用 return this 的形式來返回當前對象,這在鏈式調用中非常有用。

複製

class Student {
    private String name;
    private int age;
    private String studentId;

    // 無參構造方法
    public Student() {
        this("未知", 0, "未知");  // 調用有參構造方法
    }

    // 有參構造方法
    public Student(String name, int age, String studentId) {
        this.name = name;  // 使用 this 區分成員變量和局部變量
        this.age = age;
        this.studentId = studentId;
    }

    // 返回當前對象的方法
    public Student setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        } else {
            System.out.println("姓名不能為空");
        }
        return this;  // 返回當前對象
    }

    public Student setAge(int age) {
        if (age >= 0 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("年齡必須在0到120之間");
        }
        return this;  // 返回當前對象
    }

    public Student setStudentId(String studentId) {
        this.studentId = studentId;
        return this;  // 返回當前對象
    }

    public void showInfo() {
        System.out.println("姓名:" + name + ",年齡:" + age + ",學號:" + studentId);
    }
}

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.showInfo();  // 輸出:姓名:未知,年齡:0,學號:未知

        // 鏈式調用
        student.setName("張三").setAge(18).setStudentId("2023001");
        student.showInfo();  // 輸出:姓名:張三,年齡:18,學號:2023001
    }
}

完整案例:學生類的實現

現在,讓我們綜合運用今天所學的知識,來實現一個完整的學生類。

複製

class Student {
    // 成員變量(使用 private 封裝)
    private String name;
    private int age;
    private String studentId;
    private String major;
    private double score;

    // 靜態變量
    private static String schoolName = "陽光大學";

    // 構造方法重載
    public Student() {
        this("未知", 0, "未知", "未知", 0.0);
    }

    public Student(String name, int age, String studentId) {
        this(name, age, studentId, "未知", 0.0);
    }

    public Student(String name, int age, String studentId, String major, double score) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
        this.major = major;
        this.score = score;
    }

    // getter 和 setter 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        } else {
            System.out.println("姓名不能為空");
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("年齡必須在0到120之間");
        }
    }

    public String getStudentId() {
        return studentId;
    }

    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        if (score >= 0 && score <= 100) {
            this.score = score;
        } else {
            System.out.println("成績必須在0到100之間");
        }
    }

    // 靜態方法
    public static String getSchoolName() {
        return schoolName;
    }

    public static void setSchoolName(String schoolName) {
        Student.schoolName = schoolName;
    }

    // 普通方法
    public void study() {
        System.out.println(name + "正在學習" + major + "專業課程");
    }

    public void takeExam() {
        System.out.println(name + "正在參加考試");
    }

    public void showInfo() {
        System.out.println("學校:" + schoolName);
        System.out.println("姓名:" + name);
        System.out.println("年齡:" + age);
        System.out.println("學號:" + studentId);
        System.out.println("專業:" + major);
        System.out.println("成績:" + score);
    }
}

public class Main {
    public static void main(String[] args) {
        // 創建學生對象
        Student student1 = new Student("張三", 18, "2023001", "計算機科學", 90.5);
        Student student2 = new Student("李四", 19, "2023002");

        // 調用對象的方法
        student1.showInfo();
        student1.study();

        System.out.println("-------------------");

        student2.setMajor("軟件工程");
        student2.setScore(85.5);
        student2.showInfo();
        student2.takeExam();

        // 調用靜態方法
        System.out.println("學校名稱:" + Student.getSchoolName());
        Student.setSchoolName("星光大學");
        System.out.println("修改後的學校名稱:" + Student.getSchoolName());
    }
}

這個案例綜合運用了類與對象、成員變量與方法、構造方法與重載、封裝與訪問修飾符以及 this 關鍵字等知識點,實現了一個功能完善的學生類。通過這個案例,我們可以更好地理解面向對象編程的基本概念和思想。

總結

今天我們學習了 Java 面向對象編程的基礎知識,包括:

  1. 類與對象的概念:類是模板,對象是具體的實例。
  2. 類的組成:成員變量(屬性)和成員方法(行為)。
  3. 構造方法與方法重載:構造方法用於初始化對象,方法重載允許定義多個同名但參數不同的方法。
  4. 封裝與訪問修飾符:使用訪問修飾符控制訪問權限,通過 getter 和 setter 方法實現封裝。
  5. this 關鍵字:表示當前對象的引用,用於區分成員變量和局部變量、調用本類的其他構造方法等。

面向對象編程是 Java 的核心思想,掌握這些基礎知識對於後續的學習非常重要。明天我們將繼續學習 Java 面向對象編程的其他重要概念,包括繼承、多態等。

希望今天的學習對你有所幫助!如果你有任何問題,歡迎隨時提問。