Java 之路 (五) -- 初始化和清理(構(gòu)造器與初始化熔恢、方法重載脐湾、this、垃圾回收器叙淌、枚舉類型)

學(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 - 需要明確指出當前對象的引用

  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() 方法回怜。
    
  2. 比如將當前對象傳遞給其他方法

    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)鍵字
    
  3. 比如初始化成員變量時玉雾,避免參數(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é)處理和垃圾回收

寫在最前面钧舌,很重要:

  1. 對象可能不被垃圾回收
  2. 垃圾回收并不等于"析構(gòu)"
  3. 垃圾回收只與內(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) 初始化的順序 (重點)

  1. 此處直接引入 對象的創(chuàng)建過程分飞,加入有個名為 Dog 的類:
    1. 當首次創(chuàng)建 Dog 的對象時悴务,或者 Dog 類的靜態(tài)方法/靜態(tài)域首次被訪問時,Java 解釋器查找類路徑譬猫,定位 Dog.class 文件
    2. 然后載入 Dog.class(這會創(chuàng)建一個 Class 對象)讯檐,執(zhí)行有關(guān)靜態(tài)初始化的所有動作。因此染服,靜態(tài)初始化只在 Class 對象首次加載的時候進行一次
    3. 當用 new Dog() 創(chuàng)建對象的時候别洪,首先在堆上為 Dog 對象分配存儲空間
    4. 這塊存儲空間會被清零,也就自動的將 Dog 對象的所有成員變量設(shè)置成了默認值肌索。
    5. 執(zhí)行所有出現(xiàn)于成員變量定義處的初始化動作
    6. 執(zhí)行構(gòu)造器蕉拢。(涉及到 第7章繼承時 比較麻煩特碳,之后會詳細分析)
  2. 補充:
    1. 非靜態(tài)成員變量的定義順序決定了初始化的順序。
    2. 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了焙蹭,期待下一章吧晒杈。

共勉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壳嚎,一起剝皮案震驚了整個濱河市桐智,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烟馅,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件然磷,死亡現(xiàn)場離奇詭異郑趁,居然都是意外死亡,警方通過查閱死者的電腦和手機姿搜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門寡润,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舅柜,你說我怎么就攤上這事梭纹。” “怎么了致份?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵变抽,是天一觀的道長。 經(jīng)常有香客問我氮块,道長绍载,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任滔蝉,我火速辦了婚禮击儡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蝠引。我一直安慰自己阳谍,他們只是感情好蛀柴,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著矫夯,像睡著了一般名扛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茧痒,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天肮韧,我揣著相機與錄音,去河邊找鬼旺订。 笑死弄企,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的区拳。 我是一名探鬼主播拘领,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼樱调!你這毒婦竟也來了约素?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤笆凌,失蹤者是張志新(化名)和其女友劉穎圣猎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乞而,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡送悔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了爪模。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欠啤。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖屋灌,靈堂內(nèi)的尸體忽然破棺而出洁段,到底是詐尸還是另有隱情,我是刑警寧澤共郭,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布祠丝,位于F島的核電站,受9級特大地震影響落塑,放射性物質(zhì)發(fā)生泄漏纽疟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一憾赁、第九天 我趴在偏房一處隱蔽的房頂上張望污朽。 院中可真熱鬧,春花似錦龙考、人聲如沸蟆肆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炎功。三九已至枚冗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛇损,已是汗流浹背赁温。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淤齐,地道東北人股囊。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像更啄,于是被迫代替她去往敵國和親稚疹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容