教妹學(xué)Java:Java中的數(shù)據(jù)類型
我妹(親妹)今年上大學(xué)了,學(xué)的計算機(jī)編程,沒成想,她的一名老師竟然是我的讀者,我妹是又驚喜又恐慌,驚喜是她哥我的讀者群體還挺廣泛的嘛,恐慌的是萬一學(xué)不好豈不是很丟他哥的臉?但我相信她一定能學(xué)好!
“二哥,上一節(jié)提到了 Java 變量的數(shù)據(jù)類型,是不是指定了類型就限定了變量的取值范圍啊?”三妹吸了一口麥香可可奶茶后對我說。
“三妹,你不得了啊,長進(jìn)很大嘛,都學(xué)會推理判斷了。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ù)組(對,沒錯,數(shù)組是引用類型)、class(也就是類),以及接口(指向的是實(shí)現(xiàn)接口的類的對象)。
來個思維導(dǎo)圖,感受下。
通過上一節(jié)的學(xué)習(xí),我們知道變量可以分為局部變量、成員變量、靜態(tài)變量。
當(dāng)變量是局部變量的時候,必須得先初始化,否則編譯器不允許你使用它。拿 int 來舉例吧,看下圖。
當(dāng)變量是成員變量或者靜態(tài)變量時,可以不進(jìn)行初始化,它們會有一個默認(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 作為成員變量時或者靜態(tài)變量時的默認(rèn)值是 0。那不同的基本數(shù)據(jù)類型,是有不同的默認(rèn)值和大小的,來個表格感受下。
| 數(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)然不是亂起的,確實(shí)和比特、字節(jié)有關(guān)系。
1)bit(比特)
比特作為信息技術(shù)的最基本存儲單位,非常小,但大名鼎鼎的比特幣就是以此命名的,它的簡寫為小寫字母“b”。
大家都知道,計算機(jī)是以二進(jìn)制存儲數(shù)據(jù)的,二進(jìn)制的一位,就是 1 比特,也就是說,比特要么為 0 要么為 1。
2)Byte(字節(jié))
通常來說,一個英文字符是一個字節(jié),一個中文字符是兩個字節(jié)。字節(jié)與比特的換算關(guān)系是:1 字節(jié) = 8 比特。
在往上的單位就是 KB,并不是 1000 字節(jié),因?yàn)橛嬎銠C(jī)只認(rèn)識二進(jìn)制,因此是 2 的 10 次方,也就是 1024 個字節(jié)。
(終于知道 1024 和程序員的關(guān)系了吧?狗頭保命)
接下來,我們再來詳細(xì)地了解一下 8 種基本數(shù)據(jù)類型。
01、布爾
布爾(boolean)僅用于存儲兩個值: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 存儲不下,就用 long,整形數(shù)據(jù)就用 int。代碼示例:
- long a = 100000L;
- long b = -200000L;
為了和 int 作區(qū)分,long 型變量在聲明的時候,末尾要帶上大寫的“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 型變量在聲明的時候,末尾要帶上小寫的“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,它可以表示一個任意大小且精度完全準(zhǔn)確的浮點(diǎn)數(shù)。針對貨幣類型的數(shù)值,也可以先乘以 100 轉(zhuǎn)成整形進(jìn)行處理。
Tips:單精度是這樣的格式,1 位符號,8 位指數(shù),23 位小數(shù),有效位數(shù)為 7 位。
雙精度是這樣的格式,1 位符號,11 位指數(shù),52 為小數(shù),有效位數(shù)為 16 位。
取值范圍取決于指數(shù)位,計算精度取決于小數(shù)位(尾數(shù))。小數(shù)位越多,則能表示的數(shù)越大,那么計算精度則越高。
一個數(shù)由若干位數(shù)字組成,其中影響測量精度的數(shù)字稱作有效數(shù)字,也稱有效數(shù)位。有效數(shù)字指科學(xué)計算中用以表示一個浮點(diǎn)數(shù)精度的那些數(shù)字。一般地,指一個用小數(shù)形式表示的浮點(diǎn)數(shù)中,從第一個非零的數(shù)字算起的所有數(shù)字。如 1.24 和 0.00124 的有效數(shù)字都有 3 位。
08、char
char 可以表示一個 16 位的 Unicode 字符,其值范圍在 '\u0000'(0)和 '\uffff'(65,535)(包含)之間。代碼示例:
- char letterA = 'A'; // 用英文的單引號包裹住。
那三妹可能要問,“char 既然只有一個字符,為什么占 2 個字節(jié)呢?”
“主要是因?yàn)?Java 使用的是 Unicode 字符集而不是 ASCII 字符集。”
這又是為什么呢?我們留到下一節(jié)再講。
基本數(shù)據(jù)類型在作為成員變量和靜態(tài)變量的時候有默認(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 中是一個很神奇的存在,在你以后的程序生涯中,見它的次數(shù)不會少,尤其是伴隨著令人煩惱的“空指針異常”,也就是所謂的 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 是一個 int 類型的數(shù)組,對吧?打印結(jié)果如下所示:
- [I@2d209079
[I 表示數(shù)組是 int 類型的,@ 后面是十六進(jìn)制的 hashCode——這樣的打印結(jié)果太“人性化”了,一般人表示看不懂!為什么會這樣顯示呢?查看一下 java.lang.Object 類的toString() 方法就明白了。
數(shù)組雖然沒有顯式定義成一個類,但它的確是一個對象,繼承了祖先類 Object 的所有方法。那為什么數(shù)組不單獨(dú)定義一個類來表示呢?就像字符串 String 類那樣呢?
一個合理的解釋是 Java 將其隱藏了。假如真的存在一個 Array.java,我們也可以假想它真實(shí)的樣子,它必須要定義一個容器來存放數(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 是一個非常典型的接口:
- public interface List<E> extends Collection<E> {}
而 ArrayList 是 List 接口的一個實(shí)現(xiàn):
- public class ArrayList<E> extends AbstractList<E>
- implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- {}
對于接口類型的引用變量來說,你沒法直接 new 一個:
只能 new 一個實(shí)現(xiàn)它的類的對象——那自然接口也是引用數(shù)據(jù)類型了。
來看一下基本數(shù)據(jù)類型和引用數(shù)據(jù)類型之間最大的差別。
基本數(shù)據(jù)類型:
1、變量名指向具體的數(shù)值。2、基本數(shù)據(jù)類型存儲在棧上。
引用數(shù)據(jù)類型:
1、變量名指向的是存儲對象的內(nèi)存地址,在棧上。2、內(nèi)存地址指向的對象存儲在堆上。
看到這,三妹是不是又要問,“堆是什么,棧又是什么?”
堆是堆(heap),棧是棧(stack),如果看到“堆棧”的話,請不要懷疑自己,那是翻譯的錯,堆棧也是棧,反正我很不喜歡“堆棧”這種叫法,容易讓新人掉坑里。
堆是在程序運(yùn)行時在內(nèi)存中申請的空間(可理解為動態(tài)的過程);切記,不是在編譯時;因此,Java 中的對象就放在這里,這樣做的好處就是:
當(dāng)需要一個對象時,只需要通過 new 關(guān)鍵字寫一行代碼即可,當(dāng)執(zhí)行這行代碼時,會自動在內(nèi)存的“堆”區(qū)分配空間——這樣就很靈活。
棧,能夠和處理器(CPU,也就是腦子)直接關(guān)聯(lián),因此訪問速度更快。既然訪問速度快,要好好利用啊!Java 就把對象的引用放在棧里。為什么呢?因?yàn)橐玫氖褂妙l率高嗎?
不是的,因?yàn)?Java 在編譯程序時,必須明確的知道存儲在棧里的東西的生命周期,否則就沒法釋放舊的內(nèi)存來開辟新的內(nèi)存空間存放引用——空間就那么大,前浪要把后浪拍死在沙灘上啊。
這么說就理解了吧?
“好了,三妹,關(guān)于 Java 中的數(shù)據(jù)類型就先說這么多吧,你是不是已經(jīng)清楚了?”轉(zhuǎn)動了一下僵硬的脖子后,我對三妹說。
“差不多,二哥,我覺得還得再消化會。”
本文轉(zhuǎn)載自微信公眾號「沉默王二」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系沉默王二公眾號。






























