前言
大家好啊,我是湯圓,今天給大家帶來的是《Java中物件的生與滅- 核心篇》,希望對大家有幫助,謝謝
文章純屬原創,個人總結難免有差錯,如果有,麻煩在評論區回覆或後臺私信,謝啦
簡介
前面我們瞭解了Java的三大特性,其中介紹了類的繼承、過載等,這裡我們就基於這些知識點,把物件的建立和回收進行一個簡單的介紹
這篇不是很長,只是介紹核心的幾個知識點,相信大家很快就可以看完,真的
目錄
- 堆和棧
- 建構函式(生)
- 物件的回收(滅)
正文
堆(heap)和棧(stack)
堆是一塊記憶體,用來存放物件
棧是另一塊記憶體,用來執行方法並儲存區域性變數,遵循後進先出的原則;
PS:棧並不儲存方法,只是執行方法,執行完方法後,會將方法彈出棧(方法存在方法區)
下面我們用實際程式碼,來看下堆和棧的區別
程式碼如下:
public class LiveAndDeathDemo {
// 基本型別屬性
private int a;
public static void main(String[] args) {
LiveAndDeathDemo live = new LiveAndDeathDemo(1);
live.fun();
}
public void fun(){
int temp = 10;
System.out.println(temp);
}
public LiveAndDeathDemo(int a) {
this.a = a;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
可以看到,有一個例項變數a(堆), 兩個方法main和fun,其中fun有一個區域性變數temp(棧)
它們的區別如下所示:
這裡簡單介紹一下上面的流程
- main方法壓入棧中,建立區域性變數live(物件的引用)
- 建立物件live,在堆中開闢記憶體,將live放入堆中
- live呼叫fun方法,將fun壓入棧中(此時fun在棧頂)
- fun執行完成,出棧,繼續執行main方法
- 最後main方法執行完成,也出棧,程式結束
這裡可能有朋友要問了,那如果屬性是一個引用呢?它要存放在哪裡?
堆
引用存放在堆裡,引用指向的物件也存放在堆裡,只不過是堆的另一個地方
如下圖所示:堆中 live物件的屬性 liveRef
指向了另一個物件(live物件2)
為啥要先介紹堆和棧呢?
因為堆和棧跟物件的生活息息相關
如果用人來比作物件的話,那堆就是人的家,棧就是外面的世界
我們出生在家裡,跟外面的世界打交道,最後在家裡。。。
物件的建立(生)
生存還是毀滅,這是一個問題。
-- 莎士比亞《哈姆萊特》
在Java的花花世界中,這也是個問題,不過是個有答案的問題;
答案就在下面。。。
這裡我們先把問題簡化
因為我們最常見的建立物件是通過new建立,而new物件的核心就是通過建構函式來實現,所以我們這裡簡單起見,著重介紹建構函式,其他的後面等到虛擬機器部分再介紹
建構函式的分類:
-
無參建構函式
-
有參建構函式
建構函式和普通方法的區別:
-
建構函式沒有返回型別
-
建構函式名與類名一致
關於編譯器的預設操作:
- 如果沒有定義建構函式,編譯器會預設建立一個無參建構函式
- 如果子類定義了有參建構函式,且沒有顯示呼叫父類的建構函式,則編譯器預設呼叫父類的無參建構函式
當你自己有建立建構函式(無參或有參)時,編譯器都不會再去建立建構函式
建構函式的過載:
很常用,一般用來設定屬性的預設值
具體做法就是多個建構函式層層呼叫(又來套娃了)
下面舉個例子:
public class LiveAndDeathDemo {
private int a;
private String name;
public LiveAndDeathDemo(){
this(1);
}
public LiveAndDeathDemo(int a) {
this(a, "JavaLover");
}
public LiveAndDeathDemo(int a, String name) {
this.a = a;
this.name = name;
}
// 省略getter,setter
}
用圖表示的話,就是下面這個樣子
建構函式的私有化
如果建構函式私有化,那麼它要怎麼用呢?
私有化說明只有類本身可以呼叫,這種主要用在工廠方法中
比如Java中的LocalDate,原始碼如下:
public final class LocalDate
implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
// 建構函式私有化
private LocalDate(int year, int month, int dayOfMonth) {
this.year = year;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
// 對外提供一個靜態方法,用來建立物件例項
public static LocalDate of(int year, int month, int dayOfMonth) {
YEAR.checkValidValue(year);
MONTH_OF_YEAR.checkValidValue(month);
DAY_OF_MONTH.checkValidValue(dayOfMonth);
return create(year, month, dayOfMonth);
}
}
這種用法在LocalDate這種工具類中用的比較多,還有就是單例模式(後面設計模式時再介紹)
上面介紹的建構函式沒有介紹到父類,下面開始介紹
如果有父類,建構函式的有哪些不一樣的地方
this和super:
在介紹父類的建構函式之前,有必要介紹下這個super
-
this
指向當前類 -
super
指向父類
super
用來顯式呼叫父類相關屬性和方法(包括建構函式)
比如super.filedA
, super.fun()
等
這裡有個特例,如果是在子類的建構函式中或者覆寫方法中,則直接呼叫super()即可呼叫父類對應的建構函式或方法(下面程式碼有演示)
建構函式的執行順序
-
如果子類
Dog
繼承父類Animal
,那麼會先呼叫父類的建構函式,再呼叫子類的建構函式; -
如果父類Animal上面還有父類,會繼續往上呼叫;
-
上面這個過程就叫做“建構函式鏈”
這個關係有點像是:子女和父母的關係,子女要想出生,必須先讓爺爺奶奶把父母生出來,然後父母才能生子女
所以這裡如果我們要構造子類,必須先構造父類;如果父類還有父類,則繼續延伸,一直到Object超類為止
下面用程式碼演示下這個層層呼叫的過程:
public class SuperDemo extends Father{
public SuperDemo() {
// 1.1 這裡顯示呼叫父類的建構函式
// 1.2 Super必須放在建構函式的第一行
super();
System.out.println("sub construct");
}
public static void main(String[] args) {
SuperDemo demo = new SuperDemo();
}
}
class Father{
public Father() {
// 2. 這裡沒有顯示呼叫父類(Object)的建構函式,編譯器會自己去呼叫
System.out.println("father construct");
}
}
/** 假設下面這個Object就是我們的超類
class Object{
public Object(){
// 3. 最終的建構函式,會調到這裡為止
}
}
**/
輸出如下:
father construct
sub construct
可以看到,先呼叫父類Father的建構函式,再呼叫子類的建構函式
他們之間的繼承關係如下:
圖示說明:
左邊的虛線表示層層往上呼叫,直到超類Object
右邊的實現表示上面的構造完成會回到下面那一層,繼續構造,直到當前類
好了,構造的過程大致就是這個樣子了,還有很多其他方面的細節(比如類的初始化等)這裡先不介紹了,太多了,放到後面介紹
物件的回收(滅)
物件的回收是在程式記憶體不夠用時,將沒用的物件(可回收)進行釋放的一種操作,這個操作是由垃圾收集器GC來完成的
什麼是沒用的物件?
沒用的物件就是可回收的物件
說人話:當指向物件A的最後一個引用ref消失時,這個物件A就會變成沒用的物件,等待著垃圾收集器的回收
那怎麼才算引用消失呢?
基本分為兩種情況:
-
如果引用是區域性變數,那當引用所在的方法執行完畢時,引用就會被釋放,那麼該物件隨即也就會被標記為沒用的物件,等待回收
-
當引用指向其他物件或者null時,該物件會被標記為沒用的物件,等待回收
上面都是假設引用是指向物件的最後一個引用的情況,如果有多個引用指向同一個物件,那麼要等到引用都消失,物件才會被標記為可回收,即沒用的東西
總結
- 堆和棧
堆存放物件,棧用來執行方法並存放區域性變數
- 物件的建立
主要通過建構函式來建立,比如new物件
如果是反序列化來建立物件,則不會構造,因為構造後,物件的屬性會被重新初始化,那麼序列化的屬性值就被抹掉了(前面的Java中的IO流有涉及)
如果子類有父類,則先呼叫父類的建構函式,如果父類還有父類,則依次類推,直到Object超類
- 物件的回收
當指向物件的最後一個引用消失時,這個物件就會變成沒用的物件,等待著垃圾收集器的回收
後記
最後,感謝大家的觀看,謝謝