Java編程思想--控制執行流程和初始化與清理

  • 在Java 中,涉及的關鍵字包括 if-else ,while ,do-while ,for , return , break 以及選擇語句 switch,然而, Java並不支持 goto 語句(該語句引起許多反對意見,但它仍是解決某些特殊問題的最便利的方法)。在Java中,仍然可以進行類似 goto那樣的跳轉,但比起典型的 goto ,有了很多限制。

  • 所有的條件語句都利用條件表達式的真或假來決定執行路徑(true 和 false)。
  • if-else 語句是控制程序流程的最基本形式。
  • 迭代 while, do-while  和 for 用來控制循環 , 有時將它們劃分爲 迭代語句。語句會重複執行,知道起控制做用的布爾表達式得到假的結果爲止。
  • for 循環可能是最經常使用的迭代形式,這種在第一次迭代之前要進行初始化。
  • 逗號操作符 : Java裏唯一用到逗號操作符的地方就是for循環的控制表達式中。在控制表達式的初始化和步進控制部分,可以使用一系列由逗號分隔的語句,而且那些語句均會獨立運行。
  • Foreach 語法是Java SE5引入了一種新的更加簡潔的for語法用於數組和容器。
  • return :關鍵詞有兩方面的用途 一方面指定一個方法返回什麼值(假設它沒有void返回值),另一方面它會導致當前的方法退出,並返回那個值。
  • beack 和continue 在任何迭代語句的主題部分,都可用 break 和continue控制循環的流程。其中,break用於強行退出循環,不執行循環中剩餘的語句。而continue則停止執行當前的迭代,然後退回循環起始處,開始下一次迭代。
  • switch 有時也被劃分爲一種選擇語句。根據證書表達式的值,switch語句可以從一系列代碼中選出一段去執行,其中 switch(exp) exp變量類型只能是 int ,short ,char ,byte 和 enum類型(在 JDK1.7以後,類型可以是String了)

 

用構造器確保初始化

  • 在Java中,通過提供構造器,類的設計者可以確保每個對象都會得到初始化。創建對象時,如果其類具有構造器,Java就會在用戶有能力操作對象之前自動調用相應的構造器,從而保證了初始化的進行。
  • 在Java命名採用了C++的方案,即構造器採用與類名相同的名稱。
  • 不接受任何參數的構造器叫做 默認構造器 ,Java文檔中通常使用術語無參構造器,但是默認構造器在Java出現之前已經使用了許多年了。
  • 構造器有助於減少錯誤,並使代碼更易於閱讀。構造器是一種特殊類型方法,因爲它沒有返回值。這與返回值爲空(void) 明顯不同。對於空返回值,儘管方法本身不會自動返回什麼,但仍可讓它返回別的東西。構造器則不會返回任何東西,你別無選擇(new 表達式確實返回了對新建對象的引用 ,但構造器本身並沒有任何返回值)。假如構造器具有返回值,並且允許人們自行選擇返回類型,那麼勢必得讓編譯器知道該如何處理此返回值)。

 

方法重載

  • 在Java裏,構造器是強制重載方法名的另一個原因。既然構造器的名字已經由類名所決定,就只能有一個構造器名。
  • 那麼如果想用多種方式創建一個對象該怎麼辦呢?
  • 假設你要創建一個類,既可以用標準方式進行初始化,也可以從文件裏讀取信息來進行初始化。這就需要兩個構造器:一個默認構造器,另一個取字符串作爲形式參數__該字符串表示初始化對象所需的文件名稱。由於都是構造器,所以它們必須由相同的名字,即 類名。爲了讓方法名相同而形式參數不同的構造器同時存在,必須用到方法重載

 

區分重載方法

  • 要是幾個方法有相同的名字, Java如何才能知道你指的是哪一個呢?其實規則很簡單: 每個重載的方法都必須有一個獨一無二的參數類型列表
  • 也可以通過參數順序的不同也足以區分倆個方法, 不過,一般情況下別那麼做,因爲這會使代碼難以維護。

 

    static void f(String s,int i){
        System.out.println("String :"+s+"int: "+i);
    }
    static void f(int i,String s){
        System.out.println("int: "+i+"String: "+s);
    }
    
    public static void main(String[] args) {
        f("String first",66);
        f(77,"Int first");
    }

//運行結果
String :String firstint: 66
int: 77String: Int first

 

涉及基本類型的重載

  • 基本類型能從一個 較小 的類型自動提升至一個 較大 的類型,此過程一旦涉及到重載,可能會造成一些混淆。

  • 如果傳入的實際參數大於重載方法聲明的形式參數,就得通過類型轉換來執行窄化處理,如果不那麼做,編譯器就會報錯。

 

以返回值區分重載方法

  • 在區分重載方法的時候,爲什麼只能以類名和方法的形參列表作爲標準呢?能否考慮使用方法的返回值來進行區分呢?例如
void f(){
}
int f(){
return 1;
}
  1. 只要編譯器可以根據語境明確判斷出語義, 比如在 int x=f() 中,那麼的確可以據此區分重載方法。
  2. 不過,有時,你並不關心方法的返回值,你想要的是方法調用的其他效果(這常被稱爲 爲了副作用而調用), 這時你可能會調用方法而忽略其返回值。所以,如果你像下面這樣調用方法
f();
  1. 此時Java如何才能判斷改用那個f()方法呢? 別人該如何理解這種代碼呢?因此,根據方法的返回值來區分重載方法是行不通的。

 

 

默認構造器

  • 默認構造器(又名 無參構造器) 是沒有形式參數的__它的作用是創建一個 默認對象。如果你寫的類中沒有構造器,則編譯器會自動創建一個默認構造器。 例如
public class IndexController {
    
    public static void main(String[] args) {
        IndexController controller = new IndexController(); //default
    }
}
  1. 表達式 new IndexController() 行創建了一個新對象,並調用其默認構造器__即使你沒有明確定義它。沒有它的話,就沒有方法可調用,就無法創建對象。如果已經定義了一個構造器(無論是否有參數),編譯器就不會幫你自動創建默認構造器。
public class IndexController {

    IndexController(int i){
    }

    IndexController(String s){
    }

    public static void main(String[] args) {
        //IndexController controller = new IndexController(); //default
        IndexController controller1 = new IndexController(1); //default
        IndexController controller2 = new IndexController("s"); //default
    }
}
  1. 編譯器就會報錯:沒有找到匹配的構造器。 就好比,要是你沒有提供任何構造器,編譯器會認爲 你需要一個構造器,讓我給你製造一個吧 。 但假如你已寫了一個構造器,編譯器則會認爲 你已經自己寫了一個構造器,所以你只帶你在做什麼,你是刻意省略了默認構造器。

 

this 關鍵字

  • this 關鍵字只能在方法內部使用,表示對調用方法的那個對象的引用。 this的用法和其他對象引用並無不同。但要注意,如果在方法內部調用同一類的另一方法,就不必使用this , 直接調用即可。 當前方法中的this引用會自動應用於同一類中的其他方法。
public class IndexController {
    
    void pick(){
    }
    
    void pit(){
        pick();
    }
}
  1. 在 pit方法內部,你可以寫 this.pick() 但無此必要。編譯器能幫你自動添加。只有當需要明確指出對當前對象的引用時,你才需要使用this關鍵字。
  2. 例如, 當需要返回當前對象的引用時,就常常在return 語句中這樣寫:
public class IndexController {
    int i;
    
    IndexController increment(){
        i++;
        return this;
    }
    void  print(){
        System.out.println("i->: "+i);
    }
    
    public static void main(String[] args) {
        new IndexController().increment().increment().print();
    }
}

//運行結果
i->: 2
  1. 通過 increment() 通過this關鍵字返回了對當前對象的引用,所以很容易在一條語句裏對同一個對象執行多次操作。

 

 

在構造器中調用構造器

  •  可能爲一個類寫了多個構造器,有時可能想在構造器中調用另一個構造器,以避免重複代碼。可使用 this 關鍵字可以做到這一點。
  • 通常寫 this 的時候,都是指 這個對象 或者 當前對象 ,而且它本身表示對當前對象的引用。在構造器中,如果爲this添加了參數列表,那麼就有了不同的含義。這將產生對符合此參數列表的某個構造器的明確調用,這樣調用其他構造器就有了直接的途徑。
public class Flower {
    Flower(){
        this(1,"zs");
        System.out.println("默認構造器已經執行");
    }

    Flower(int i,String s){
        this(2);
        System.out.println("Flower(int i,String s) String ="+s);
        System.out.println("Flower(int i,String s)int ="+i);
    }
    Flower(int i){
        System.out.println("Flower(int i) "+i);
    }

    public static void main(String[] args) {
        Flower flower = new Flower();
    }
}
//運行結果爲
Flower(int i) 2
Flower(int i,String s) String =zs
Flower(int i,String s)int =1
默認構造器已經執行
  1. 構造器Flower(int i,String s)儘管可以用this 調用一個構造器,但卻不能調用倆個。此外,必須將構造器調用置於最其實處,否則編譯器會報錯
  2. 也可以使用 this.s(s爲變量名) 來代表數據成員就能解決這個問題。在Java程序中經常出現這種寫法。

 

static 的含義

  • static 方法就是沒有this的方法。在 static方法的內部不能調用非 static方法,反過來倒是可以的。而且在沒有創建任何對象的前提下,僅僅通過類本身來調用static 方法,這實際上正是static方法的主要用途。

 

清理:終結處理和垃圾回收

  • Java 有垃圾回收器負責回收無用的對象佔據的內存資源。 但也有特殊情況: 假定你的對象(並非使用 new )獲取了一塊特殊 的內存區域, 由於垃圾回收器只知道釋放那些經由 new 分配的內存,所以它不知道該如何釋放該對象這塊 特殊 內存。爲了應對這種情況, Java允許在類中定義一個名爲 finalize()方法。
  • 它的工作原理 假定是這樣的, 一旦垃圾回收器準備好釋放對象佔用的內存。將首先調用 finalize()方法,並且在下一次垃圾回收動作發生時,纔會真正回收對象佔用的內存。所以要是你打算使用 finlize() 就能在垃圾回收時刻做一些重要的清理工作。
  • 在C++中,對象一定會被銷燬(如果程序中沒有缺陷的話),而Java裏的對象卻並非總是被垃圾回收。或者換句話說就是:
  • ① 對象可能不被垃圾回收。 ②垃圾回收並不等於 析構  這意味着你不再需要某個對象之前,如果必須執行某些動作,那麼你得自己去做。 Java並未提供 析構函數 或相似的概念,要做類似的清理工作,必須自己動手創建一個執行清理工作的普通方法。
  • 也許你會發現,只要程序沒有瀕臨存儲空間用完的那一刻,對象佔用的空間就總也得不到釋放。如果程序執行結束,並且垃圾回收器一直都沒有釋放你創建的任何對象的存儲空間,則隨着程序的退出,那些資源也會全部交還給操作系統。

 

 

finalize()的用途何在

  • 這是大概已經明白: 不該將 finalize() 作爲通用的清理方法。
  1. 垃圾回收只與內存有關 也就是說,使用垃圾回收器的唯一原因是爲了回收程序不再使用的內存。所以對於垃圾回收有關的任何行爲來說(尤其是finlize() 方法)它們必須同內存及回收有關。
  2. 但這是否意味要是對象中含有其他對象, finzlize() 就應該明確釋放那些對象呢? 不, 無論對象是如何創建的, 垃圾回收器都會負責釋放對象佔據的所有內存。
  3. 之所以要有 finalize() 是由於在分配內存時可能採用類似於C語言中的做法,而非Java中通常的做法。這種情況主要發生在使用本地方法的情況下,本地方法是一種在Java中調用非Java代碼的方式
  4. 本地方法目前只支持 C和C++ ,但他們可以調用其他語言寫的代碼,所以實際上可以調用任何代碼。在非Java代碼中,也許會調用C的malloc()函數系列進行分配存儲空間,而且除非調用了 free() 函數,否則存儲空間將得不到釋放,從而造成內存泄漏。

 

你必須實施清理

  • 要清理一個對象,用戶必須在需要清理的時刻調用執行清理動作的方法。
  • 在C++中,創建一個局部對象(也就是在堆棧上創建,這在Java中是行不通的),此時的銷燬動作發生在以 右花括號 爲邊界,此對象的作用域的末尾處。 如果對象時用 new 創建的(類似於Java中),那麼當程序員調用 C++的 delete操作符時,就會調用相應的析構函數。如果程序員忘記了delete,那麼永遠都不會調用析構函數,這樣就出現了內存泄漏,對象的其他部分也不會得到清理。
  • 相反,Java不允許創建局部對象,必須使用new 創建對象。在Java中,也沒有用於釋放對象的 delete,因爲垃圾回收器會幫助你釋放存儲空間。可以認爲,正是垃圾回收機制的存在,使Java 沒有析構函數。 垃圾回收器的存在並不能完全替代析構函數(而且絕對不能直接調用 finalize(),這也不是一種解決方案)。

 

終結條件

  • 通常, 不能指望 finalize(), 必須創建其他的清理方法,並且明確地調用它們。 看來, finalize()只能存在於程序員很難用到的一些晦澀用法裏了。不過,finalize()還有一個有趣的用法,它並不依賴於每次都要對 finalize() 進行調用, 這就是對象終結條件的驗證
  • 當對某個對象不再感興趣__也就是它被清理了,這個對象應該處於某種狀態,使它佔用的內存可以被安全地釋放。

 

垃圾回收器如何工作

  • 垃圾回收器對於提高對象的創建速度,卻具有明顯的效果。聽起來很奇怪__存儲空間的釋放竟然會影響存儲空間的分配,但這確實是某些Java虛擬機的工作方式。這也意外着,Java從堆分配空間的速度,可以和其他語言從堆棧上分配空間的速度相媲美。
  1. 引用計數是一種簡單但速度很慢的垃圾回收計數。每個對象都含有一個引用計數器,當有引用連接至對象時,引用計數加1。當離開作用域或者被置爲null時,引用計數減1。雖然引用計數的開銷不大,但這項開銷在整個程序生命週期中持續發生。垃圾回收器會在含有全部對象的列表上進行遍歷,當某個對象的引用計數爲0時,就釋放其佔用的空間。
  2. 這個方法有一個缺陷,如果的對象之間存在循環引用,可能會出現 對象已經被回收 ,但引用計數卻不爲0的情況,對於垃圾回收器而言,定位這樣的交互自引用的對象組所需的工作量極大。 引用計數常用來說明手機的工作方式,但似乎從未被應用到任何一種Java虛擬機實現中。
  • 在一些更快的模式中, 垃圾回收器並非基於引用計數技術。 思想是: 對任何 的對象,一定能最終追溯到其存活的堆棧或靜態存儲區之中的引用。這個引用鏈條可能會穿過數個對象層次。因此,如果從堆棧和靜態存儲區開始,遍歷所有的引用,就能找到所有  的對象,對於發現的每個引用, 必須追蹤它所引用的對象,然後是此對象包含的所有引用,如此反覆,直到 根源於堆棧和靜態存儲區的引用 所形成的網絡全部被訪問爲止。你所訪問過的對象必須都是 的。注意,這就解決了 交互自引用的對象組 的問題__這種現象根本不會被發現,因此也就被自動回收了。
  • 在這種方式下, Java虛擬機採用一種 自適應的垃圾回收技術。 至於如何處理 找到存活對象,取決於不同的Java虛擬機實現。 有一種做法爲 停止__複製。顯然這意味着,先暫停程序的運行(所以它不屬於後臺回收模式),然後將所有存活的對象從當前堆複製到另一個堆,沒有被複制的全部都是垃圾。當對象被複制到新堆時,它們一個挨着一個的,所以新堆保持緊湊排列,然後就可以按前述方法簡單,直接地分配新空間。
  • 對於這種所謂 複製式回收器  而言,效率會降低,倆個原因:
  1. 得有兩個堆,然後得在倆個堆之間來回倒騰,從而得維護比實際需要多一倍的空間。
  2. 在於複製的問題, 程序進入穩定狀態之後,可能只會產生少量的垃圾,甚至沒有垃圾。儘管如此,複製式回收器仍然會將內存來自一處複製到另外一處,這就很浪費。
  • 爲了避免這種情形,一些Java虛擬機會進行檢查: 要是沒有新垃圾產生,就會轉換到另一種工作模式(即 自適應),這種模式被稱爲 標記__清掃, Sun公司早期版本的Java虛擬機使用了這種技術。對一般用途而言 標記__清掃 方式速度相當慢,但是你知道只會產生少量垃圾甚至不會產生垃圾時,它的速度就很快。
  • 標記__清掃 所依據的思路同樣是從堆棧和靜態存儲區出發,遍歷所有的引用,進而找出所有存活的對象。每當找到一個存活對象,就會給對象設一個標記,這個過程中不會回收任何對象。只有全部標記工作完成的時候,清理動作纔會開始。在清理過程中,沒有標記的對象將被釋放,不會發生任何複製動作。剩下的堆空間是不連續的,垃圾回收器要是希望看到連續空間的話, 就得重新整理剩下的對象。
  • 停止__複製 的意思是這種垃圾回收動作不是在後臺進行的, 相反,垃圾回收動作發生的同時,程序將會被暫停。在Sun公司的文檔中會發現,許多參考文獻將垃圾回收視爲低優先級的後臺進程,但事實上垃圾回收器在Sun公司早起版本的Java虛擬機中並非以這種方式實現的。當可用內存數量較低時,Sun版本的垃圾回收器會暫停運行程序,同樣 標記__清掃 工作也必須在程序暫停的情況下才能運行。
  • 在Java虛擬機中,內存分配以較大的 爲單位。如果對象較大,它會佔用單獨的塊。它會佔用單獨的塊。嚴格來說 ,停止__複製 要求在釋放舊有對象之前,必須先把所有存活對象從舊堆複製到新堆,這也將導致大量內存複製行爲。有了塊之後,垃圾回收器在回收的時候就可以往廢棄的塊裏拷貝對象了,每個塊都用相應的代數來記錄它是否還活。
  • 通常,如果塊在某處被引用,其代數會增加,垃圾回收器將對上次回收動作之後新分配的塊進行整理。這對處理大量短命的臨時對象很有幫助。垃圾回收器會定期進行完整的清理動作__大型對象仍然不會被複制(只是其代數會增加).內含小型對象的那些塊則被複制並整理。Java虛擬機會進行監視,如果所有對象都很穩定,垃圾回收器的效率降低的話,就切換到 標記__清掃 方式,同樣 Java虛擬機會跟蹤 標記__清掃 的效果,要是堆空間出現很多碎片,就會切換回 停止__複製 的方式, 這就是自適應技術,你可以稱爲 自適應的,分代的,停止_複製,標記_清掃 式垃圾回收器
  • Java虛擬機中有許多附加技術用於提升速度。 與加載器操作有關的-> 即時 編譯器技術(Just In Time ,JIT)
  • 這種技術可以把程序全部或部分編譯成本地機器碼(這本來是Java虛擬機的工作),程序運行速度因此得到提升。當需要裝載某個類(通常是在爲該類粗昂見第一個對象)時,編譯器會先找到 .class 文件,然後將該類字節碼裝入內存。此時,有兩種方案可供選擇:
  1. 讓即時編譯器編譯所有的代碼。 缺陷: 這種加載動作散落在整個程序生命週期內,累加起來要花更多時間,並且會增加可執行代碼的長度(字節碼要比即時編譯器展開後的本地機器碼小很多)這將導致頁面調度,從而降低程序速度。
  2. 惰性評估 : 即時編譯器只在必要的時候才能編譯代碼。這樣,從不會被執行的代碼也許就壓根不會被JIT所編譯。新版JDK中的Java HotSpot技術就是採用了類似方法,代碼每次被執行的回收都會做一些優化,所以執行次數越多,他的速度就越快。

 

成員初始化

  • Java盡力保證:所有變量在使用前都能得到恰當的初始化。對於方法的局部變量,Java以編譯時錯誤的形式來貫徹這種保證。
void f(){
int i;
i++;  //error  i not initialized
}   
  1. 會告訴你一條出錯的消息,告訴你 i 可能未初始化。當然,編譯器也可以爲i 賦一個默認值,但是,未初始化的局部變量更有可能是程序員的疏忽,所以採用默認值反而會掩蓋這種失誤。因此強調程序員提供一個初始值,往往能夠幫助找出程序裏面的錯誤。
  2. 在類定義一個對象引用時,如果不將其初始化,此引用就會獲取一個特殊值 null。

 

指定初始化

  • 可以通過某個方法來提供初始值
public class MethodInit{

    int y=f();
    
    int f(){
        return 1;
    }
}

 

構造器初始化

  • 可以使用構造器進行初始化。在運行時刻,可以調用方法或者執行某些動作來確定初值,這爲編程帶來了更大的靈活性。需要牢記 : 無法阻止自動初始化的進行,它將在構造器被調用之前發生
public class Counter{

    int i;
    
    Counter(){
        i=7;
    }
}
  1. 那麼i首先會被置0,然後變成7。對於所有基本類型和對象引用,包括在定義時已經指定初值的變量,這種情況都是成立的,因此,編譯器不會強制你在構造器某個地方或者在使用它們之前對元素進行初始化__因此初始化早已得到了保證。

 

初始化順序

  • 在類的內部,變量定義的先後順序決定了初始化的順序。即使變量定義散佈於方法定義之間,它們仍舊會在任何方法(包括構造器)被調用之前得到初始化。如下
public class DataOnly {
    int i;
    DataOnly(int i){
        this.i=i;
        System.out.println("DataOnly parents==>:"+i);
    }
}


public class Calculation {

    DataOnly dataOnly=new DataOnly(1);

    Calculation(){
        System.out.println("Calculation 構造器執行");
        dataOnly3=new DataOnly(33);
    }

    DataOnly dataOnly2=new DataOnly(2);

    void f(){
        System.out.println("f()方法執行");
    }
    DataOnly dataOnly3=new DataOnly(3);
    
    public static void main(String[] args) {
        Calculation calculation = new Calculation();
        calculation.f();
    }
}

//運行結果爲
DataOnly parents==>:1
DataOnly parents==>:2
DataOnly parents==>:3
Calculation 構造器執行
DataOnly parents==>:33
f()方法執行
  1. 在Calculation 類中,故意吧幾個 DataOnly 對象的定義散落到各處,以證明它們全部都會在調用構造器或其他方法之前得到初始化。此外, dataOnly3在構造器內在次被初始化。
  2. 由輸出可見, dataOnly3這個引用會被初始化倆次:
  • 一次是調用期間(第一次引用的對象將被丟棄,並作爲垃圾回收)。
  • 一次是調用構造器前。

 

靜態數據的初始化

  • 無論創建多少個對象,靜態數據只佔用一份存儲區域。 static 關鍵字不能應用於局部變量,因此它只能作用於域。如果一個域是靜態的基本類型域,病也沒有對它進行初始化,那麼它就會獲得基本類型的標準初值。如果他是一個對象的引用,那麼它的默認初始化值就是 null。
public class Mug {

    Mug(int marker){
        System.out.println("Mug==>"+marker);
    }
}

public class Mugs {

     Mug mug1;
     Mug mug2;
    {
        mug1=new Mug(1);
        mug2=new Mug(2);
        System.out.println("static 靜態初始化已經執行");
    }

    Mugs(){
        System.out.println("Mugs");
    }

    Mugs(int i){
        System.out.println("i==>Mugs");
    }

    public static void main(String[] args) {
        new Mugs();
        System.out.println("=====");
        new Mugs(1);
    }

}


//運行結果爲
Mug==>1
Mug==>2
static 靜態初始化已經執行
Mugs
=====
Mug==>1
Mug==>2
static 靜態初始化已經執行
i==>Mugs
  • 如果 把static Mug mug1; static Mug mug2; 這個改爲靜態域static{ mug1=new Mug(1); mug2=new Mug(2); System.out.println("static 靜態初始化已經執行"); }
  • 執行結果爲
  • Mug==>1
  • Mug==>2
  • static 靜態初始化已經執行
  • Mugs ===== i==>Mugs
  1. 由此我們發現 如果沒有 加上staiic 這個域裏面會執行倆次,稱爲 非靜態初始化。如果加上static 關鍵字 則只會執行一次靜態域

 

數組初始化

  • 數組只是相同類型的,用一個標識符名稱封裝到一起的一個對象序列或基本類型數據序列。數組是通過方括號([])來定義和使用的。定義一個數組,只需要在類型名後加上一對空括號即可如下:
int [] a1;
或者
int a2 [];
  • 編譯器不允許指定數組的大小。對於數組初始化動作可以出現在代碼的任何地方,但也可以使用一種特殊的初始化表達式,它必須在創建數組的地方出現。這種特殊的初始化是由一對花括號({})的值組成,在這種情況下,存儲空間的分配(等同於 new )將由編譯器負責。
int [] a1={1,2,3,4,5};
//可以將一個數組賦值給另一個引用如下
int [] a2=a1;
  • Java數組計數也是從 0 個元素開始,所以不能使用最大下標數 是 length-1。要是超出這個邊界,C和C++會默默地接受,並允許你訪問所有內存,則 Java能保護你免受這一問題的困擾,一旦下標過界,就會出現運行時錯誤(即 異常)。

 

可變參數列表

  • 第二種形式提供了一種方便的語法來創建對象並調用方法,以獲得與C的可變參數列表(C通常把它稱爲 varargs)一樣的效果。這個可以使用於參數個數或類型未知的場合。
    static void f(int i,String ...s){
        System.out.println("f方法已經執行`");
    }

    static void g(String s,int ...i){
        System.out.println("g 方法執行");
    }

    public static void main(String[] args) {
        f(1,"張三","hello");
        g("張三",1,2,3);
    }

//運行結果爲
f方法已經執行`
g 方法執行

 

枚舉類型

  • JavaSE5添加了一個看似很小的特性,即 enum 關鍵字,它使得我們再需要羣組並使用枚舉類型集時,可以很方便地處理。
public enum Spiciness {
    NOT,MILD,MEDIUM,HOT,FLAMING
}

public class SimpleEnumUse {
    public static void main(String[] args) {
        Spiciness spiciness = Spiciness.FLAMING;
        System.out.println(spiciness);
    }
}

//運行結果爲
FLAMING
  • 在你創建 enum 時,編譯器會自動添加一些有用的特性。 例如 ,它會創建 toString()方法, 以便你可以很方便地顯示某個 enum 實例的名字,這就是上面打印語句如何產生其輸出的答案。
  • 編譯器還會創建 ordinal() 方法,用來表示某個特定 enum 常量的聲明順序,以及static values()方法,用來按照 enum 常量的聲明順序,產生這些常量值構成的數組:
    public static void main(String[] args) {
        for (Spiciness spiciness: Spiciness.values()) {
            System.out.println(spiciness+" ordinal: "+spiciness.ordinal());
        }
    }
  • 儘管enum 看起來像是一種新的數據類型, 但是這個關鍵字只是爲 enum 生成對應的類時, 產生了某些編譯器行爲,因此在很大程度上,你可以將 enum 當做任何類來進行處理。 事實上,enum 確實是類,並且具有自己的方法。

  • enum 也可以用在 switch語句內。

 

總結:

  • 構造器,這種精巧的初始化機制,應該給了讀者很強的暗示: 初始化在Java中佔有至關重要的地位。
  • 在C++中 ,析構相當重要,因爲用 new 創建的對象必須明確被銷燬。在Java中,垃圾回收器會自動爲對象釋放內存,所以在很多場合下,類似的清理方法在Java中就不太需要了。
  • 在不需要類似解析函數的行爲的時候,Java 的垃圾回收器可以極大地簡化編程工作,而且在處理內存的時候也更安全。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章