---
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);
}
```
> 
* **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);
}
```
> 
:::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);
}
```
> 
:::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 模型中的位置,類別「參考類型」是放在方法區的 對象(物件)類型
>
> 
### 物件的預設參考: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 基礎`