初始化和清理
1. 用構造器確保初始化
初始化需要解決的兩個問題:
- 任何名字都可能與類的某個成員名稱沖突
- 調用構造器是編譯器的責任,所以必須讓編譯器知道調用哪個方法
在Java中采用這種方法:構造器與類有相同的名稱
- 構造器不需要返回值
- 不接受任何參數的構造器叫默認構造器,也叫無參構造器
- 一個類,如果我們沒有提供構造器,會有一個無參構造器。如果我們自定義了構造器,那么會頂替掉默認構造器,如果還想要無參構造器,得自己手動添加。
2. 方法重載
現在我們有多種屬性的初始化需求,那么想用多種方式創建一個對象該怎么辦?
重載
2.1 區分重載方法
要是幾個方法有相同的名字,那么你指的是哪一個呢?
規則:每個重載的方法都必須有一個獨一無二的參數列表。參數順序不同,也算不同。
方法返回值不同,其他相同,即返回參數不同算重載嗎?
如果調用方法時,不保存返回值,那么編譯器不知道該調用哪個方法,所以編譯不通過,不算。
2.2 涉及基本類型的重載
基本類型能從一個“較小”的類型自動提升至一個“較大的”類型。那么將一個較小類型傳入到參數為較大類型的方法,會怎么樣呢?
- 如果傳入類型小於方法中聲明的參數類型,實際數據類型會提升。char略有不同,會直接提升到int
- 如果傳入類型大於方法中聲明的參數類型,那就必須強轉了。
3. this關鍵字
如果想在方法內部獲得當前對象的引用,那么你需要this關鍵字
3.1 在構造器中調用構造器
3.2 static
static方法就是沒有this的方法
4. 終結處理和垃圾回收
Java有自己的垃圾回收機制,為啥還要了解垃圾回收呢?
如果通過JNI創建了一些對象,那么這些對象Java是無法控制的,你怎么做?
4.1 finalize()
為了釋放一些特殊的內存(如:natvie方法),java允許在類中定義一個名為finalize()的方法。
原理:一旦垃圾回收器准備好釋放對象占用的存儲空間,將首先調用finalize()方法,並且在下一次垃圾回收動作是,才會真正回收對象占用的內存。
因此:我們就可以在finalize()中,調用free()等釋放內存的方法。
如果對象中含有其他對象(普通java對象),finalize應該明確釋放那么對象嗎?
不,對於java中的對象,不管是如何創建的,垃圾回收器都會負責釋放對象占據的內存,最好要在finalize中對一般java對象做釋放操作。
4.2 垃圾回收器如何工作
- 首先理解其他系統中的垃圾回收機制
- 引用計數:每個對象都有一個引用計數器,有引用連接至對象那么計數器加一,當離開引用域或者置為null將計數器減一,當發現某個對象的引用計數為零的時候,就釋放對象占用的內存。存在缺陷:如果對象間存在循環引用,那么應該被回收的對象,引用計數卻不為零。
- Java中垃圾回收機制:自適用垃圾回收機制。
- 停止-復制:先停止程序的運行,然后將所有活着的對象復制到另一個堆,沒有被復制的全部是垃圾。缺點
- 需要額外的操作空間(兩個堆)
- 程序穩定后,只會產生少量的垃圾,甚至沒有垃圾,直接復制的話,這很浪費。
- 標記--清掃:從堆棧和靜態存儲區出發,遍歷所有引用,進而找到所有存活的對象。每當找到一個活的對象,就會給對象設一個標記,這個過程中不會回收任何對象。只有標記工作完成,才會開始清理動作。剩下的對空間是不連續的,如果垃圾回收器希望得到連續的空間,就得重新整理剩下的對象。
- 停止-復制:先停止程序的運行,然后將所有活着的對象復制到另一個堆,沒有被復制的全部是垃圾。缺點
Java虛擬機會監視資源,如果所有對象都很穩定,垃圾回收器的效率降低的話,就切換到“標記-清掃”;同樣,如果堆空間出現很多碎片就會切換回“停止-復制”方式。這就是“自適用”技術。
6. 成員初始化
Java盡力保證:所有變量在使用前都能得到恰當的初始化。
6.1 初始化順序
成員變量的初始化,放在構造器初始化之前。比如:成員變量是有初始值默認值的,經過構造器賦值才變為新的值。所以 成員變量初始化--->構造器初始化
-
如果有多處成員變量初始化呢?
成員變量按上下順序初始化后,開始構造器中的初始化
-
靜態數據的初始化
靜態對象先於非靜態。
6.1.2 靜態代碼塊和非靜態實例初始化
Child繼承自Parent,兩者的初始化順序。子類可能會用到父類中的屬性,所以父類中的初始化會先於子類,但是Static比較特殊,先於兩者的所有其他非靜態屬性。
順序 | 代碼塊 |
---|---|
1 | Parent static block |
2 | Child static block |
3 | Parent block |
4 | Parent constructor |
5 | Child block |
6 | Child constructor |