計算機的世界只有0和1。
1.1 進制
- 十進制整數->二進制整數:除2倒取余
- 二進制->十進制:權值相加法
-
- 結論:1位8進制值 = 3位二進制值,1位十六進制值 = 4位二進制值
public class JinZhiDemo {public static void main(String[] args) {System.out.println(10);//十進制System.out.println(0B10);//二進制,輸出的是十進制結果System.out.println(010);//八進制,輸出的是十進制結果System.out.println(0x10);//十六進制,輸出的是十進制結果System.out.println(25);//十進制//System.out.println(0B25);//二進制,錯誤,二進制只有0-1System.out.println(025);//八進制System.out.println(0x25);//十六進制}
}
1.2 符號位、原碼、反碼、補碼(理解)
- 符號位:最高位,表示正負。0代表正數,1代表負數
- 原碼:最直觀的二進制表示法:符號位加上真值的絕對值。+25:
0001 1001
,-25:1001 1001
。 - 反碼、補碼
-
- 正數:反碼、補碼與原碼一樣。
- 負數的反碼:原碼的符號位不變,數值位按位取反(0變1,1變0)
-
- 負數的補碼:反碼加1
在Java中的整數類型(byte
, short
, int
, long
)均采用補碼表示
1.2.1 為什么使用補碼?
直接說結論:
原碼的主要缺陷有:
- 存在兩種零:+0( 0000 0000) 和 -0( 1000 0000),浪費了一個二進制組合,并且在邏輯上是冗余的。
- 加減運算復雜:符號位不能直接參與運算,需要根據符號位進行額外判斷(正數和負數相加需要取絕對值大者的符號),硬件實現效率低下。
原碼的加法運算:
原碼雖然是最直觀的二進制表示,但是用原碼進行運算時會存在致命的缺陷(eg (+25) + (-25)得不到0)。
→原碼的加法不支持符號修正:不能自動讓結果的符號與數值正確對應,并且不支持進位消除
(多出的進位被丟棄,不影響結果)
一個字節為例的二進制值原碼加法:
+25: 0001 1001
-25: 1001 1001
+-----------------1011 0010 -50(錯誤)
-5: 1000 0101
-5: 1000 0101 (-5)
+ -----------------1 0000 1010 結果為 10(注意:最高位溢出了1位舍棄)符號位不僅僅要用來表示正、負數,還要考慮計算問題。
補碼的優勢在于:
- 統一零的表示:0的補碼只有一種形式,即 0000 0000。
- 簡化硬件設計:減法運算可以轉換為加法運算( A - B = A + (-B)),CPU只需一套加法電路即可處理加減法,硬件實現高效簡單。
- 自然處理溢出:運算時產生的進位可以直接舍棄,不影響結果的正確性( eg(+25) + (-25)舍棄進位后得到0。
- 反碼:試圖改進運算,仍有雙零和循環進位問題(歷史概念)
- Java實踐:
byte
,short
,int
,long
均用補碼。Integer.toBinaryString()
可查看補碼形式.
1.3 計算機存儲單元
最小單位
:1位,一個比特位
,bit
,它只有0和1。
最基本的單位
:字節
,Byte
,1B = 8bit。字節是數據存儲和尋址的基本單元,例如一個英文字符通常占用1個字節,而一個中文字符通常占用2個字節。
// Java內部使用Unicode編碼
char c = 'A'; // 英文字符,1個char單位(2字節)
char ch = '中'; // 中文字符,1個char單位(2字節)// 但轉換為字節數組時,取決于編碼方式
String str = "A中";
byte[] utf8Bytes = str.getBytes("UTF-8"); // A占1字節,中占3字節
byte[] gbkBytes = str.getBytes("GBK"); // A占1字節,中占2字節
- 1 KB(Kilobyte,千字節) = 1024 Byte
- 1 MB(Megabyte,兆字節) = 1024 KB
- 1 GB(Gigabyte,吉字節) = 1024 MB
- 1 TB(Terabyte,太字節) = 1024 GB
- 1 PB(Petabyte,拍字節) = 1024 TB
- 1 EB(Exabyte,艾字節) = 1024 PB
- 1 ZB(Zettabyte,澤字節) = 1024 EB
PS:帶寬
(1)帶寬:100Mb = 100/8MB
(2)硬盤等硬件 1TB 實際會少于1TB:工業上硬盤制造商有時會使用 用1000代替1024進行進制換算(1GB = 1000MB),導致操作系統中識別的可用空間略小于標稱值。
為了便于內存管理,不同虛擬機實現不一樣,很多虛擬機會給一個boolean的變量分配1個字節。
面試題:
boolean的寬度是多少?
- boolean類型的值true和false底層就是用1和0表示,1代表true,0代表false。
- 理論上1個比特位就夠了,但是實際中,大多數JVM實現(如HotSpot)為每個
boolean
變量分配1個字節(8位)。因為計算機中通常以字節為最小尋址單位,按字節處理效率更高。在數組(如boolean[]
)中,JVM可能會進行優化,嘗試使用1位來表示每個元素。
1.4 不同數據類型的存儲
寬度:不同數據類型的寬度(字節數)不同,與它們的取值范圍直接相關。對于整數類型,其取值范圍由位數和補碼表示法決定。
數據類型 | 內存占用(字節) |
byte | 1個字節 |
short | 2個字節 |
int | 4個字節 |
long | 8個字節 |
float | 4個字節 |
double | 8個字節 |
char | 2個字節 |
1.5 一個字節可以表示多大數字
1個字節可以表示的范圍是 -128 ~ 127
同理:對于一個有符號整數類型,其位數(n bits)和取值范圍的關系是:
最小值:?2^(n?1)
最大值:2^(n?1) ?1
補碼:
正數:0000 0001 十進制10111 1111 十進制127
負數:1000 0001 十進制-127 補碼:1000 0001反碼:1000 0000原碼:1111 11111111 1111 十進制:-1補碼:1111 1111反碼:1111 1110原碼:1000 0001
兩個特殊值:0000 0000 十進制:01000 0000 十進制:-128 最高位是1,肯定是負數,還要滿足計算規則-127: 補碼 1000 0001
1 : 補碼 0000 0001
相減 ------------------ 補碼:1000 0000 (十進制-128)數學中 -127 -1 = -128
public class IntegerStorageDemo {public static void main(String[] args) {// 1. 驗證byte范圍byte minByte = Byte.MIN_VALUE; // -128byte maxByte = Byte.MAX_VALUE; // 127System.out.println("Byte Range: " + minByte + " to " + maxByte);// 2. 查看-128的補碼 (1000 0000)// 將byte轉換為int并掩碼最低8位,以查看其二進制表示System.out.println("-128 (byte) 的補碼: " +String.format("%8s", Integer.toBinaryString(minByte & 0xFF)).replace(' ', '0'));// 3. 驗證運算:(-127) + (-1) = -128byte b1 = -127;byte b2 = -1;byte sum = (byte) (b1 + b2); // 需要強制轉換,因為byte運算會提升為intSystem.out.println("(-127) + (-1) = " + sum); // 輸出: -128// 4. 驗證int的默認性long longNum = 10000000000L; // 必須加L,否則100億這個int字面量已超出int范圍,編譯報錯。System.out.println("Big Long Number: " + longNum);}
}
Byte Range: -128 to 127
-128 (byte) 的補碼: 10000000
(-127) + (-1) = -128
Big Long Number: 10000000000
1.6 整數的存儲范圍(記byte、short)
整數存儲:
數據類型 | 關鍵字 | 內存占用 | 取值范圍 | 備注 |
byte |
| 1字節 | -128 ~ 127 | 1. 范圍計算:
|
short |
| 2字節 | -32,768 ~ 32,767 | 1. 范圍計算: 。 |
int |
| 4字節 | -2,147,483,648 ~ 2,147,483,647 (約±21億) | 1. 默認類型:Java中整數字變量默認是 |
long |
| 8字節 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 1. 字面量:使用時需在數字后加 |
面試題:Q: byte b = 200;
這句代碼能否通過編譯?為什么?
A: 不能。編譯錯誤“不兼容的類型: 從int轉換到byte可能會有損失”。因為整數字面量 200
默認是 int
類型,而其值 200
超出了 byte
的范圍 (-128~127)。必須進行強制類型轉換:byte b = (byte) 200;
,但轉換后值會溢出,不再是200。
面試題:Integer.MIN_VALUE
的絕對值為什么還是負數?
A: 這是一個經典的溢出陷阱。Math.abs(Integer.MIN_VALUE)的結果仍是 Integer.MIN_VALUE。因為32位補碼能表示的最大正數是 2^31 - 1(即 Integer.MAX_VALUE),而 Integer.MIN_VALUE是 -2^31。它的絕對值 2^31超出了 int的正數表示范圍,導致運算溢出,結果又回到了負數區間。
Q: 基本類型 int
和包裝類 Integer
的區別?自動裝箱/拆箱的原理?
int
是基本數據類型,存儲的是值本身,存放在棧(局部變量)或堆(成員變量)中。
Integer
是對象,是 int
的包裝類,實例存儲在堆中。
自動裝箱(Autoboxing): Integer i = 10;
-> 編譯器自動轉換為 Integer i = Integer.valueOf(10);
自動拆箱(Unboxing): int n = i;
-> 編譯器自動轉換為 int n = i.intValue();
緩存機制: Integer.valueOf()
方法會緩存 -128 到 127 之間的對象。
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true, 因為取自緩存,是同一個對象Integer c = 128;
Integer d = 128;
System.out.println(c == d);// false, 超出緩存范圍,new了新對象
System.out.println(c.equals(d)); // true, 比較的是值
比較包裝類對象時,總是使用 .equals()
而不是 ==
。
自動拆箱、裝箱的原理與作用
1.7 小數存儲范圍(記結論)
結論:
1、4個字節float比8個字節long類型的存儲范圍大。
2、float和double都是浮點型,不精確。
3、double的精度大約是十進制科學計數法小數點后15-16位,float是7-8位。
浮點數:
1.8 char類型
char類型的字符底層也是用二進制表示。每一個char都對應一個整數值,然后底層存的是這個整數值的二進制。規定char對應整數值的表稱為字符集。
最早的字符集:ASCII碼字符集,最早規定127個字符。
'0' : 48 '1':49
‘A': 65 'B':66
'a': 97 'b':98
當計算機傳到歐洲的時候,超出了ASCII表的范圍,它們動起了心思,把ASCII碼表127之后的編碼值利用起來,但是它們無法通用,每個國家不同一樣。當計算機傳到亞洲的時候,ASCII完全不夠用,大陸地區最新用GB2312,現在改為GBK字符集。當一個程序或一個文檔中同時出現多個國家的文字,就麻煩了,必須出一個萬國碼,能同時表示全世界所有國家的常用文字。它就是unicode字符集。
Java目前在JVM中采用的就是unicode字符集。字符編碼的范圍是 0- 65535。編碼值沒有負數。
char
本質是整數,它可以和 int
進行各種運算。
制表位Tab:'\t'
刪除鍵Backspace:'\b'
回車鍵:'\r'
換行鍵:'\n'
單引號:'\''
雙引號:'\"'