從現實世界到代碼世界:理解類與對象
在編程世界中,我們經常需要描述現實中的事物。比如學生、汽車、手機等。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 都是成員變量。
成員變量可以分為兩種:
- 實例變量:屬於對象的變量,每個對象都有自己的一份拷貝。
- 類變量(靜態變量):屬於類的變量,所有對象共享同一份拷貝。使用 static 關鍵字修飾。
class Student {
// 實例變量
String name;
int age;
String studentId;
// 類變量(靜態變量)
static String schoolName = "陽光大學";
}
成員方法
成員方法用於描述類的行為。它們是在類中定義的函數,用於操作對象的數據或執行特定的功能。
成員方法也可以分為兩種:
- 實例方法:屬於對象的方法,需要通過對象來調用。
- 類方法(靜態方法):屬於類的方法,可以直接通過類名來調用。使用 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 關鍵字創建對象時,才會在內存中為對象分配空間。
對象在內存中的分配情況如下:
- 當創建一個對象時,會在堆(Heap)內存中為該對象分配空間,用於存儲對象的實例變量。
- 類的信息(包括類變量和方法)存儲在方法區(Method Area)中,所有對象共享這些信息。
- 引用變量(用於指向對象的變量)存儲在棧(Stack)內存中。
讓我們通過一個例子來理解這個過程:
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;
}
}
在這個例子中:
- 當程序啓動時,Student 類的信息(包括靜態變量 schoolName)被加載到方法區。
- 當執行 Student student1 = new Student(); 時,在堆內存中為 Student 對象分配空間,並將對象的地址賦給棧內存中的 student1 變量。
- 類似地,創建 student2 對象時,也會在堆內存中分配新的空間。
- student1 和 student2 有各自的 name 和 age 實例變量,但共享同一個 schoolName 靜態變量。
構造方法與方法重載
構造方法
構造方法是一種特殊的方法,用於在創建對象時初始化對象。它具有以下特點:
- 構造方法的名稱必須與類名相同。
- 構造方法沒有返回類型,包括 void。
- 構造方法在創建對象時自動調用。
如果我們沒有顯式地定義構造方法,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編程
}
}
封裝與訪問修飾符
封裝的概念
封裝是面向對象編程的三大特性之一(另外兩個是繼承和多態)。它指的是將對象的狀態(成員變量)和行為(成員方法)捆綁在一起,並對外部隱藏對象的內部實現細節,只通過公共的接口來訪問對象。
封裝的主要目的是:
- 提高代碼的安全性:防止外部代碼隨意修改對象的內部狀態。
- 提高代碼的可維護性:將對象的實現細節隱藏起來,使得修改對象的內部結構時不會影響到外部代碼。
- 簡化代碼的使用:只需要關心對象提供的公共接口,而不需要了解其內部實現。
訪問修飾符
Java 提供了四種訪問修飾符來控制類、成員變量和成員方法的訪問權限:
- private:私有的,只能在本類中訪問。
- default(默認,不寫修飾符):包級私有的,只能在本包中訪問。
- protected:受保護的,只能在本類、子類和本包中訪問。
- public:公共的,可以在任何地方訪問。
以下是訪問修飾符的權限對比表:
表格
|
修飾符 |
本類 |
同包 |
子類 |
其他包 |
|
private |
√ |
× |
× |
× |
|
default |
√ |
√ |
× |
× |
|
protected |
√ |
√ |
√ |
× |
|
public |
√ |
√ |
√ |
√ |
封裝的實現
要實現封裝,我們通常需要:
- 使用 private 修飾符來修飾成員變量,使其只能在本類中訪問。
- 提供公共的 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 關鍵字表示當前對象的引用。它可以用於以下幾種情況:
- 區分成員變量和局部變量:當成員變量和局部變量同名時,可以使用 this 關鍵字來引用成員變量。
- 調用本類的其他構造方法:使用 this(參數列表) 的形式,可以在一個構造方法中調用本類的其他構造方法。
- 返回當前對象:在實例方法中,可以使用 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 面向對象編程的基礎知識,包括:
- 類與對象的概念:類是模板,對象是具體的實例。
- 類的組成:成員變量(屬性)和成員方法(行為)。
- 構造方法與方法重載:構造方法用於初始化對象,方法重載允許定義多個同名但參數不同的方法。
- 封裝與訪問修飾符:使用訪問修飾符控制訪問權限,通過 getter 和 setter 方法實現封裝。
- this 關鍵字:表示當前對象的引用,用於區分成員變量和局部變量、調用本類的其他構造方法等。
面向對象編程是 Java 的核心思想,掌握這些基礎知識對於後續的學習非常重要。明天我們將繼續學習 Java 面向對象編程的其他重要概念,包括繼承、多態等。
希望今天的學習對你有所幫助!如果你有任何問題,歡迎隨時提問。