來(lái)源:騰訊云
程序本質(zhì)上是對(duì)數(shù)據(jù)的處理(邏輯運(yùn)算),因此任何語(yǔ)言都需先解決如何表征【數(shù)據(jù)】這個(gè)核心概念。數(shù)據(jù)作為抽象的概念,天然的包含2個(gè)方面屬性:
類型
:類型決定了數(shù)據(jù)只能和同類型的數(shù)據(jù)進(jìn)行運(yùn)算才有意義,不同類型的數(shù)據(jù)必須進(jìn)行類型轉(zhuǎn)換數(shù)值
:是數(shù)據(jù)的數(shù)學(xué)意義上的大小或內(nèi)容。同時(shí)數(shù)據(jù)保存在內(nèi)存或磁盤中,總是占用一定的存儲(chǔ)空間,因此一個(gè)數(shù)據(jù)在程序中是由類型
、數(shù)值
和存儲(chǔ)空間
表示的。
Java 是一種強(qiáng)類型語(yǔ)言,每個(gè)變量在聲明時(shí)必須指定其數(shù)據(jù)類型。根據(jù)參數(shù)傳遞時(shí)的賦值方式,Java 中的數(shù)據(jù)類型分2類:
【資料圖】
參數(shù)傳遞時(shí)總是以值拷貝的形式,賦值給接收變量。
本質(zhì)是對(duì)數(shù)學(xué)意義上的純數(shù)值類數(shù)據(jù)的抽象,因此又叫值類型
.
注意:基本數(shù)據(jù)類型在語(yǔ)言層面是不可分割的基本單元,但硬件層面并非是不可分割的,因?yàn)閮?nèi)存的讀寫是以內(nèi)存行(64位)為基本單位的,占用2個(gè)或2個(gè)以上內(nèi)存行的數(shù)據(jù)在高并發(fā)下是不安全的。
即參數(shù)傳遞時(shí)是以拷貝引用地址的方式傳遞給接收變量,而非復(fù)制整個(gè)"數(shù)據(jù)"本體。
除了基本數(shù)據(jù)類型外的、其他結(jié)構(gòu)化的數(shù)據(jù)類型,如字符串類型String、或自定義的類(如Person),本質(zhì)上是對(duì)現(xiàn)實(shí)世界結(jié)構(gòu)化、關(guān)系化數(shù)據(jù)的抽象,因?yàn)橐粋€(gè)對(duì)象(如人)總是具備多個(gè)特征屬性的,每個(gè)屬性都是一個(gè)基本數(shù)據(jù)類型。
為什么不是拷貝值?
這類類型的變量本質(zhì)上是一段存儲(chǔ)空間的起始地址,因?yàn)榻Y(jié)構(gòu)化數(shù)據(jù)的存儲(chǔ)空間的大小是由所有屬性疊加的,且是可變的,不能或很難實(shí)現(xiàn)原子性的拷貝,且空間代價(jià)很大,因此參數(shù)傳遞時(shí)并不是機(jī)械的拷貝所有屬性、所有空間,而是僅復(fù)制拷貝起始地址就行了,其他字段可以據(jù)此基地址和字段順序進(jìn)行偏移計(jì)算所得。
基本類型:變量名指向具體的數(shù)值,參數(shù)傳遞會(huì)拷貝值的副本,原值不受影響
引用類型:變量名指向存數(shù)據(jù)對(duì)象的內(nèi)存地址,參數(shù)傳遞是復(fù)制內(nèi)存段的起始地址,最終指向同一內(nèi)存段。
相等語(yǔ)義基本類型:使用時(shí)需要賦具體值,使用 == 號(hào)判斷值是否相等。
引用類型:== 是判斷引用地址是否相同;通常應(yīng)重寫 equals 方法實(shí)現(xiàn)自定義的邏輯,如同一學(xué)生會(huì)存在多個(gè)緩存中,重寫equals,根據(jù)學(xué)號(hào)no是否相等判斷是否是同一學(xué)生。
Java 語(yǔ)言共提供了4 類、8 種基本類型
是對(duì)數(shù)學(xué)中整數(shù)的表達(dá),按照數(shù)值范圍和存儲(chǔ)空間大小順序:byte < short < int < long
是對(duì)數(shù)學(xué)中的小數(shù)的表達(dá),即有浮動(dòng)小數(shù)點(diǎn)的數(shù),
float
- 32 位,直接賦值時(shí)必須在數(shù)字后加上 f 或 F,指示編譯器這是一個(gè)float型浮點(diǎn)數(shù)
double
- 64 位,賦值時(shí)一般在數(shù)字后加 d 或 D,指示編譯器這是一個(gè)double型浮點(diǎn)數(shù)
是對(duì)Unicode編碼的表達(dá),Unicode編碼是對(duì)全世界所有主要語(yǔ)言中各類字符、符號(hào)的編碼,是將文檔轉(zhuǎn)存成計(jì)算機(jī)的二進(jìn)制序列進(jìn)行保存的理論基礎(chǔ)。
char
- 16 位,存儲(chǔ) Unicode 碼,用單引號(hào)賦值。
可計(jì)算的
java 提供字符型,可以更方便的表述字符,同時(shí)支持基于編碼的數(shù)值計(jì)算,因此char類型的值本質(zhì)還是數(shù)值,可以參與數(shù)值計(jì)算。
是對(duì)邏輯運(yùn)算值的表達(dá),即真True
和假False
。
boolean
- 只有 true 和 false 兩個(gè)取值。
非數(shù)值
boolean 類型是非數(shù)值類型的,因此和其他基本數(shù)據(jù)類型不能參與計(jì)算。
即在滿足需求的前提下,優(yōu)先使用更小的類型,可以節(jié)省大量的內(nèi)存,提升程序的性能。Java每個(gè)大類都提高了多個(gè)大小不一的類型,即是靈活的需要,更是內(nèi)存優(yōu)化的需要。
正如方法論中所說(shuō),不同類型的數(shù)據(jù)不能直接混合計(jì)算,必須轉(zhuǎn)換同一種類型。
Java 中,數(shù)據(jù)類型轉(zhuǎn)換有兩種方式:自動(dòng)轉(zhuǎn)換
和 強(qiáng)制轉(zhuǎn)換
在符合下面的規(guī)則下,Java編譯器會(huì)安全的、隱式的進(jìn)行轉(zhuǎn)換,降低用戶手動(dòng)轉(zhuǎn)換的壓力。
自動(dòng)膨脹原則即在精度不一致的混合場(chǎng)景下,Java編譯器會(huì)隱式的將精度較小的類型轉(zhuǎn)換成精度稍大的類型后才進(jìn)行計(jì)算,由小轉(zhuǎn)大,數(shù)據(jù)精度并不會(huì)丟失,因此是安全的。
膨脹的順序是:byte -> short/char -> int -> long -> float -> double
由下面的例子可以看出,Java編譯器不僅自動(dòng)優(yōu)化了變量類型,減少了內(nèi)存,而且自動(dòng)隱式的將 byte 轉(zhuǎn)換成了 float,整個(gè)表達(dá)式的最終結(jié)果已是float類型
再使用精度較小的int型變量接收時(shí)就會(huì)提示出錯(cuò)。
在不符合自動(dòng)轉(zhuǎn)換條件時(shí)或者根據(jù)用戶的需要,可以使用符號(hào)()對(duì)數(shù)據(jù)類型做強(qiáng)制的轉(zhuǎn)換。
注意?。。。?/strong>從精度大到精度小的強(qiáng)制轉(zhuǎn)換,因?yàn)榇鎯?chǔ)空間也會(huì)縮小一半,因此存在數(shù)據(jù)出錯(cuò)的不確定性問(wèn)題,用戶自身需要自我承擔(dān)這樣的風(fēng)險(xiǎn)。
可以看到雖然編譯階段是正常的,沒(méi)有提示錯(cuò)誤,但是運(yùn)行時(shí)的結(jié)果卻是一個(gè)不確定的數(shù)據(jù),而不是預(yù)期的。
為了基本數(shù)據(jù)類型可以與引用類型互相轉(zhuǎn)換,以利用彼此的特性,Java 為每一種基本數(shù)據(jù)類型提供了相應(yīng)的包裝(封裝)類。
Java基本數(shù)據(jù)類型的包裝類以value字段保留其對(duì)應(yīng)的數(shù)值,如Integer#value,可以通過(guò)構(gòu)造器或者valueof方法生成新的包裝對(duì)象實(shí)例。
享元模式(即緩存池模式)因?yàn)樵诙阎袆?chuàng)建新的對(duì)象是相對(duì)比較重的操作,同時(shí)基本數(shù)據(jù)類型的自動(dòng)裝箱又是程序中最常見(jiàn)的情況之一,因此為為避免重復(fù)創(chuàng)建這些常用的對(duì)象,Jdk實(shí)現(xiàn)的基本類型包裝類基本都會(huì)采用緩存池的設(shè)計(jì),即享元模式,數(shù)量由jvm參數(shù)XX:AutoBoxCacheMax指定。
饑餓池化
- 包裝類在首次加載時(shí)會(huì)對(duì)指定小范圍內(nèi)的數(shù)值進(jìn)行饑餓式池化,如java.lang.Integer.IntegerCache.high指定
懶加載池化
- 對(duì)每個(gè)首次使用的其他數(shù)值進(jìn)行懶加載池化和替換
享元模式是非常重要的設(shè)計(jì)模式一直,對(duì)內(nèi)存優(yōu)化和系統(tǒng)系統(tǒng)方面都是非常友好的。
即將基本數(shù)據(jù)類型轉(zhuǎn)換為對(duì)應(yīng)封裝類的引用類型,目的是獲得對(duì)應(yīng)封裝類的各類方法的能力。例如:int 轉(zhuǎn) Integer,編譯器是通過(guò)調(diào)用包裝類的 valueOf 方法實(shí)現(xiàn)的。
自動(dòng)裝箱(auto boxing)
當(dāng)基本數(shù)據(jù)類型賦值給對(duì)應(yīng)包裝類的引用類型時(shí),Java會(huì)自動(dòng)的將基本數(shù)值類型通過(guò)其包裝類的valueOf,在堆上創(chuàng)建其對(duì)應(yīng)的包裝類對(duì)象。
是將封裝類的引用類型轉(zhuǎn)換為基本數(shù)據(jù)類型,例如:Integer 轉(zhuǎn) int,目的是可以參與常規(guī)的數(shù)學(xué)運(yùn)算。本質(zhì)是編譯器通過(guò)調(diào)用包裝類的 xxxValue 方法實(shí)現(xiàn)的。(xxx 代表對(duì)應(yīng)的基本數(shù)據(jù)類型)
自動(dòng)拆箱(auto unboxing)
當(dāng)包裝類與基本數(shù)據(jù)類型混合運(yùn)算時(shí),Java會(huì)調(diào)用該包裝類的xxxValue獲得對(duì)應(yīng)的值類型的值,然后才參與表達(dá)式的計(jì)算。
最后整理了一個(gè)完整的腦圖。