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