--- title: '資料類型、編碼、變數、參考類型' disqus: kyleAlien --- 資料類型、編碼、變數、參考類型 === ## Overview of Content 如有引用參考請詳註出處,感謝 :smile_cat: :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**深入探索 Java 基礎類型、編碼、浮點數、參考類型和變數作用域 | 探討細節**](https://devtechascendancy.com/basic-types_encoding_reference_variables-scopes/) ::: [TOC] ## Java 基礎類型 Java 語言將資料類型分為 **基本類型**、**參考類型**,如下表展示… 並本篇會依據幾個特別、大家容易稿混的類型進行說明 1. **基本類型** 如下表 | 基本類型 | 補充 | | - | - | | boolean | - | | float | 數值類型(浮點數) | | double | 數值類型(浮點數) | | byte | 數值類型(整數類) | | short | 數值類型(整數類) | | int | 數值類型(整數類) | | long | 數值類型(整數類) | | char | 數值類型(字符類) | :::info * Java 編譯器會將 Java 編譯為 Bytecode 時,會用 **int、byte 來表示 boolean** ::: 2. **參考類型** 如下表 | 參考類型 | 補充 | | - | - | | class | 類別 | | interface | 界面 | | **array** | 陣列 | ## Java 編碼 ### Java 字元編碼、常見的編碼:Unicode 產生 * char 是字元類型,**Java 大多採用 `Unicode` 字元編碼** > `Unicde` 編碼也就是 **`UTF-16BE`** :::success * **字元編碼** 是什麼概念? 用一串特定的二進位碼來標示特定得字元,就像是一個 Mapping 表單一樣 ::: * 常見的 **字元編碼** 如下表 | 字元編碼 | 字元、字節數 | 簡單介紹 | | - | - |- | | `ASCII` | 7 bit | **當今最通用單字節碼**,美國資訊互換標準程式碼,是為 **羅馬字母編制的編碼**,主要用於表達現代英語、西歐語言 | | `ISO-8859-1`(`Latin-1`) | 8 bit | 國際標準化組織 (IOS) 為 **西歐語言** 的字元碼制定的編碼,與 ASCII 相容 | | `GB2312` | 2 byte | 簡中編碼,與 `ASCII` 相容 | | `GBK` | 2 byte | 漢字編碼,與 `GB2312` 相容 | | `Unicode`, `UCS` | 2 or 4 byte (後面說明) | 收入全世界所有語言的編碼,`UCS` (`Universl Character Set`) 是指採用 `Unicode` 字元編碼的通用字元集,單位為碼元(`Code point`) | | `UTF8`、`UTF16` | 1~16 byte | **將 `Unicode` 轉換為作業系統支援的編碼!** 單位為碼點(`Code unit`) | :::info * **`Unicode` 分為 2 種字元編碼** * 使用 2byte 編碼:又稱為 `UCS-2`,也是 **Java 採用的位元組編碼方案** * 使用 4byte 編碼:又稱為 `UCS-4 ` * `Java` 語言如果要使用 `Unicode` 則需要 **使用 `\u` 開頭標注** ::: * Unicode 產生是因為,原本在 1990 年左右有兩個團隊(`ISO/IEC`、`Unicode Consortium`)都打算統一字元集合… 其思想很簡單,單純的使用 Mapping 的方式,將碼點(`Code point`)對應上碼元(`Code unit`) > `ISO/IEC` 搶先在 1990 年發布第一套字元集編碼 `UCS-2`,這種編碼的碼元就是兩個 Byte 大小 ```mermaid graph LR 人類文字的字元 <--> cp(碼點, Code point) 編碼 <--> cu(碼元, Code unit) cp <-.->|一一對應| cu ``` 然而兩個團隊很快意到一個世界不需要兩種統一編碼,最後決定合併,才發佈了 `Unicode 1.0`(也就是 `UCS-2`),然而人類的字元統計過後漸漸超過 2 Byte,後來才出現 `UCS-4` 編碼 :::info * UTF-16 可以說是容納 `UCS-2`、`UCS-4` 編碼 UTF-16 會依照字元在哪個碼點(`Code point`)去對應到 `UCS-2`、`UCS-4` 編碼 * 像是 Java、JavaScript 兩種語言,在計算字元長度時就是使用 UTF-16 的「碼元」作為長度計算 ::: ### 手動運算 Unicode 編碼轉 UTF8 * **`UTF8` 就是以 1 個 Byte 為單位對 `UCS`(Unicode) 編碼**,使用遮罩的方式將 `UCS` 編碼資料填入 `UTF8` 編碼;範例如下… 我們將 Unicode 編碼轉 UTF8 | `USC-2` 編碼範圍(16 進位) | `UTF8` 遮罩(2 進位) | | - | - | | 0000-007F | `0xxx` `xxxx` | | 0080-07FF | `110x` `xxxx` `10xx` `xxxx` | | 0800-FFFF | `1110` `xxxx` `10xx` `xxxx` `10xx` `xxxx` | * **「漢」`USC-2` 轉「漢」`UTF8`** 1. `Unicode` 編碼為 16 進位資料 `0x6C49` 2. `0x6C49` 至於 `USC-2` 編碼範圍 `0800-FFFF` 3. 將 「漢」的 `0x6C49` 轉為 2 進位 `0110 110001 001001` 4. 將其套上 `UTF8` 遮罩就變為 `E6 B1 89` | 「漢」`Unicode` 編碼 | 「漢」2 進位 | 「漢」`USC-2` 編碼區間 | 「漢」`UTF8` 遮罩 | 套上遮罩後 | | - | - | - | - | - | | `0x6C49` | `0110 110001 001001` | `0800-FFFF` | `1110` `xxxx` `10xx` `xxxx` `10xx` `xxxx` | `1110` `0110` `1011` `0001` `1000` `1001` | ## Java 浮點數類型 Java 的浮點數類型有兩種,分別是 `float`、`double` 類型,這裡我們就要說明浮點數的計算方式、浮點數誤差、Java 提供的特殊數字 ### Java 計算浮點數的方式:浮點數規則 * Java 語言的 `float`、`double` 類型 **遵循 `IEEE754` 標準**,該標準分別為 32、64 位元浮點數規定二進位資料表示形式,主要由 3 個元素組成,如下表 | 元素 | 說明 | bit 數量 | | - | - | - | | S | 符號位元,表示正負 | 1 | | E | 指數 | 8 | | M | 尾數 | 23 | ```java= // 32 bit 的 float 的二進位表示,透過固定規則就可以轉換為浮點數 SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMM ``` * float 類型的二進位資料 **儲存規則,會牽涉到 `隱含位元`、`階碼`** * **隱含位元**:尾數 `M` 之前有一個 隱含位元,隱含位元為 `1.` or `0.` 兩種,而使用哪個取決於指數 `M`、`E` | 元素 `M` 值 | 元素 `E` 值 | 隱含位元 | | - | - | - | | 非 0 | `1 ~ 254` | `1.` | | 非 0 | 其他 | `0.` | * **階碼**:階碼採用移碼表示,對應表如下 | 元素 `M` 值 | 元素 `E` 值 | 階碼 | | - | - | - | | 非 0 | `1 ~ 254` | `E-127` | | 非 0 | `0` | `-126` | * **換算格式**:**(-1)^S^ * 2^階碼^ * (隱含位元)**;範例如下 * **`5.0f` 的轉換** * 二進位: `0 10000001 01000000000000000000000` > 查表可對應到:隱含位元 `1.`、階碼為 `E-127` * 隱含位元:由於 `E` 為 `10000001` 也就是 129,在 `1 ~ 254` 之間,所以隱含位元為 `1.` * 套上公式計算:(-1)^0^ * 2^129-127^ * `1.01000000000000000000000`,也就是 2^2^ * `1.01000000000000000000000` 透過位元移動 2 位,所以最終就是 `101.000000000000000000000` 也就是 5(解答!) ### 浮點數誤差 * 由上 Float 浮點數計算規則可知,**尾數數值越多,精度越高**,而 **double 精度又比 float 高**,測試範例如下 * **float 精度測試** ```java= public void testFloat() { float res = 0, testTime = 26; while (testTime > 0) { res += 0.1f; testTime--; } System.out.println("float add 26 times: " + res); } ``` > ![](https://hackmd.io/_uploads/BJ2gvYTY3.png) * **double 精度測試** ```java= public void testDouble() { double res = 0, testTime = 26; while (testTime > 0) { res += 0.1f; testTime--; } System.out.println("double add 26 times: " + res); } ``` > ![](https://hackmd.io/_uploads/By2bPY6Fh.png) :::success * 在某些場合會特別注重浮點數的誤差(像是銀行的業務),如果要表示任意精度的資料,可以使用 **`BigDecimal` 類** ::: ### Java 提供的特殊數字 * 在浮點數計算中,如果 **科符記號 `E` 為 255**,那就用來表示一些 **特殊數字**,如下表 | 特殊數字 | 二進位 | | - | - | | `Float.NaN` 非數字 | `0111 1111 1100 0000 0000 0000 0000 0000` | | `Float.POSITIVE_INFINITY` 正無窮大 | `0111 1111 1000 0000 0000 0000 0000 0000` | | `Float.NEGATIVE_INFINITY` 負無窮大 | `1111 1111 1000 0000 0000 0000 0000 0000` | ```java= public void testSpecialNum() { float nan = (float) (0.0 / 0.0); System.out.println(nan); float pos = (float) (1.0 / 0.0); System.out.println(pos); float neg = (float) (-1.0 / 0.0); System.out.println(neg); } ``` > ![](https://hackmd.io/_uploads/rJNTjKTK2.png) :::info * **計算、程式中,科學記號的差異** 差異在 **底數**,**程式中,科學記號 E 的底數為 10**,這是為了符合一般人的計算標準;而在 **電腦中則是使用 2 進位作為科學記號 E 的底數**(方便電腦識別) ::: ## 參考類型 Java 是物件導向語言,隱藏了物件在 JVM 內的真實地址,使用 **參考類型 (++引用的概念++)** > 像是 C 語言,就把變數地址暴露給開發者使用 而 Java 的參考類型有 3 個 * **類別**(`Class`)參考 * **界面**(`Interface`)參考 * **陣列參考**(在 Java 中,陣列也被視為物件) :::info * **類別類型、類別參考類型** ```java= class Hello { public static void main(String[] args) { // Hello hello,中的 Hello 是類別「參考」類型 // new Hello(),中的 Hello 則是「類別類型」 Hello hello = new Hello(); } } ``` ::: ### 基本類型、參考類型 * **基本類型**:也就是 Java 的基礎八大類型(布林、整數、浮點數...),**在記憶體中佔有實體位置,數據多大就佔多少空間位置** * **參考類型**:參考類型包裝多個基礎類型(也可以包裝其他參考類型),**在記憶體中佔固定大小位置(指針),指針指向 JVM 堆區中的實體位置** > 參考類型須使用 `new` 關鍵字創建 ```java= // 參考類型 class Hello { // 基本類型 int foo = 100; long bar = 30000L; // 參考類型 int[] fooArray = {10, 20, 30}; String object = "HelloWorld"; public static void main(String[] args) { Hello hello = new Hello(); } } ``` > 下圖中,類別「參考類型」在 JVM 模型中的位置,類別「參考類型」是放在方法區的 對象(物件)類型 > > ![](https://hackmd.io/_uploads/HkWGg6RYn.png) ### 物件的預設參考:this > 「物件」說的是實體資料(`instance`),反觀「類」則是物件的模板 * 當物件建立好後,JVM 會分類一個參考自身物件得引用 `this`,透過 `this` 可以傳遞、訪問當前物件(instance) :::warning * 物件建立完成後才會建立出 `this` 引用,如果在物件尚未建立完成時去使用子類,則很可能導致無法取得當前引用 ::: ```java= class Hello { Hello() { // 將自身引用傳入到令一個物件,令一個物件就可以調用該 instance new World().startHello(this); } String getMsg() { return "Hello"; } } class World { public void startHello(Hello hello) { System.out.println(hello.getMsg()); } } ``` ## 變數作用域 * 變數的作用域是指 **它的存在範圍**,不在範圍內部可訪問 * 變數存活的生命週期,不同描述的變數會有不同的生命週期限制,一般變數生命週期都至於花括號 `{ }` 之中 ### 一般變數、靜態變數 * **一般變數**:一般變數區域會 **存在 JVM 堆區、棧區**,常見如下表 | 變數區域 | 區域 | 生命週期 | | - | - | - | | 全域變數 (堆區) | 這裡的全域,是指 Java 類中的全域,作用域是整個類中 | 與物件同進退 | | 區域變數 | 只存在花括號之間的變數,外部不可使用 | 只存在 `{}` 中 | | 方法參數 | 作用域是方法開始、到結束 | 只存在 `{}` 中 | | 例外處理參數 | `catch` 區塊內的變數 | 只存在 `{}` 中 | ```java= class Hello { // 全域變數 msg String msg = "My message"; void myFunction(int value) { // 方法參數 value // 區域變數 showValue int showValue = value * value; try { // 區域變數 str String[] str = msg.split(" "); // 區域變數 firstStr String firstStr = str[0]; System.out.println("Show value " + showValue + ", msg: " + firstStr); } catch (Exception e) { // 例外處理參數 e e.printStackTrace(); } } } ``` :::warning * **當基礎類型、引用類型傳入方法時,是不同的狀態**! * 基礎類型傳入方法時,是使用 **複製數值** 的方式來將參數傳入,修改方法內的參數,並不會影響外部變數 * 引用類型傳入方法時,是直接將 **引用傳入**,所以在內部可以直接修改引用的物件的成員 ::: * **靜態變數**:靜態變數 **存在 JVM 的方法區之中**,**在方法區中的變數,會被所有的類共同使用**,不需要建立物件實體(instance) ```java= class Hello { static String HELLO_WORLD = "Hello World"; public static void main(String[] args) { Hello.HELLO_WORLD = "This is main"; } } ``` ### 變數的初始化、預設值 * **區域變數是沒有初始化數值的!必須手動初始化** * 而 Java 會對類的全域變數給予初始化值,如下表 | 類型 | 預設初始化值 | | - | - | | `byte`、`short`、`int`、`long` | 0 | | `float` | 0.0f | | `double` | 0.0d | | `char` | `\u0000` | | `boolean` | false | | 參考類型(物件) | null | | 參考陣列(物件) | 預設也為 null,在初始化過後,如果仍沒有給元素賦值,那就使用該元素預設的初始化值 | ## 更多的 Java 語言相關文章 ### Java 語言深入 * 在這個系列中,我們深入探討了 Java 語言的各個方面,從基礎類型到異常處理,從運算子到物件創建與引用細節。點擊連結了解更多! :::info * [**深入探索 Java 基礎類型、編碼、浮點數、參考類型和變數作用域 | 探討細節**](https://devtechascendancy.com/basic-types_encoding_reference_variables-scopes/) * [**深入了解 Java 應用與編譯:從原始檔到命令產出 JavaDoc 文件 | JDK 結構**](https://devtechascendancy.com/java-compilation_jdk_javadoc_jar_guide/) * [**深入理解 Java 異常處理:從基礎概念到最佳實踐指南**](https://devtechascendancy.com/java-jvm-exception-handling-guide/) * [**深入理解 Java 運算子與修飾符 | 重要概念、細節 | equals 比較**](https://devtechascendancy.com/java-operators-modifiers-key-concepts_equals/) * [**深入探索 Java 物件創建與引用細節:Clone 和finalize 特性,以及強、軟、弱、虛引用**](https://devtechascendancy.com/java-object-creation_jvm-reference-details/) ::: ### Java IO 相關文章 * 探索 Java IO 的奧秘,了解檔案操作、流處理、NIO等精彩內容! :::warning * [**Java File 操作指南:基礎屬性判斷、資料夾和檔案的創建、以及簡單示範**](https://devtechascendancy.com/java-file-operations-guide/) * [**深入探索 Java 編碼知識**](https://devtechascendancy.com/basic-types_encoding_reference_variables-scopes/) * [**深入理解 Java IO 操作:徹底了解流、讀寫、序列化與技巧**](https://devtechascendancy.com/deep-in-java-io-operations_stream-io/) * [**深入理解 Java NIO:緩衝、通道與編碼 | Buffer、Channel、Charset**](https://devtechascendancy.com/deep-dive-into-java-nio_buf-channel-charset/) ::: ### 深入 Java 物件導向 * 探索 Java 物件導向的奧妙,掌握介面、抽象類、繼承等重要概念! :::danger * [**深入比較介面與抽象類:從多個角度剖析**](https://devtechascendancy.com/comparing-interfaces-abstract-classes/) * [**深度探究物件導向:繼承的利與弊 | Java、Kotlin 為例 | 最佳實踐 | 內部類細節**](https://devtechascendancy.com/deep-dive-into-oop-inheritance/) * [**「類」的生命週期、ClassLoader 加載 | JVM 與 Class | Java 為例**](https://devtechascendancy.com/class-lifecycle_classloader-exploration_jvm/) ::: ## Appendix & FAQ :::info ::: ###### tags: `Java 基礎`