1:資料在計算機中是如何儲存的
二進位制,八進位制,十六進位制
二進位制是計算技術中廣泛採用的一種數制。所謂的二進位制就是指由 0,1 兩個數碼錶示的數。進位規則為: “逢二進一”。
計算機運算基礎採用的就是二進位制。原因在於:電子電晶體有兩種基本狀態,開和關,對應表示為 0 和 1。
常用的進位制還有 八進位制以及十六進位制,在電腦科學中,經常會用到 二進位制與十六進位制。
如何使用 Java 語言表示一個二進位制,八進位制與十六進位制的數呢?
二進位制表示:
int a = 0b11;
在數字前加上 0b 則表示一個二進位制數,該數字的十進位制表示為:3
八進位制表示:
int a = 011;
在數字前加上 0 則表示一個八進位制數,該數字的十進位制表示為:9
十六進位制表示:
int a = 0x11;
在數字前加上 0x 則表示一個十六進位制數,該數字的十六進位制表示為:17
同樣的,我們也可以使用程式將十進位制數字表示為二進位制,八進位制,十六進位制的形式
public class Main {
public static void main(String[] args) {
int a = 10;
System.out.println(Integer.toBinaryString(a)); // 轉換為二進位制的字串形式
System.out.println(Integer.toHexString(a)); // 轉換為十六進位制的字串形式
System.out.println(Integer.toOctalString(a)); // 轉換為八進位制的字串形式
}
}
位元組
位元組(Byte)是計算機資訊科技用於計量儲存容量的一種計量單位,一個位元組儲存 8 位無符號二進位制數字1 Byte = 8 bit
ASCII 碼
在計算機中,所有的資料在儲存和運算時都要使用二進位制數來表示。
例如數字 32 的 十六進位制表示為:20;二進位制表示為:0010 0000
但是,除了數字以外,還有字母,空格等特殊符號需要被表示出來,而具體使用哪些二進位制數來表示哪些符號,就要制定出一套編碼規範為了大家相互通訊時不造成混亂,那麼大家必須使用這套編碼規則。於是美國相關的標準化組織就推出了 ASCII 編碼,既定了一套規範,到目前為止一共定義了 128 個字元。
在 Mac 或 Linux 作業系統的終端,我們可以輸入命令:
man ascii
來檢視 ASCII 碼錶
比較常見的有:
65 表示為 A
97 表示為 a
2:基本資料型別
Java 中有兩種資料型別
原生(基本)資料型別
引用資料型別
Java 的基本資料型別有八種
byte
short
int
long
float
double
char
boolean
基本資料型別的宣告
public class Main {
public static void main(String[] args) {
byte b = 1;
short s = 1;
int i = 1;
long l = 1L; // 一般我們會使用大寫的 L 作為字尾表示,因為小寫的 l 不易區分
float f = 0.1f;
double d = 0.1;
char c = '1';
boolean flag = true;
}
}
Java 語言中也提供了幾種利於表示數字的表示機制,比如我們可以使用 “_” 來分割數字,這樣我們就可以清晰地知道一個大數是多少
public class Main {
public static void main(String[] args) {
int a = 10_0000_0000; // 我們可以很清晰地知道 該數字為 10 億
}
}
同時,也有科學記數法表示:
public class Main {
public static void main(String[] args) {
int a = (int) 1e8; // 該數字為 1 億
}
}
各個基本型別的儲存範圍
它們表示的儲存範圍,我們可以通過各個基本型別對應的包裝類的 MIN_VALUE 和 MAX_VALUE 得知:
public class Main {
public static void main(String[] args) {
System.out.println("byte 儲存範圍:" +Byte.MIN_VALUE+" ~ " +Byte.MAX_VALUE);
System.out.println("short 儲存範圍:" +Short.MIN_VALUE+" ~ " +Short.MAX_VALUE);
System.out.println("int 儲存範圍:" +Integer.MIN_VALUE+" ~ " +Integer.MAX_VALUE);
System.out.println("long 儲存範圍:" +Long.MIN_VALUE+" ~ " +Long.MAX_VALUE);
System.out.println("float 儲存範圍:" +Float.MIN_VALUE+" ~ " +Float.MAX_VALUE);
System.out.println("double 儲存範圍:" +Double.MIN_VALUE+" ~ " +Double.MAX_VALUE);
System.out.println("char 能夠儲存一個字元,消耗兩個位元組的空間");
System.out.println("boolean 只能表示 true 和 false");
}
}
程式輸出結果為:
byte 儲存範圍:-128 ~ 127
short 儲存範圍:-32768 ~ 32767
int 儲存範圍:-2147483648 ~ 2147483647
long 儲存範圍:-9223372036854775808 ~ 9223372036854775807
float 儲存範圍:1.4E-45 ~ 3.4028235E38
double 儲存範圍:4.9E-324 ~ 1.7976931348623157E308
char 能夠儲存一個字元,消耗兩個位元組的空間
boolean 只能表示 true 和 false
如果我們宣告的變數數值大小超出了宣告它的資料型別範圍,就會出現溢位。
關於浮點數
來看這個程式:
public class Main {
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
}
}
該程式在我的主機輸出的結果為:
0.30000000000000004
出現這個結果的原因是因為,計算機中浮點數的儲存為近似值。反應到真實專案中,我們要知道,金額是絕對不可以使用浮點數來表示的,因為會出現精度丟失。
一般情況,金額的計算我們會使用 BigDecimal 型別,如程式碼所示:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("0.1"); // 一定要使用字串,或這種形式:BigDecimal.valueOf(0.1);
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));
}
}
程式輸出結果:
0.3
3:型別轉換與型別提升
型別轉換
型別轉換原則:
低精度可以直接轉換為高精度
高精度可以強制轉換成低精度,但是要自己承擔溢位的風險
來看示例:
public class Main {
public static void main(String[] args) {
byte a = 1;
int b = a; // 低精度可以直接轉換為高精度
int c = 1;
byte d = (byte) c; // 高精度強制轉換為低精度,但是要承擔溢位風險
}
}
型別提升
什麼是型別提升?當不同精度的資料進行計算的時候,得到的結果的型別會提升到參與計算的資料裡面最高的精度
示例程式:
public class Main {
public static void main(String[] args) {
short a = 1;
int b = 1;
double c = 0.1;
double d = a + b + c; // short,int,double 這幾種型別中, double的精度最高,結果的資料型別即 double
}
}
我們知道,Java 語言中的除法為 “地板除”,譬如:3 / 2
,該運算結果為:1
我們如果想獲得帶小數結果,可以使用型別轉換與型別提升兩種方法:
型別轉換:
public static double divide(int a, int b) {
int res = a / b;
return (double) res;
}
型別提升:
public static double divide(int a, int b) {
return (double) a / b; // 先將 a 強制轉換為 double 型別,然後用 double 型別數字與int型別數字計算,結果為 double
}
4:基本資料型別對應的裝箱型別
基本資料型別與對應的裝箱型別:
基本資料型別 | 裝箱型別 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
為什麼要有裝箱型別?
我們知道 Java 中只有兩種資料型別,第一種是基本資料型別,第二種是引用型別;引用型別宣告的是一個物件,儲存在堆記憶體中。
裝箱的目的:
裝箱將一個基本資料型別包裝成一個物件,一個類的物件就可以有很多可以呼叫的方法,便於我們進行操作
泛型不支援基本型別,例如:
List<Integer> list = new ArrayList<>();
裝箱型別可以指明為 null,我們可以這樣宣告:
Integer i = null;
,反之,基本型別是不具有這個特點的
自動裝箱與自動拆箱
從 JDK1.5 開始,Java 語言提供了自動裝拆箱的機制
public class Main {
public static void main(String[] args) {
int a = 1;
Integer integer = a; // 自動裝箱
int b = a; // 自動拆箱
}
}
如程式所示,自動裝箱就是自動將基本資料型別轉換為包裝型別;自動拆箱就是自動將包裝型別轉換為基本資料型別。
5:== 與 equals 約定在資料型別中的應用
“==” 和 equals 有什麼區別?
“==” 是判斷兩個變數或例項指向的是否是同一塊記憶體空間,也就是說兩個變數或例項是否相同
equals 則是判斷兩個變數或例項指向的記憶體空間的值是否相等,這裡面“相等”的規則是由自己定義的
用一張圖可以簡要說明 “==” 和 equals 的區別:
我們來看一個示例並進行說明
程式一:
public class Main {
public static void main(String[] args) {
int a = 1000;
int b = 1000;
System.out.println(a == b);
}
}
該程式輸出的結果為 true
程式二:
public class Main {
public static void main(String[] args) {
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
}
}
該程式輸出的結果為 false
導致兩個程式輸出結果不同原因是 Integer 是裝箱型別,Integer a
和 Integer b
指向的是堆中兩塊不同的記憶體。
我們再來看兩個程式:
程式一:
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 1;
System.out.println(a == b);
}
}
程式二:
public class Main {
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a == b);
}
}
程式一的輸出結果為:
true
程式二的輸出結果為:
false
為什麼會有這樣的差異呢?
原因在於,當我們使用 Integer a = 1;
的方式宣告一個變數時,Java 實際上會呼叫 Integer.valueOf()
這個方法。
我們來看下 Integer.valueOf()
這個方法的原始碼:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
JDK 文件中說明:
- This method will always cache values in the range -128 to 127,
- inclusive, and may cache other values outside of this range.
也就是說由於 -128 ~ 127 這個區間的值非常常用, Java 為了減少申請記憶體的開銷使用了 IntegerCache(Integer 常量池)將這些物件儲存在常量池中,所以如果使用 Integer 宣告的值在 -128 ~ 127 這個區間內的話,就會直接從常量池中取出並返回,於是我們看到程式一輸出的結果為 true。
而使用Integer a = new Integer(1);
這種方式宣告,則一定會在堆中開闢一塊新的記憶體儲存物件,所以程式二的輸出結果為 false。
6:陣列型別
陣列型別是一個非常特殊的型別
陣列型別由 JDK 專門使用虛擬機器的一些指令來建立
陣列的宣告:
X[] x = new X[10]
X[] x = new X[]{...}
X[] x = {...}
陣列的主要特性:
長度不可變
型別安全
只有一個 length 屬性
可以使用 foreach 迴圈迭代
本作品採用《CC 協議》,轉載必須註明作者和本文連結