摘要:Java8增強的包裝類動裝箱:把一個基本類型變量直接賦給對應(yīng)的包裝類變量,或者賦給Object變量(Object是所有類的父類,子類對象可以直接賦給父類變量);自動拆箱:允許直接把包裝類對象直接賦給一個對應(yīng)的基本類型變量。包裝類實現(xiàn)基本類型變量和字符串之間的轉(zhuǎn)換。把字符串類型的值轉(zhuǎn)換為基本類型的值有兩種方式:1.利用包裝類提供的parseXxx(String s)靜態(tài)方法(除了Chara
Java8增強的包裝類
動裝箱:把一個基本類型變量直接賦給對應(yīng)的包裝類變量,或者賦給Object變量(Object是所有類的父類,子類對象可以直接賦給父類變量);
自動拆箱:允許直接把包裝類對象直接賦給一個對應(yīng)的基本類型變量。
包裝類實現(xiàn)基本類型變量和字符串之間的轉(zhuǎn)換。把字符串類型的值轉(zhuǎn)換為基本類型的值有兩種方式:
1.利用包裝類提供的parseXxx(String s)靜態(tài)方法(除了Character之外的所有包裝類都提供了該方法)。 2.利用包裝類提供的Xxx(String s)構(gòu)造器。String intStr = "123"; int it1 = Integer.parseInt(inStr); int it2 = new Integer(intStr);
String類提供了多個重載valueOf()方法,用于將基本類型變量轉(zhuǎn)換成字符串:
String ftStr = String.valueOf(2.345f); String boolStr = String.valueOf(true);
將基本類型轉(zhuǎn)換成字符串的更簡單方法:將基本類型變量和""進(jìn)行連接運算,系統(tǒng)會自動把基本類型變量轉(zhuǎn)換成字符串:
//intStr的值為"5" String intStr = 5 + "";
包裝類型的變量是引用數(shù)據(jù)類型,但包裝類的實例可以與數(shù)據(jù)類型的值進(jìn)行比較,這種比較是直接取出包裝類實例所包裝的數(shù)值來進(jìn)行比較的。
靜態(tài)compare(xxx val1, xxx val2)方法,比較兩個基本類型值的大小,輸出1、0、-1。
兩個boolean類型值進(jìn)行比較時,true>false。
Java7為Character包裝類增加了大量的工具方法來對一個字符進(jìn)行判斷,Java8再次增強了這些包裝類的功能,其中一個重要的增強就是支持無符號算術(shù)運算。
處理對象
打印對象和toString方法
toString()方法是Object類里的一個實例方法,所有的Java類都是Object類的子類,因此所有的Java對象都具有toString()方法。所有的Java對象都可以和字符串進(jìn)行連接運算,當(dāng)Java對象和字符串進(jìn)行連接運算時,系統(tǒng)自動調(diào)用Java對象toString()方法的返回值和字符串進(jìn)行連接運算。
toString()方法是一個非常特殊的方法,它是一個“自我描述”方法,該方法通常用于實現(xiàn)這樣一個功能:當(dāng)程序員直接打印該對象時,系統(tǒng)將會輸出該對象的“自我描述”信息,用以告訴外界該對象具有的狀態(tài)信息。
Object類提供的toString()方法總是返回該對象實現(xiàn)類的“類名+@+hashCode”值。
==和equals方法
當(dāng)使用==來判斷兩個變量是否相等時,如果兩個變量是基本類型變量,且都是數(shù)值類型(不一定要求數(shù)據(jù)類型嚴(yán)格相同),則只要兩個變量的值相等,就將返回true。但對于兩個引用類型變量,只有它們指向同一個對象時,==判斷才會返回true。==不可用于比較類型上沒有父子關(guān)系的兩個對象。
常量池(constant pool)專門用于管理在編譯時被確定并被保存在已編譯的.class文件中的一些數(shù)據(jù)。包括了關(guān)于類、方法、接口中的常量,還包括字符串常量。當(dāng)Java程序直接使用形如"hello"的字符串直接量(包括可以在編譯時就計算出來的字符串值)時,JVM將會使用常量池來管理這些字符串;當(dāng)使用new String("hello")時,JVM會先使用常量池來管理"hello"直接量,再調(diào)用String類的構(gòu)造器來創(chuàng)建一個新的String對象,新創(chuàng)建的String對象被保存在堆內(nèi)存中。換句話說,new String("hello")一共產(chǎn)生了兩個字符串對象。
equals()方法是Object類提供的一個實例方法,因此所有引用變量都可調(diào)用該方法來判斷是否與其他引用變量相等。String類以及重寫了Object的equals()方法,String的equals()方法判斷兩個字符串相等的標(biāo)準(zhǔn)是:只要兩個字符串所包含的字符序列相同,通過equals()比較將返回true,否則將返回false。
class Person { private String name; private String idStr; public Person(){} public Person(String name , String idStr) { this.name = name; this.idStr = idStr; } // 此處省略name和idStr的setter和getter方法。 // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // idStr的setter和getter方法 public void setIdStr(String idStr) { this.idStr = idStr; } public String getIdStr() { return this.idStr; } // 重寫equals()方法,提供自定義的相等標(biāo)準(zhǔn) public boolean equals(Object obj) { // 如果兩個對象為同一個對象 if (this == obj) return true; // 只有當(dāng)obj是Person對象 if (obj != null && obj.getClass() == Person.class) { Person personObj = (Person)obj; // 并且當(dāng)前對象的idStr與obj對象的idStr相等才可判斷兩個對象相等 if (this.getIdStr().equals(personObj.getIdStr())) { return true; } } return false; } } public class OverrideEqualsRight { public static void main(String[] args) { Person p1 = new Person("孫悟空" , "12343433433"); Person p2 = new Person("孫行者" , "12343433433"); Person p3 = new Person("孫悟飯" , "99933433"); // p1和p2的idStr相等,所以輸出true System.out.println("p1和p2是否相等?" + p1.equals(p2)); // p2和p3的idStr不相等,所以輸出false System.out.println("p2和p3是否相等?" + p2.equals(p3)); } }
重寫equals()方法應(yīng)該滿足下列條件:
1.自反性:對任意x,x.equals(X)一定返回true。 2.對稱性:對任意x和y,如果y.equals(x)返回true,則x.equals(y)也返回true。 3.傳遞性:對任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,則x.equals(z)也返回true。 4.一致性:對任意x和y,如果對象中用于等價比較的信息沒有改變,那么無論調(diào)用x.equals(y)多少次,返回的結(jié)果應(yīng)該保持一致,要么一直是true,要么一直是false。 5.對任何不是null的x,x.equals(null)一定返回false。
Object默認(rèn)提供的equals()只是比較對象的地址,即Object類的equals()方法比較的結(jié)果與==運算符比較的結(jié)果完全相同。在實際應(yīng)用中常常需要重寫equals()方法,重寫equals()方法時,相等條件是由業(yè)務(wù)要求決定的,因此equals()方法的實現(xiàn)也是由業(yè)務(wù)要求決定的。
類成員
理解類成員
在Java類里只能包含成員變量、方法、構(gòu)造器、初始化塊、內(nèi)部類(包括接口、枚舉)5種成員。static可以修飾成員變量、方法、初始化塊、內(nèi)部類(包括接口、枚舉),以static修飾的成員就是類成員。類成員屬于整個類,而不屬于單個對象。
類變量屬于整個類,當(dāng)系統(tǒng)第一次準(zhǔn)備使用該類時,系統(tǒng)會為該類變量分配內(nèi)存空間,類變量開始生效,直到該類被卸載,該類的類變量所占有的內(nèi)存才被系統(tǒng)的垃圾回收機制回收。類變量生存范圍幾乎等同于該類的生存范圍。當(dāng)類初始化完成后,類變量也被初始化完成。當(dāng)通過對象來訪問類變量時,系統(tǒng)會在底層轉(zhuǎn)換為通過該類來訪問類變量。
當(dāng)使用實例來訪問類成員時,實際上依然是委托給該類來訪問類成員,因此即使某個實例為null,它也可以訪問它所屬類的類成員。如果一個null對象訪問實例成員(包括實例變量和實例方法),將會引發(fā)NullPointException異常,因為null表明該實例根本不存在,既然實例不存在,那么它的實例變量和實例方法自然也不存在。
單例(Singleton)類
如果一個類始終只能創(chuàng)建一個實例,則這個類被稱為單例類。
在一些特殊場景下,要求不允許自由創(chuàng)建該類的對象,而只允許為該類創(chuàng)建一個對象。為了避免其他類自由創(chuàng)建該類的實例,應(yīng)該把該類的構(gòu)造器使用private修飾,從而把該類的所有構(gòu)造器隱藏起來。
根據(jù)良好封裝的原則:一旦把該類的構(gòu)造器隱藏起來,就需要一個public方法作為該類的訪問點,用于創(chuàng)建該類的對象,且該方法必須使用static修飾(因為調(diào)用該方法之前還不存在對象,因此調(diào)用該方法的不可能是對象,只能是類)。
除此之外,該類還必須緩存已經(jīng)創(chuàng)建的對象,否則該類無法知道是否曾經(jīng)創(chuàng)建過對象,也就無法保證只創(chuàng)建了一個對象。為此該類需要使用一個成員變量來保存曾經(jīng)創(chuàng)建的對象,因為該成員變量需要被上面的靜態(tài)方法訪問,故該成員變量必須使用static修飾。
class Singleton { // 使用一個類變量來緩存曾經(jīng)創(chuàng)建的實例 private static Singleton instance; // 將構(gòu)造器使用private修飾,隱藏該構(gòu)造器 private Singleton(){} // 提供一個靜態(tài)方法,用于返回Singleton實例 // 該方法可以加入自定義的控制,保證只產(chǎn)生一個Singleton對象 public static Singleton getInstance() { // 如果instance為null,表明還不曾創(chuàng)建Singleton對象 // 如果instance不為null,則表明已經(jīng)創(chuàng)建了Singleton對象, // 將不會重新創(chuàng)建新的實例 if (instance == null) { // 創(chuàng)建一個Singleton對象,并將其緩存起來 instance = new Singleton(); } return instance; } } public class SingletonTest { public static void main(String[] args) { // 創(chuàng)建Singleton對象不能通過構(gòu)造器, // 只能通過getInstance方法來得到實例 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // 將輸出true } }
final修飾符
final關(guān)鍵字可用于修飾類、變量和方法
final成員變量
final修飾的成員變量必須由程序員顯式地指定初始值。
final修飾的類變量、實例變量能指定初始值的地方如下: 1.類變量:必須在靜態(tài)初始化塊中指定初始值或聲明該類變量時指定初始值,而且只能在兩個地方的其中之一指定。 2.實例變量:必須在非靜態(tài)初始化塊、聲明該實例變量或構(gòu)造器中指定初始值,而且只能在三個地方的其中之一指定。
與普通成員變量不同的是,final成員變量(包括實例變量和類變量)必須由程序員顯式初始化,系統(tǒng)不會對final成員進(jìn)行隱式初始化。如果打算在構(gòu)造器、初始化塊中對final成員變量進(jìn)行初始化,則不要在初始化之前就訪問成員變量的值。
final局部變量
系統(tǒng)不會對局部變量進(jìn)行初始化,局部變量必須由程序員顯式初始化。因此使用final修飾局部變量時,既可以在定義時指定默認(rèn)值,也可以不知道默認(rèn)值。如果final修飾的局部變量在定義時已經(jīng)指定默認(rèn)值,則后面代碼中不能再對該變量賦值。
final修飾基本類型變量和引用類型變量的區(qū)別
當(dāng)使用final修飾基本類型變量時,不能對基本類型變量重新賦值,因此基本類型變量不能被改變。但對于引用類型變量而言,它保存的僅僅是一個引用,final只保證這個引用聯(lián)系不了所引用的地址不會改變,即一直引用同一個對象,但這個對象完全可以發(fā)生改變。
可執(zhí)行“宏替換”的final變量
對一個final變量來說,不管它是類變量、實例變量,還是局部變量,只要該變量滿足三個條件,這個final變量就不再是一個變量,而是相當(dāng)于一個直接量。
使用final修飾符修飾
在定義該final變量時指定了初始值
該初始值可以在編譯時就被確定下來
final修飾符的一個重要用途就是定義“宏變量”。當(dāng)定義final變量時就為該變量指定了初始值,而且該初始值可以在編譯時就確定下來,那么這個final變量本質(zhì)上就是一個“宏變量”,編譯器會把程序中所有用到該變量的地方直接替換成該變量的值。如果被賦的表達(dá)式只是基本的算術(shù)表達(dá)式或字符串連接運算,沒有訪問普通變量,調(diào)用方法,Java編譯器同樣會將這種final變量當(dāng)成“宏變量”處理。
Java會使用常量池來管理曾經(jīng)用過的字符串直接量,例如執(zhí)行String a = "java";語句之后,常量池中就會緩存一個字符串"java";如果程序再次執(zhí)行String b = "java";,系統(tǒng)將會讓b直接指向常量池中的"java"字符串,因此a==b將會返回true。
final方法
final修飾的方法不可被重寫,如果出于某些原因,不希望子類重寫父類的某個方法,則可以使用final修飾該方法。
如果子類中定義一個與父類private方法用相同方法名、相同形參列表、相同返回值類型的方法,不是方法重寫,只是重新定義了一個新方法。因此,即使使用final修飾一個private訪問權(quán)限的方法,依然可以在其子類中定義與該方法具有相同方法名、相同形參列表、相同返回值類型的方法。private final
final修飾的方法不能被重寫,可以被重載
final類
final修飾的類不可被繼承。
不可變類
不可變(immutable)類的意思是創(chuàng)建該類的實例后,該實例的實例變量是不可改變的。Java的8個包裝類和java.lang.String類都是不可變類,當(dāng)創(chuàng)建它們的實例后,其實例的實例變量是不可改變。
如果需要創(chuàng)建自定義的不可變類,可遵守如下規(guī)則
使用private和final修飾符來修飾該類的成員變量
提供帶參數(shù)構(gòu)造器,用于根據(jù)傳入?yún)?shù)來初始化類里的成員變量
僅為該類的成員變量提供getter方法,不要為該類的成員變量提供setter方法,因為普通方法無法修改final修飾的成員變量
如果有必要,重寫Object類的hashCode()和equals()方法。equals()方法根據(jù)關(guān)鍵成員變量來作為兩個對象是否相等的標(biāo)準(zhǔn),除此之外,還應(yīng)該保證兩個用equals()方法判斷為相等的對象的hashCode()也相等。