類之間的關系
在類之間,最常見的關系有:
- 依賴(“uses-a”);
- 聚合(“has-a”);
- 繼承(“is-a”)。
- 依賴:一種使用關系,即一個類的實現需要另一個類的協助,使用關系具有偶然性、臨時性、非常弱,被使用類的變化會影響到使用類。在 Java 中表現為局部變量、方法的參數或者對靜態方法的調用。例如
class Driver
中方法drive1(Car car)
、drive2()
、drive3()
分別通過形參、局部變量、靜態方法調用體現對Car
類的依賴。- 聚合:關聯關系的特例,是強關聯關系,體現整體與部分的關系,且部分可以離開整體而單獨存在,它們有各自的生命周期,部分可屬于多個整體對象,也可為多個整體對象共享。在 Java 中一般使用成員變量形式實現,一般用
setter
方法給成員變量賦值。例如class Driver
中Car mycar
,若賦予 “車是司機財產一部分” 語義,可表示聚合關系。- 繼承(泛化):是一種繼承關系,表示一般與特殊的關系,指定子類如何獲得父類的所有特征和行為。通過關鍵字
extends
明確標識。例如class Dog extends Animal
,表示Dog
類繼承自Animal
類,Dog
類擁有Animal
類的屬性和方法,還可擁有自己特有的屬性和方法。
對象與對象變量
想要使用對象,首先必須先構造對象,并且對其指定初始狀態。然后對對象應用方法。
在java程序設計語言中,要使用構造器(constructor, 或稱構造函數)構造新實例。構造器是一種特殊的方法,用來構造并初始化對象。
構造器的定義
構造器的名稱必須與類名完全相同,并且沒有返回類型(連?void
?也不能有)。
基本語法如下:
[訪問修飾符] 類名([參數列表]) {// 構造器的方法體
}
?構造器的特點
- 名稱與類名相同:構造器的名稱必須和所在類的名稱一致,這是 Java 語言的規定,用于明確標識這是一個構造器。大小寫也需要一致。
- 沒有返回類型:構造器不能聲明返回類型,包括 void 也不可以。這是因為構造器的主要目的是創建并初始化對象,而不是返回一個值。
- 在創建對象時自動調用:當使用 new 關鍵字創建一個對象時,會自動調用相應類的構造器來完成對象的初始化工作。
對象(Object)
定義
對象是類的一個實例。類是對一類事物的抽象描述,規定了這類事物所具有的屬性和行為;而對象則是類在現實世界中的具體個體,它擁有類所定義的屬性和行為的具體值。例如,“汽車” 可以看作一個類,而某一輛具體的紅色寶馬汽車就是 “汽車” 類的一個對象。
創建對象
在 Java 中,使用?new
?關鍵字來創建對象,其一般步驟如下:
- 聲明類類型的變量:指定要創建對象的類型。
- 使用?
new
?關鍵字創建對象:調用類的構造器來初始化對象。
class Car {String color;String brand;// 構造器public Car(String color, String brand) {this.color = color;this.brand = brand;}public void showInfo() {System.out.println("This is a " + color + " " + brand + " car.");}
}public class Main {public static void main(String[] args) {// 創建 Car 對象Car myCar = new Car("red", "BMW");myCar.showInfo();}
}
?在上述代碼中,Car 是一個類,myCar 是 Car 類的一個對象。通過 new Car("red", "BMW") 調用 Car 類的構造器創建了一個具體的汽車對象,并對其屬性進行了初始化。
對象變量(Object Variable)
定義
對象變量是用來引用對象的變量,它存儲的是對象在內存中的引用(地址),而不是對象本身。可以把對象變量看作是指向對象的一個 “指針”,通過這個變量可以訪問和操作對象的屬性和方法。
對象變量的聲明和賦值
聲明對象變量的語法與聲明基本數據類型變量類似,需要指定變量的類型和名稱。賦值時,將?new
?關鍵字創建的對象的引用賦給對象變量。
示例代碼如下:
class Dog {String name;public Dog(String name) {this.name = name;}public void bark() {System.out.println(name + " is barking!");}
}public class Main {public static void main(String[] args) {// 聲明對象變量Dog myDog;// 創建對象并將引用賦值給對象變量myDog = new Dog("Buddy");myDog.bark();}
}
在上述代碼中,
Dog myDog;
?聲明了一個?Dog
?類型的對象變量?myDog
,myDog = new Dog("Buddy");
?將創建的?Dog
?對象的引用賦給了?myDog
?變量,之后就可以通過?myDog
?來調用?Dog
?對象的方法。??
對象和對象變量的區別
-
存儲內容不同
- 對象:對象是在堆內存中實際分配的一塊內存區域,包含了對象的屬性值等具體數據。
- 對象變量:對象變量存儲的是對象在堆內存中的引用(地址),它位于棧內存中。
-
生命周期不同
- 對象:對象的生命周期從使用?
new
?關鍵字創建開始,直到沒有任何對象變量引用它,并且被 Java 的垃圾回收機制回收為止。 - 對象變量:對象變量的生命周期取決于它的作用域。當對象變量超出其作用域時,它就會被銷毀,但對象本身不一定被銷毀,只要還有其他對象變量引用它。
- 對象:對象的生命周期從使用?
-
操作方式不同
- 對象:對象本身不能直接進行操作,需要通過對象變量來訪問和操作對象的屬性和方法。
- 對象變量:可以通過對象變量來調用對象的方法、訪問對象的屬性等。
- 需要注意的是,對象變量并沒有實際包含一個對象,它只是引用一個對象。?
- 在java中,任何對象變量的值都是對存儲在另一個地方的某個對象的引用。
注:很多人誤以為 Java 對象變量等同于 C++ 引用。實則不然,C++ 無 null 引用且引用不可賦值。Java 對象變量類似 C++ 對象指針,如 Java 的 Date birthday; 等同于 C++ 的 Date* birthday; ,且二者用 new 初始化語法相近。變量復制后,二者指向同一對象指針,Java 的 null 引用對應 C++ 的 NULL 指針。
this關鍵字
在 Java 中,this
關鍵字是一個引用,指向當前對象的實例,主要有以下幾種用法:
引用當前對象的成員變量
當類中方法的局部變量和成員變量同名時,根據就近原則,方法會優先使用局部變量。若想訪問被覆蓋的成員變量,則需使用this
前綴。例如:
public class Teacher { private String name; private double salary; private int age; public Teacher(String name,double salary,int age) { this.name = name; this.salary = salary; this.age = age; }
}
上述代碼中,構造方法的參數與成員變量同名,通過
this.name
、this.salary
、this.age
明確操作的是成員變量。?
調用當前對象的其他方法
this
關鍵字可在方法內部調用當前對象的其他方法,能避免與方法參數或局部變量同名的方法名沖突,確保調用的是當前對象的方法。示例如下:
public class Dog { public void jump() { System.out.println("正在執行jump方法"); } public void run() { this.jump(); System.out.println("正在執行run方法"); }
}
訪問本類的構造方法
this()
用于訪問本類的構造方法,且必須是構造方法中的第一條語句。例如:
public class Student { String name; public Student() { this("張三"); } public Student(String name) { this.name = name; } public void print() { System.out.println("姓名:" + name); }
}
無參構造方法
Student()
中,this("張三")
調用了有參構造方法Student(String name)
。
實現鏈式調用
在方法返回this
關鍵字,可實現鏈式調用,即能在同一個對象上連續調用多個方法。示例:
public class Calculator { private int result; public Calculator add(int number) { this.result += number; return this; } public Calculator subtract(int number) { this.result -= number; return this; } public int getResult() { return this.result; }
}
// 鏈式調用示例
Calculator calculator = new Calculator();
calculator.add(5).subtract(3);
int result = calculator.getResult();
上述代碼中,
?add
和subtract
方法都返回this
,實現了鏈式調用。此外,使用
this
關鍵字還有一些注意事項:
- 不能在靜態方法中使用,因為靜態方法屬于類本身,而非任何對象。
this
關鍵字的值不能被賦值給另一個變量,因其只是一個引用,不是對象。- 應避免濫用,以免影響代碼的可讀性和可維護性。
- this可以區分成員變量和局部變量。
構造方法
構造方法注意事項
1. 構造方法的定義-?如果沒有定義構造方法,系統將給出一個默認的無參數構造方法。 - 如果定義了構造方法,系統將不再提供默認的構造方法。
2. 構造方法的重載?- 帶參構造方法和無參數構造方法,兩者方法名相同,但是參數不同,這叫做構造方法的重載。
3. 推薦的使用方式- 無論是否使用,都手動書寫無參數構造方法,和帶全部參數的構造方法。
無參數構造方法
如果類中沒有定義任何構造方法,Java 編譯器會自動提供一個默認的無參數構造方法。當然,也可以手動定義無參數構造方法。示例如下:
class Book {String title;int pageCount;// 無參數構造方法public Book() {title = "默認書名";pageCount = 0;}public void displayInfo() {System.out.println("書名: " + title);System.out.println("頁數: " + pageCount);}
}public class Main {public static void main(String[] args) {// 創建Book對象時,調用無參數構造方法Book myBook = new Book();myBook.displayInfo();}
}
?在上述代碼中,Book類定義了一個無參數構造方法,在創建Book對象時,該構造方法會被調用,將title初始化為 “默認書名”,pageCount初始化為 0。
帶全部參數構造方法
帶參數的構造方法用于在創建對象時,為對象的成員變量賦初始值。示例如下:
class Person {String name;int age;String address;// 帶全部參數構造方法public Person(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public void showInfo() {System.out.println("姓名: " + name);System.out.println("年齡: " + age);System.out.println("地址: " + address);}
}public class Main {public static void main(String[] args) {// 創建Person對象時,調用帶全部參數構造方法Person person = new Person("張三", 25, "北京市");person.showInfo();}
}
這里
Person
類的構造方法接受name
、age
、address
三個參數,在創建Person
對象時,通過傳遞相應的參數來初始化對象的成員變量。
構造方法重載
一個類中可以有多個構造方法,只要它們的參數列表不同,這就是構造方法重載。示例如下:
class Circle {double radius;String color;// 無參數構造方法public Circle() {radius = 1.0;color = "紅色";}// 帶一個參數構造方法public Circle(double radius) {this.radius = radius;color = "藍色";}// 帶兩個參數構造方法public Circle(double radius, String color) {this.radius = radius;this.color = color;}public void printInfo() {System.out.println("半徑: " + radius);System.out.println("顏色: " + color);}
}public class Main {public static void main(String[] args) {// 使用不同的構造方法創建對象Circle circle1 = new Circle();Circle circle2 = new Circle(2.5);Circle circle3 = new Circle(3.0, "綠色");circle1.printInfo();circle2.printInfo();circle3.printInfo();}
}
?在
Circle
類中,定義了三個構造方法,分別是無參數、帶一個參數、帶兩個參數的構造方法。通過構造方法重載,可以根據不同的需求,以不同的方式來創建Circle
對象。
標準的javabean類
JavaBean 是一種符合特定編程規范的 Java 類,在 Java 開發中用于封裝數據和提供訪問數據的方法,方便在不同組件間傳遞數據等。
在java中:
- 類是公共的(public):保證其他類能夠訪問該 JavaBean 類。
- 有一個公共的無參構造方法:便于在反射等機制以及框架(如 Spring)創建對象時使用,比如在框架初始化對象實例時,會首先調用這個無參構造方法。
- 屬性私有(private):對數據進行封裝,保證數據的安全性,防止外部直接訪問和修改。
- 通過公共的 getter 和 setter 方法訪問屬性:提供了受控的方式來訪問和修改私有屬性,同時也可以在這些方法中添加業務邏輯,比如數據驗證等。
組成部分
- 私有屬性:用于存儲數據,例如:
private String name;
private int age;
上述代碼中,name和age就是JavaBean類的私有屬性。?
?2.?無參構造方法:
public MyBean() {
}
無參構造方法在創建對象時若沒有傳入參數,會默認初始化對象的屬性。
3.?getter 方法:用于獲取私有屬性的值,命名規范是get
加上屬性名,且首字母大寫(對于布爾類型屬性,若屬性名是is開頭
,則 getter 方法為is屬性名
),例如:?
public String getName() {return name;
}
public int getAge() {return age;
}
4.setter 方法:用于設置私有屬性的值,命名規范是set
加上屬性名,且首字母大寫,例如:
public void setName(String name) {this.name = name;
}
public void setAge(int age) {this.age = age;
}
?一個完整的JavaBean
示例如下:
public class Person {// 私有屬性private String name;private int age;// 無參構造方法public Person() {}// getter方法public String getName() {return name;}public int getAge() {return age;}// setter方法public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}
}
在使用時,可以:
public class Main {public static void main(String[] args) {Person person = new Person();person.setName("Alice");person.setAge(25);System.out.println("姓名:" + person.getName() + ",年齡:" + person.getAge());}
}
注意事項
- 類名需要見名知意。
- 成員變量使用private修飾。
- 提供至少兩個構造方法: 無參構造方法。 帶全部參數的構造方法。
- 成員方法: 提供每一個成員變量對應的setXxx()/getXxx()。 如果還有其他行為,也需要寫上。
Java 內存分配區域
Java 內存主要分為以下幾個區域:
- 棧(Stack):方法運行時所進入的內存,局部變量也是在這里存儲。每個方法在執行時會創建一個棧幀用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。方法執行結束,棧幀就會被銷毀。例如,在一個方法中定義的
int num = 10;
,num
這個局部變量就存儲在棧內存中。- 堆(Heap):
new
出來的對象和數組等都在堆內存中開辟空間并產生地址。堆是 Java 內存管理的核心區域,是被所有線程共享的一塊內存區域,其生命周期從 JVM 啟動開始,直到 JVM 停止。對象的實例化、內存分配等操作都在堆中進行,比如new Student();
創建的學生對象就存放在堆內存中。- 方法區(Method Area):字節碼文件加載時進入的內存,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。例如
HelloWorld.class
、Test.class
等字節碼文件在加載時就會進入方法區。另外,像static
修飾的靜態變量也存儲在方法區。- 本地方法棧(Native Method Stack):與虛擬機使用的本地方法相關,為 Native 方法服務。比如 Java 調用 C 或 C++ 的本地代碼時,相關的內存操作就在本地方法棧進行。
- 寄存器:用于存儲指令、操作數等,是 CPU 內部的高速存儲區域,與 Java 內存模型的關系相對間接,主要供 CPU 快速訪問數據。
一個對象的內存分配過程(以Student s = new Student();
為例)
- 加載 class 文件:JVM 首先會查找并加載
Student
類的字節碼文件(.class
文件)到方法區,解析類的元數據信息,如類的字段、方法等。- 申明局部變量:在棧內存中聲明一個名為
s
的局部變量,此時它還沒有指向任何有效的對象。- 在堆內存中開辟一個空間:使用
new
關鍵字在堆內存中為Student
對象分配一塊內存空間,這塊空間用于存儲對象的實例變量等數據。- 默認初始化:堆內存中為對象分配的空間會進行默認初始化,比如對于基本數據類型的成員變量,
int
類型默認初始化為0
,boolean
類型默認初始化為false
等;對于引用類型的成員變量,默認初始化為null
。- 顯示初始化:按照類中成員變量定義時的賦值語句進行初始化,例如
private int age = 18;
,如果有這樣的定義,此時age
就會被初始化為18
。- 構造方法初始化:調用
Student
類的構造方法,對對象進行進一步的初始化操作,構造方法中可以對成員變量進行賦值等操作。- 將堆內存中的地址值賦值給左邊的局部變量:把在堆內存中創建的
Student
對象的地址賦值給棧內存中的局部變量s
,此時s
就指向了堆內存中的Student
對象,后續就可以通過s
來操作該對象。
多個對象引用指向同一個對象的內存情況
假設有以下代碼:
Student s1 = new Student();
Student s2 = s1;
- 首先按照上述單個對象創建過程,
new Student();
在堆內存中創建一個Student
對象,同時在棧內存中創建局部變量s1
并指向堆中的對象。- 執行
Student s2 = s1;
時,在棧內存中又創建了一個局部變量s2
,并將s1
中存儲的對象地址賦值給s2
,這樣s1
和s2
都指向了堆內存中的同一個Student
對象。此時,如果通過s1
修改對象的屬性,s2
訪問該對象時也會看到屬性的變化,因為它們指向的是同一個對象。
不同對象的內存情況
當有多個不同對象創建時,比如:
Student s1 = new Student();
Student s3 = new Student();
- 每次執行
new Student();
都會在? 堆內存中開辟獨立的空間創建新的Student
對象。- 棧內存中分別有
s1
和s3
兩個局部變量,它們各自指向堆內存中不同的Student
對象,這兩個對象的屬性相互獨立,修改s1
指向對象的屬性不會影響s3
指向的對象。