3.Java資料型別

jrh_2333發表於2021-06-20

1:資料在計算機中是如何儲存的

二進位制,八進位制,十六進位制

二進位制是計算技術中廣泛採用的一種數制。所謂的二進位制就是指由 01 兩個數碼錶示的數。進位規則為: “逢二進一”。

計算機運算基礎採用的就是二進位制。原因在於:電子電晶體有兩種基本狀態,開和關,對應表示為 01

常用的進位制還有 八進位制以及十六進位制,在電腦科學中,經常會用到 二進位制與十六進位制。

如何使用 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 個字元。

MacLinux 作業系統的終端,我們可以輸入命令:

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_VALUEMAX_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 只能表示 truefalse

如果我們宣告的變數數值大小超出了宣告它的資料型別範圍,就會出現溢位。

關於浮點數

來看這個程式:

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 中只有兩種資料型別,第一種是基本資料型別,第二種是引用型別;引用型別宣告的是一個物件,儲存在堆記憶體中。

裝箱的目的:

  1. 裝箱將一個基本資料型別包裝成一個物件,一個類的物件就可以有很多可以呼叫的方法,便於我們進行操作

  2. 泛型不支援基本型別,例如:List<Integer> list = new ArrayList<>();

  3. 裝箱型別可以指明為 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 aInteger 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 為了減少申請記憶體的開銷使用了 IntegerCacheInteger 常量池)將這些物件儲存在常量池中,所以如果使用 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 協議》,轉載必須註明作者和本文連結

相關文章