學(xué)習(xí)內(nèi)容:
- 構(gòu)造器
- 方法重載
- this 關(guān)鍵字
- 垃圾回收器的清理
- 初始化問題
- 枚舉類型
這一章內(nèi)容有一點點多秤掌,需要注意的地方也很多。下面就開始我的表演了鹰霍。
1. 構(gòu)造器
(1) 概念:
- 一個創(chuàng)建對象時被自動調(diào)用的特殊方法闻鉴。
(2) 作用:
- 通過構(gòu)造器,創(chuàng)建對象茂洒,并確保對象得到初始化椒拗。
(3) 命名:
- 構(gòu)造器的名稱必須與類名相同。
(4) 特殊:
- 構(gòu)造器是一種特殊類型的方法获黔,它沒有返回值蚀苛。但是!它與返回值為空(void)不同玷氏。
- 對于空返回值堵未,方法本身不會自動返回什么,但是可以選擇讓它返回別的東西
- 對于構(gòu)造器盏触,不會返回任何東西渗蟹。new 表達式返回了對新建對象的引用,但是構(gòu)造器本身沒有返回任何值赞辩。
- 不接受任何參數(shù)的構(gòu)造器稱為 默認構(gòu)造器 / 無參構(gòu)造器
- 如果類中沒有構(gòu)造器雌芽,那么編譯器會自動創(chuàng)建 默認構(gòu)造器;反之辨嗽,如果已經(jīng)定義了一個構(gòu)造器(無論是否有參數(shù))世落,編譯器都不會再自動創(chuàng)建 默認構(gòu)造器。
2. 方法重載
(1) 原因
- 每個方法要有獨一無二的標識符
- 構(gòu)造器強制重載方法名:為了讓方法名相同而形式參數(shù)不同的構(gòu)造器同時存在糟需。
(2) 重載規(guī)則:
- 具有相同的的方法名
- 必須有一個獨一無二的參數(shù)類型列表(包括參數(shù)類型屉佳,以及參數(shù)類型對應(yīng)的順序)
(3) 需要注意谷朝,涉及基本類型的重載
- 常數(shù)值會被當作 int 值處理
- 如果傳入實參的數(shù)據(jù)類型 小于 方法中聲明的形參的數(shù)據(jù)類型,那么會將 實參的數(shù)據(jù)類型提升武花。
- 特殊的圆凰,對于 char 而言,如果沒有恰好接收 char 參順的方法体箕,那么會把 char 提升至 int
- 如果傳入實參的數(shù)據(jù)類型 大于 方法中聲明的形參的數(shù)據(jù)類型专钉,那么會將 實參的數(shù)據(jù)類型進行窄化轉(zhuǎn)換。
3. this 關(guān)鍵字
(1) 作用
- 通過 this 關(guān)鍵字累铅,可以在方法的內(nèi)部獲得當前對象的引用跃须。
- this 只能在方法內(nèi)部使用,表示對 “調(diào)用方法的那個對象” 的引用
(2) 用途 1 - 需要明確指出當前對象的引用
-
比如需要返回這個引用
public class Leaf{ int i=0; Leaf increment(){ i++; return this; } void print(){ System.out.println("i = " + i); } public static void main(String[] args){ Leaf x = new Leaf(); x.increment().increment().print(); } } //結(jié)果為 i = 3 //分析 //因為 increment()方法中返回了 對象的引用争群,所以才可以連綴多個 increment() 方法回怜。
-
比如將當前對象傳遞給其他方法
class person{ public void eat(Apple apple){ Apple peeled = apple.getPeeled(); Ssytem.out.println("Yummy"); } } class Peeler{ static Apple peel(Apple apple){ // ... remove peel return apple; // Peeled } } class Apple{ Apple getPeeled(){ return Peeler.peel(this); } } public class PassingThis{ public static void main(String[] args){ new Person().eat(new Apple()); } } //輸出為 Yummy //分析 //Apple 需要調(diào)用 Peeler.peel() 方法,為了將自身傳遞給這個外部方法换薄, Apple 必須使用 this 關(guān)鍵字
-
比如初始化成員變量時玉雾,避免參數(shù)重名造成混淆
public class Person{ String name; public Person(String name){ this.name = name; } } //this.name 指的是 Person 類的 name 這個成員變量 //name 指的是 接收的 String 參數(shù) name
(3) 用途 2 - 在構(gòu)造器中調(diào)用構(gòu)造器
通過 this,可以在一個構(gòu)造器中調(diào)用另一個構(gòu)造器轻要,避免重復(fù)代碼
-
一般來說复旬,單獨的 this 關(guān)鍵字指的是 “當前對象”,表示引用冲泥;如果為 this 添加函數(shù)列表驹碍,這就產(chǎn)生了對符合此參數(shù)列表的某個構(gòu)造器的明確調(diào)用。
public class Person{ String name; int age; public Person(String name){ this.name = name; } public Person(String name,int age){ this(name); this.age = age; System.out.println("name : " + name + "; age :" + age); } } //如果此時調(diào)用 Person person = new Person("whadlive",21); //那么輸出的結(jié)果是 name : whdalive; age : 21 //原因 //首先 new Person("whdalive,21) 調(diào)用了 Person(String name,int age) 這個構(gòu)造器 //然后內(nèi)部又通過 this(name) 調(diào)用了 Person(String name)凡恍。
(4) 關(guān)于 static 的問題志秃;
- static 方法就是沒有 this 的方法 -- 因為 static 屬于 類,而非對象嚼酝,自然不存在引用浮还,即沒有 this
- static 方法內(nèi)部不能調(diào)用非靜態(tài)方法;反過來是可以的闽巩。
4. 清理:終結(jié)處理和垃圾回收
寫在最前面钧舌,很重要:
- 對象可能不被垃圾回收
- 垃圾回收并不等于"析構(gòu)"
- 垃圾回收只與內(nèi)存有關(guān)
(1) Java 中的垃圾回收器負責回收無用對象占據(jù)的內(nèi)存資源
-
特殊情況:
假定對象(并非使用 new) 獲得了一塊特殊的內(nèi)存區(qū)域(比如在 Java 中使用 C 并且通過 malloc 分配空間),而 垃圾回收器只能釋放由 new 分配的內(nèi)存涎跨,所以此時這塊特殊的內(nèi)存區(qū)域無法釋放洼冻。
應(yīng)對方法:Java 中定義了 finalize() 方法
- 當垃圾回收器準備好釋放對象占用的存儲空間,首先會調(diào)用 finalize() 方法隅很,并且在下一次垃圾回收動作發(fā)生時撞牢,才會真正回收對象占用的內(nèi)存。也就是說,我們可以通過 finalize() 方法做一些重要的清理工作普泡。(比如在 finalize() 方法中去調(diào)用 C 語言的 free() )
-
坑點
-
垃圾回收(垃圾回收有關(guān)的任何行為) 不能保證一定會發(fā)生
我們無法控制垃圾回收的時機播掷,前面第 3 點提到了审编,垃圾回收只與內(nèi)存有關(guān)撼班,如果 jvm 并未面臨內(nèi)存耗盡,它是不會浪費時間執(zhí)行垃圾回收以恢復(fù)內(nèi)存的垒酬。因此我們不能將 finalize() 作為通用的清理方法砰嘁,我們需要創(chuàng)建其他的一些方法去進行清理。
-
關(guān)于 System.gc()
首要記住一點:System.gc() 不能保證執(zhí)行垃圾回收勘究,原因還是由于 垃圾回收只和內(nèi)存有關(guān)矮湘。
這個方法的作用只是提醒 JVM:開發(fā)者希望進行一次垃圾回收,但是否執(zhí)行垃圾回收全看 虛擬機的臉色口糕。
-
(3)終結(jié)條件
- 對象處于某種狀態(tài)缅阳,使它使用的內(nèi)存可以被安全的釋放
(4) 垃圾回收器如何工作?(需要好好消化)
首先提個問題:在堆上分配內(nèi)存代價很高,但是由于垃圾回收器的存在,在java中,在堆中分配內(nèi)存的速度甚至可以與其他語言在棧上的速度向媲美. 為什么?
因為java的垃圾回收器一方面會釋放空間,一方面會進行內(nèi)存碎片整理. 所以java創(chuàng)建對象的時候,在堆上分配內(nèi)存只需要將堆指針移動一下,就像在棧上那樣景描。
-
垃圾回收機制 - 引用計數(shù)法(并非 Java 使用)
每個對象都有一個引用計數(shù)器十办,如果有一個引用變量連接到該對象時,則該對象的引用計數(shù)器加 1超棺;當引用離開作用域或者被置為 null 的時候向族,引用計時器減 1 。如果引用計數(shù)器為 0棠绘,則判定該對象失活件相。(經(jīng)常會被立即清理)。但是如果出現(xiàn)循環(huán)引用的時候氧苍,單純靠引用計數(shù)器就不行了.夜矗。
-
Java 采用的垃圾回收機制的思想:
所有活的對象不管是被引用了多少層,一定可以追溯到存活在堆椚门埃或者靜態(tài)存儲區(qū)之中的引用紊撕。對于發(fā)現(xiàn)的每個引用,追蹤它引用的對象澄干,尋找此對象包含的所有引用逛揩,反復(fù)進行,直到 ”根源于堆棧和靜態(tài)存儲區(qū)的引用“所形成的網(wǎng)絡(luò)全部被訪問為止麸俘。這樣就找到了所有”活“的對象辩稽。
-
Java 采用的 自適應(yīng) 的垃圾回收技術(shù)。
在上面思想的基礎(chǔ)下从媚,關(guān)于如何處理找到的存活對象逞泄,取決于不同的 jvm 實現(xiàn)。
有一種做法為 停止-復(fù)制
- 簡單來說就是 先暫停程序,但后將所有存活的對象復(fù)制到另外一個堆中喷众,沒有被復(fù)制的全是垃圾各谚。當對象被復(fù)制到新的堆中時,緊湊排列到千。 當對象從一個堆被復(fù)制到另外一個堆之后昌渤,指向它的引用就必須被修正,靜態(tài)存儲區(qū)和棧上的引用可以直接被修正.憔四,但可能還有其他指向這些對象的引用膀息,會在之后的遍歷中被找到并修正。
- 這種方式效率低了赵,存在兩個問題:
- 開銷變大潜支,增加了一個堆,在兩個分離的堆之間來回操作
- 復(fù)制的問題柿汛,程序穩(wěn)定之后冗酿,只有少量垃圾,全部將內(nèi)存復(fù)制一遍很浪費络断。
- 解決方法:
- 針對 開銷大的問題:按需從堆中分配幾塊較大的內(nèi)存姿骏,復(fù)制動作發(fā)生在這些大塊內(nèi)存之間 上鞠。
- 針對 復(fù)制的問題:jvm 進行檢查中鼠,沒有新垃圾產(chǎn)生的話色难,轉(zhuǎn)換到另一種工作模式 標記-清掃,這也是為什么說 java 是 自適應(yīng) 的垃圾回收躁绸。
關(guān)于 標記-清掃:
- 思路:同樣是從堆棧和靜態(tài)存儲區(qū)出發(fā)裕循,遍歷所有引用,進而找出所有存活的對象净刮。每當找到一個存活對象剥哑,就給它一個比奧及,這個過程中不會回收任何對象淹父。當全部標記工作完成的時候株婴,才開始清理動作。清理過程中暑认,沒有標記的對象被釋放困介,并不進行復(fù)制。這樣蘸际,剩下的堆空間是不連續(xù)的座哩,如果需要連續(xù)空間,則需要重新整理剩下對象粮彤。
- 同樣的根穷,也需要在程序暫停的時候才能進行姜骡。
-
進一步解釋 自適應(yīng)
前置知識:內(nèi)存分配以較大的 塊 為單位,如果對象較大就會占用單獨的塊屿良。
-
細節(jié):停止-復(fù)制 嚴格來說要先把所有存活對象從舊堆復(fù)制到新堆圈澈,然后才能釋放舊對象,這將導(dǎo)致大量內(nèi)存復(fù)制行為尘惧。 在分配 塊 之后康栈,垃圾回收器可以往廢棄的 塊 中拷貝對象,每個 塊 有相應(yīng)的 代數(shù)generation count 來記錄它是否存活褥伴。通常如果塊在某處被引用谅将,代數(shù) 會增加漾狼;垃圾回收器將對上次回收動作之后的新分配的 塊 進行整理重慢。
同時,垃圾回收器會定期進行完整的整理動作--大型對象不會被復(fù)制(只是增加 代數(shù))逊躁,內(nèi)含小型對象的那些 塊 則被復(fù)制并整理似踱。
個人理解,這種做法就是避免復(fù)制大塊內(nèi)存稽煤,只復(fù)制一些小的對象核芽。
Java 虛擬機會進行監(jiān)視,如果所有對象都很穩(wěn)定酵熙,垃圾回收器效率降低轧简,則切換到 標記-清掃 模式。同樣如果 標記-清掃 模式的效率降低的話匾二,就切換回 停止-復(fù)制 模式哮独。
5. 初始化
(1) 類的成員變量 & 局部變量:
- 對于類的成員變量:
- 如果是基本數(shù)據(jù)類型:未初始化,則會默認設(shè)置初值(具體的值見 Java 之路 (二) -- 一切都是對象 )
- 如果是對象引用:未初始化察藐,則會默認設(shè)置為 null
- 局部變量未初始化就使用皮璧,會報錯。
(2) 初始化的順序 (重點)
- 此處直接引入 對象的創(chuàng)建過程分飞,加入有個名為 Dog 的類:
- 當首次創(chuàng)建 Dog 的對象時悴务,或者 Dog 類的靜態(tài)方法/靜態(tài)域首次被訪問時,Java 解釋器查找類路徑譬猫,定位 Dog.class 文件
- 然后載入 Dog.class(這會創(chuàng)建一個 Class 對象)讯檐,執(zhí)行有關(guān)靜態(tài)初始化的所有動作。因此染服,靜態(tài)初始化只在 Class 對象首次加載的時候進行一次
- 當用 new Dog() 創(chuàng)建對象的時候别洪,首先在堆上為 Dog 對象分配存儲空間
- 這塊存儲空間會被清零,也就自動的將 Dog 對象的所有成員變量設(shè)置成了默認值肌索。
- 執(zhí)行所有出現(xiàn)于成員變量定義處的初始化動作
- 執(zhí)行構(gòu)造器蕉拢。(涉及到 第7章繼承時 比較麻煩特碳,之后會詳細分析)
- 補充:
- 非靜態(tài)成員變量的定義順序決定了初始化的順序。
- static 不會改變成員變量未初始化的默認值
(3) 關(guān)于數(shù)組的初始化
-
關(guān)于數(shù)組
//對于基本數(shù)據(jù)類型: // //此時只定義了一個數(shù)組晕换,同時擁有的只是對數(shù)組的引用 int[] a1; int a1[]; //兩種初始化形式 //1.先創(chuàng)建午乓,后分別對數(shù)組元素初始化 int[] a1 = new int[space];//此時定義的同時,在數(shù)據(jù)里創(chuàng)建了 固定個數(shù)的元素闸准,一旦個數(shù)固定益愈,不能修改,此時 數(shù)組中的元素全部初始化為 默認值(由類型決定夷家,此處為 int 的默認值 0) a1[0] = 1;a1[2]=2;... //2.也可以通過如下方式蒸其,創(chuàng)建的同時進行初始化 int[] a1 = {1,2,3,4,5}; //對于非基本類型的數(shù)組 //假定有一個 Person 類 //兩種形式 //1.先創(chuàng)建,后分別對數(shù)組元素初始化 Person[] people = new People[space];//此時創(chuàng)建的是一個引用數(shù)組库快,該數(shù)組中的元素都是 Person 類型的空引用摸袁。 //需要對 元素進行初始化之后才可以使用,否則會發(fā)生異常 people[0] = new People(); //2.創(chuàng)建同時初始化 People[] people = {new People(),new People()}
-
需要強調(diào)一個知識點:可變參數(shù)列表
-
應(yīng)用于參數(shù)個數(shù)或類型未知的場合义屏。
public class Main { public void printf(String... args) { for (String s : args) { System.out.println(s); } } }
語法: "類型" + "..." + "空格" + "參數(shù)名稱"
指定參數(shù)時靠汁,實際上編譯器會幫我們填充數(shù)組,這樣我們獲取的仍舊是一個數(shù)組闽铐。
-
6. 枚舉
本章只涉及一些 枚舉 的概念蝶怔,具體在 Java 中的特性在原書 第 19 章,留待日后整理兄墅。
(1) 枚舉踢星,即 enum,在 Java SE5 中加入隙咸。
(2) enum 可以將一組具名的值的有限集合創(chuàng)建為一種新的類型沐悦,而這些具名的值可以作為常規(guī)的程序組件使用。這時一種非常有用的功能扎瓶。
(3) enum 是一個類所踊,我們只需要把他用作一種創(chuàng)建數(shù)據(jù)類型的方式,然后直接將所得到的類型拿來使用即可概荷。
(4) 簡單示例:
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
//通過以下調(diào)用秕岛,即可獲得 MEDIUM 這個值。
Spiciness sp = Spiciness.MEDIUM;
(5) 問題
- 在 Effective java 中误证,認為 枚舉 代替常量是一個非常安全的方法继薛。
- 但是學(xué) Android 的過程中,發(fā)現(xiàn) Google 官方不建議使用 枚舉愈捅。
- 原因是因為 占內(nèi)存遏考。
- 因為 反編譯之后,會發(fā)現(xiàn) 枚舉對象的變量 全部會以 static final 形式存在蓝谨。(由網(wǎng)上的分析文章得來灌具,并未親自實踐過)
總結(jié)
這一章算是真正接觸到 Java 這門語言了(也許吧)青团,雖然都很基礎(chǔ),但也是屬于必須掌握的知識咖楣。
另外強調(diào)關(guān)于 垃圾回收的部分督笆,這一章只講了理論性的東西,然而現(xiàn)在回頭看诱贿,只了解這些是不夠的娃肿。畢竟出門動輒都是從源碼層問 垃圾回收是怎么實現(xiàn)得,hhh珠十,累覺不愛料扰,所以還是要再深入了解。
不多BB了焙蹭,期待下一章吧晒杈。
共勉。