教妹學(xué)Java:Java中的數(shù)據(jù)類型
我妹(親妹)今年上大學(xué)了,學(xué)的計(jì)算機(jī)編程,沒成想,她的一名老師竟然是我的讀者,我妹是又驚喜又恐慌,驚喜是她哥我的讀者群體還挺廣泛的嘛,恐慌的是萬一學(xué)不好豈不是很丟他哥的臉?但我相信她一定能學(xué)好!
“二哥,上一節(jié)提到了 Java 變量的數(shù)據(jù)類型,是不是指定了類型就限定了變量的取值范圍啊?”三妹吸了一口麥香可可奶茶后對(duì)我說。
“三妹,你不得了啊,長(zhǎng)進(jìn)很大嘛,都學(xué)會(huì)推理判斷了。Java 是一種靜態(tài)類型的編程語言,這意味著所有變量必須在使用之前聲明好,也就是必須得先指定變量的類型和名稱。”
Java 中的數(shù)據(jù)類型可分為 2 種:
1)基本數(shù)據(jù)類型。
基本數(shù)據(jù)類型是 Java 語言操作數(shù)據(jù)的基礎(chǔ),包括 boolean、char、byte、short、int、long、float 和 double,共 8 種。
2)引用數(shù)據(jù)類型。
除了基本數(shù)據(jù)類型以外的類型,都是所謂的引用類型。常見的有數(shù)組(對(duì),沒錯(cuò),數(shù)組是引用類型)、class(也就是類),以及接口(指向的是實(shí)現(xiàn)接口的類的對(duì)象)。
來個(gè)思維導(dǎo)圖,感受下。
通過上一節(jié)的學(xué)習(xí),我們知道變量可以分為局部變量、成員變量、靜態(tài)變量。
當(dāng)變量是局部變量的時(shí)候,必須得先初始化,否則編譯器不允許你使用它。拿 int 來舉例吧,看下圖。
當(dāng)變量是成員變量或者靜態(tài)變量時(shí),可以不進(jìn)行初始化,它們會(huì)有一個(gè)默認(rèn)值,仍然以 int 為例,來看代碼:
- /**
- * @author 微信搜「沉默王二」,回復(fù)關(guān)鍵字 PDF
- */
- public class LocalVar {
- private int a;
- static int b;
- public static void main(String[] args) {
- LocalVar lv = new LocalVar();
- System.out.println(lv.a);
- System.out.println(b);
- }
- }
來看輸出結(jié)果:
- 0
- 0
瞧見沒,int 作為成員變量時(shí)或者靜態(tài)變量時(shí)的默認(rèn)值是 0。那不同的基本數(shù)據(jù)類型,是有不同的默認(rèn)值和大小的,來個(gè)表格感受下。
數(shù)據(jù)類型 | 默認(rèn)值 | 大小 |
---|---|---|
boolean | false | 1比特 |
char | '\u0000' | 2字節(jié) |
byte | 0 | 1字節(jié) |
short | 0 | 2字節(jié) |
int | 0 | 4字節(jié) |
long | 0L | 8字節(jié) |
float | 0.0f | 4字節(jié) |
double | 0.0 | 8字節(jié) |
那三妹可能要問,“比特和字節(jié)是什么鬼?”
比特幣聽說過吧?字節(jié)跳動(dòng)聽說過吧?這些名字當(dāng)然不是亂起的,確實(shí)和比特、字節(jié)有關(guān)系。
1)bit(比特)
比特作為信息技術(shù)的最基本存儲(chǔ)單位,非常小,但大名鼎鼎的比特幣就是以此命名的,它的簡(jiǎn)寫為小寫字母“b”。
大家都知道,計(jì)算機(jī)是以二進(jìn)制存儲(chǔ)數(shù)據(jù)的,二進(jìn)制的一位,就是 1 比特,也就是說,比特要么為 0 要么為 1。
2)Byte(字節(jié))
通常來說,一個(gè)英文字符是一個(gè)字節(jié),一個(gè)中文字符是兩個(gè)字節(jié)。字節(jié)與比特的換算關(guān)系是:1 字節(jié) = 8 比特。
在往上的單位就是 KB,并不是 1000 字節(jié),因?yàn)橛?jì)算機(jī)只認(rèn)識(shí)二進(jìn)制,因此是 2 的 10 次方,也就是 1024 個(gè)字節(jié)。
(終于知道 1024 和程序員的關(guān)系了吧?狗頭保命)
接下來,我們?cè)賮碓敿?xì)地了解一下 8 種基本數(shù)據(jù)類型。
01、布爾
布爾(boolean)僅用于存儲(chǔ)兩個(gè)值:true 和 false,也就是真和假,通常用于條件的判斷。代碼示例:
- boolean flag = true;
02、byte
byte 的取值范圍在 -128 和 127 之間,包含 127。最小值為 -128,最大值為 127,默認(rèn)值為 0。
在網(wǎng)絡(luò)傳輸?shù)倪^程中,為了節(jié)省空間,常用字節(jié)來作為數(shù)據(jù)的傳輸方式。代碼示例:
- byte a = 10;
- byte b = -10;
03、short
short 的取值范圍在 -32,768 和 32,767 之間,包含 32,767。最小值為 -32,768,最大值為 32,767,默認(rèn)值為 0。代碼示例:
- short s = 10000;
- short r = -5000;
04、int
int 的取值范圍在 -2,147,483,648(-2 ^ 31)和 2,147,483,647(2 ^ 31 -1)(含)之間,默認(rèn)值為 0。如果沒有特殊需求,整形數(shù)據(jù)就用 int。代碼示例:
- int a = 100000;
- int b = -200000;
05、long
long 的取值范圍在 -9,223,372,036,854,775,808(-2^63) 和 9,223,372,036,854,775,807(2^63 -1)(含)之間,默認(rèn)值為 0。如果 int 存儲(chǔ)不下,就用 long,整形數(shù)據(jù)就用 int。代碼示例:
- long a = 100000L;
- long b = -200000L;
為了和 int 作區(qū)分,long 型變量在聲明的時(shí)候,末尾要帶上大寫的“L”。不用小寫的“l”,是因?yàn)樾懙?ldquo;l”容易和數(shù)字“1”混淆。
06、float
float 是單精度的浮點(diǎn)數(shù),遵循 IEEE 754(二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)),取值范圍是無限的,默認(rèn)值為 0.0f。float 不適合用于精確的數(shù)值,比如說貨幣。代碼示例:
- float f1 = 234.5f;
為了和 double 作區(qū)分,float 型變量在聲明的時(shí)候,末尾要帶上小寫的“f”。不需要使用大寫的“F”,是因?yàn)樾懙?ldquo;f”很容易辨別。
07、double
double 是雙精度的浮點(diǎn)數(shù),遵循 IEEE 754(二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)),取值范圍也是無限的,默認(rèn)值為 0.0。double 同樣不適合用于精確的數(shù)值,比如說貨幣。代碼示例:
- double d1 = 12.3
那精確的數(shù)值用什么表示呢?最好使用 BigDecimal,它可以表示一個(gè)任意大小且精度完全準(zhǔn)確的浮點(diǎn)數(shù)。針對(duì)貨幣類型的數(shù)值,也可以先乘以 100 轉(zhuǎn)成整形進(jìn)行處理。
Tips:?jiǎn)尉仁沁@樣的格式,1 位符號(hào),8 位指數(shù),23 位小數(shù),有效位數(shù)為 7 位。
雙精度是這樣的格式,1 位符號(hào),11 位指數(shù),52 為小數(shù),有效位數(shù)為 16 位。
取值范圍取決于指數(shù)位,計(jì)算精度取決于小數(shù)位(尾數(shù))。小數(shù)位越多,則能表示的數(shù)越大,那么計(jì)算精度則越高。
一個(gè)數(shù)由若干位數(shù)字組成,其中影響測(cè)量精度的數(shù)字稱作有效數(shù)字,也稱有效數(shù)位。有效數(shù)字指科學(xué)計(jì)算中用以表示一個(gè)浮點(diǎn)數(shù)精度的那些數(shù)字。一般地,指一個(gè)用小數(shù)形式表示的浮點(diǎn)數(shù)中,從第一個(gè)非零的數(shù)字算起的所有數(shù)字。如 1.24 和 0.00124 的有效數(shù)字都有 3 位。
08、char
char 可以表示一個(gè) 16 位的 Unicode 字符,其值范圍在 '\u0000'(0)和 '\uffff'(65,535)(包含)之間。代碼示例:
- char letterA = 'A'; // 用英文的單引號(hào)包裹住。
那三妹可能要問,“char 既然只有一個(gè)字符,為什么占 2 個(gè)字節(jié)呢?”
“主要是因?yàn)?Java 使用的是 Unicode 字符集而不是 ASCII 字符集。”
這又是為什么呢?我們留到下一節(jié)再講。
基本數(shù)據(jù)類型在作為成員變量和靜態(tài)變量的時(shí)候有默認(rèn)值,引用數(shù)據(jù)類型也有的。
String 是最典型的引用數(shù)據(jù)類型,所以我們就拿 String 類舉例,看下面這段代碼:
- /**
- * @author 微信搜「沉默王二」,回復(fù)關(guān)鍵字 PDF
- */
- public class LocalRef {
- private String a;
- static String b;
- public static void main(String[] args) {
- LocalRef lv = new LocalRef();
- System.out.println(lv.a);
- System.out.println(b);
- }
- }
輸出結(jié)果如下所示:
- null
- null
null 在 Java 中是一個(gè)很神奇的存在,在你以后的程序生涯中,見它的次數(shù)不會(huì)少,尤其是伴隨著令人煩惱的“空指針異常”,也就是所謂的 NullPointerException。
也就是說,引用數(shù)據(jù)類型的默認(rèn)值為 null,包括數(shù)組和接口。
那三妹是不是很好奇,為什么數(shù)組和接口也是引用數(shù)據(jù)類型啊?
先來看數(shù)組:
- /**
- * @author 微信搜「沉默王二」,回復(fù)關(guān)鍵字 java
- */
- public class ArrayDemo {
- public static void main(String[] args) {
- int [] arrays = {1,2,3};
- System.out.println(arrays);
- }
- }
arrays 是一個(gè) int 類型的數(shù)組,對(duì)吧?打印結(jié)果如下所示:
- [I@2d209079
[I 表示數(shù)組是 int 類型的,@ 后面是十六進(jìn)制的 hashCode——這樣的打印結(jié)果太“人性化”了,一般人表示看不懂!為什么會(huì)這樣顯示呢?查看一下 java.lang.Object 類的toString() 方法就明白了。
數(shù)組雖然沒有顯式定義成一個(gè)類,但它的確是一個(gè)對(duì)象,繼承了祖先類 Object 的所有方法。那為什么數(shù)組不單獨(dú)定義一個(gè)類來表示呢?就像字符串 String 類那樣呢?
一個(gè)合理的解釋是 Java 將其隱藏了。假如真的存在一個(gè) Array.java,我們也可以假想它真實(shí)的樣子,它必須要定義一個(gè)容器來存放數(shù)組的元素,就像 String 類那樣。
- public final class String
- implements java.io.Serializable, Comparable<String>, CharSequence {
- /** The value is used for character storage. */
- private final char value[];
- }
數(shù)組內(nèi)部定義數(shù)組?沒必要的!
再來看接口:
- /**
- * @author 微信搜「沉默王二」,回復(fù)關(guān)鍵字 Java
- */
- public class IntefaceDemo {
- public static void main(String[] args) {
- List<String> list = new ArrayList<>();
- System.out.println(list);
- }
- }
List 是一個(gè)非常典型的接口:
- public interface List<E> extends Collection<E> {}
而 ArrayList 是 List 接口的一個(gè)實(shí)現(xiàn):
- public class ArrayList<E> extends AbstractList<E>
- implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- {}
對(duì)于接口類型的引用變量來說,你沒法直接 new 一個(gè):
只能 new 一個(gè)實(shí)現(xiàn)它的類的對(duì)象——那自然接口也是引用數(shù)據(jù)類型了。
來看一下基本數(shù)據(jù)類型和引用數(shù)據(jù)類型之間最大的差別。
基本數(shù)據(jù)類型:
1、變量名指向具體的數(shù)值。2、基本數(shù)據(jù)類型存儲(chǔ)在棧上。
引用數(shù)據(jù)類型:
1、變量名指向的是存儲(chǔ)對(duì)象的內(nèi)存地址,在棧上。2、內(nèi)存地址指向的對(duì)象存儲(chǔ)在堆上。
看到這,三妹是不是又要問,“堆是什么,棧又是什么?”
堆是堆(heap),棧是棧(stack),如果看到“堆棧”的話,請(qǐng)不要懷疑自己,那是翻譯的錯(cuò),堆棧也是棧,反正我很不喜歡“堆棧”這種叫法,容易讓新人掉坑里。
堆是在程序運(yùn)行時(shí)在內(nèi)存中申請(qǐng)的空間(可理解為動(dòng)態(tài)的過程);切記,不是在編譯時(shí);因此,Java 中的對(duì)象就放在這里,這樣做的好處就是:
當(dāng)需要一個(gè)對(duì)象時(shí),只需要通過 new 關(guān)鍵字寫一行代碼即可,當(dāng)執(zhí)行這行代碼時(shí),會(huì)自動(dòng)在內(nèi)存的“堆”區(qū)分配空間——這樣就很靈活。
棧,能夠和處理器(CPU,也就是腦子)直接關(guān)聯(lián),因此訪問速度更快。既然訪問速度快,要好好利用啊!Java 就把對(duì)象的引用放在棧里。為什么呢?因?yàn)橐玫氖褂妙l率高嗎?
不是的,因?yàn)?Java 在編譯程序時(shí),必須明確的知道存儲(chǔ)在棧里的東西的生命周期,否則就沒法釋放舊的內(nèi)存來開辟新的內(nèi)存空間存放引用——空間就那么大,前浪要把后浪拍死在沙灘上啊。
這么說就理解了吧?
“好了,三妹,關(guān)于 Java 中的數(shù)據(jù)類型就先說這么多吧,你是不是已經(jīng)清楚了?”轉(zhuǎn)動(dòng)了一下僵硬的脖子后,我對(duì)三妹說。
“差不多,二哥,我覺得還得再消化會(huì)。”
本文轉(zhuǎn)載自微信公眾號(hào)「沉默王二」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系沉默王二公眾號(hào)。