深入探索 Java 基礎類型、編碼、浮點數、參考類型和變數作用域 | 探討細節

深入探索 Java 基礎類型、編碼、浮點數、參考類型和變數作用域 | 探討細節

Overview of Content

本文將帶您深入研究 Java 的基礎類型、編碼、浮點數、參考類型和變數作用域

我們將從 Java 字元編碼、常見編碼和手動運算 Unicode 編碼轉 UTF8 開始,深入瞭解 Java 編碼的概念。接著,我們將探討 Java 浮點數的計算方式、浮點數誤差和 Java 提供的特殊數字

然後,我們會討論參考類型,包括基本類型和參考類型,以及物件的預設參考 this

最後,我們將探討變數作用域,包括一般變數和靜態變數,以及變數的初始化和預設值。無論您是初學者還是有經驗的開發者,本文都將為您提供深入且全面的Java知識,助您更好地理解和應用基礎概念。

寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀


Java 基礎類型

Java 語言將資料類型分為 基本類型參考類型,如下表展示… 並本篇會依據幾個特別、大家容易稿混的類型進行說明

A. 基本類型 如下表

基本類型補充
boolean-
float數值類型(浮點數)
double數值類型(浮點數)
byte數值類型(整數類)
short數值類型(整數類)
int數值類型(整數類)
long數值類型(整數類)
char數值類型(字符類)

● Java 編譯器會將 Java 編譯為 Bytecode 時,會用 int、byte 來表示 boolean

B. 參考類型 如下表

參考類型稱呼
class類別
interface界面
array陣列

Java 編碼

Java 字元編碼、常見的編碼:Unicode 產生

● char 是字元類型,Java 大多採用 Unicode 字元編碼

Unicde 編碼也就是 UTF-16BE

字元編碼 是什麼概念?

用一串特定的二進位碼來標示特定得字元,就像是一個 Mapping 表單一樣

● 常見的 字元編碼 如下表

字元編碼字元、字節數簡單介紹
ASCII7 bit當今最通用單字節碼,美國資訊互換標準程式碼,是為 羅馬字母編制的編碼,主要用於表達現代英語、西歐語言
ISO-8859-1Latin-18 bit國際標準化組織 (IOS) 為 西歐語言 的字元碼制定的編碼,與 ASCII 相容
GB23122 byte簡中編碼,與 ASCII 相容
GBK2 byte漢字編碼,與 GB2312 相容
Unicode, UCS2 or 4 byte (後面說明)收入全世界所有語言的編碼,UCS (Universl Character Set) 是指採用 Unicode 字元編碼的通用字元集,單位為碼元(Code point
UTF8UTF161~16 byte將 Unicode 轉換為「作業系統」支援的編碼!單位為碼點(Code unit

Unicode 分為 2 種字元編碼

● 使用 2byte 編碼:又稱為 UCS-2,也是 Java 採用的位元組編碼方案

● 使用 4byte 編碼:又稱為 UCS-4

Java 語言如果要使用 Unicode 則需要 使用 \u 開頭標注

● Unicode 產生是因為,原本在 1990 年左右有兩個團隊(ISO/IECUnicode Consortium)都打算統一字元集合… 其思想很簡單,單純的使用 Mapping 的方式,將碼點(Code point)對應上碼元(Code unit

ISO/IEC 搶先在 1990 年發布第一套字元集編碼 UCS-2,這種編碼的碼元就是兩個 Byte 大小

graph LR 人類文字的字元 <--> cp(碼點, Code point) 編碼 <--> cu(碼元, Code unit) cp <-.->|一一對應| cu

然而兩個團隊很快意到一個世界不需要兩種統一編碼,最後決定合併,才發佈了 Unicode 1.0(也就是 UCS-2),然而人類的字元統計過後漸漸超過 2 Byte,後來才出現 UCS-4 編碼

● UTF-16 可以說是容納 UCS-2UCS-4 編碼

UTF-16 會依照字元在哪個碼點(Code point)去對應到 UCS-2UCS-4 編碼

● 像是 Java、JavaScript 兩種語言,在計算字元長度時就是使用 UTF-16 的「碼元」作為長度計算

手動運算 Unicode 編碼轉 UTF8

UTF8 就是以 1 個 Byte 為單位對 UCS(Unicode) 編碼,使用遮罩的方式將 UCS 編碼資料填入 UTF8 編碼;範例如下… 我們將 Unicode 編碼轉 UTF8

USC-2 編碼範圍(16 進位)UTF8 遮罩(2 進位)
0000-007F0xxx xxxx
0080-07FF110x xxxx 10xx xxxx
0800-FFFF1110 xxxx 10xx xxxx 10xx xxxx

「漢」USC-2 轉「漢」UTF8

A. Unicode 編碼為 16 進位資料 0x6C49

B. 對照上表找到編碼範圍:0x6C49 至於 USC-2 編碼範圍 0800-FFFF

C. 將 「」的 0x6C49 轉為 2 進位 0110 110001 001001

D. 將其套上 UTF8 遮罩就變為 E6 B1 89 ,也就是「漢」這個字使用在 UTF8 上表示時的編碼

「漢」Unicode 編碼「漢」2 進位「漢」USC-2 編碼區間「漢」UTF8 遮罩套上遮罩後
0x6C490110 110001 0010010800-FFFF1110 xxxx 10xx xxxx 10xx xxxx1110 0110 1011 0001 1000 1001

Java 浮點數類型

Java 的浮點數類型有兩種,分別是 floatdouble 類型,這裡我們就要說明浮點數的計算方式、浮點數誤差、Java 提供的特殊數字

Java 計算浮點數的方式:浮點數規則

● Java 語言的 floatdouble 類型 遵循 IEEE754 標準,該標準分別為 32、64 位元浮點數規定二進位資料表示形式,主要由 3 個元素組成,如下表

元素說明bit 數量(加起來總共 32 bit)
S符號位元,表示正負1
E指數8
M尾數23

// 32 bit 的 float 的二進位表示,透過固定規則就可以轉換為浮點數

SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMM

● float 類型的二進位資料 儲存規則,會牽涉到 隱含位元階碼

隱含位元:尾數 M 之前有一個 隱含位元,隱含位元為 1. or 0. 兩種,而使用哪個取決於指數 ME

元素 M元素 E隱含位元
非 01 ~ 2541.
非 0其他0.

階碼:階碼採用移碼表示,對應表如下

元素 M元素 E階碼
非 01 ~ 254E-127
非 00-126

換算格式(-1)S * 2階碼 * (隱含位元);範例如下

5.0f 的轉換

● 二進位: 0 10000001 01000000000000000000000

查表可對應到:隱含位元 1.、階碼為 E-127

● 隱含位元:由於 E10000001 也就是 129,在 1 ~ 254 之間,所以隱含位元為 1.

● 套上公式計算:(-1)0 * 2129-127 * 1.01000000000000000000000,也就是 22 * 1.01000000000000000000000

透過位元移動 2 位,所以最終就是 101.000000000000000000000 也就是 5(解答!)

浮點數誤差

● 由上 Float 浮點數計算規則可知,尾數數值越多,精度越高,而 double 精度又比 float 高,測試範例如下

float 精度測試


public void testFloat() {
    float res = 0, testTime = 26;

    while (testTime > 0) {
        res += 0.1f;
        testTime--;
    }

    System.out.println("float add 26 times: " + res);
}

double 精度測試


public void testDouble() {
    double res = 0, testTime = 26;

    while (testTime > 0) {
        res += 0.1f;
        testTime--;
    }

    System.out.println("double add 26 times: " + res);
}

● 在某些場合會特別注重浮點數的誤差(像是銀行的業務),如果要表示任意精度的資料,可以使用 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

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);

}

計算、程式中,科學記號的差異

差異在 底數程式中,科學記號 E 的底數為 10,這是為了符合一般人的計算標準;而在 電腦中則是使用 2 進位作為科學記號 E 的底數(方便電腦識別)


Java 參考類型

Java 是物件導向語言,隱藏了物件在 JVM 內的真實地址,使用 參考類型 (引用的概念)

像是 C 語言,就把變數地址暴露給開發者使用

而 Java 的參考類型有 3 個

類別Class)參考

界面Interface)參考

陣列參考(在 Java 中,陣列也被視為物件)

類別類型、類別參考類型


class Hello {
    
    public static void main(String[] args) {
        // Hello hello,中的 Hello 是類別「參考」類型
        // new Hello(),中的 Hello 則是「類別類型」
        Hello hello = new Hello();
    }
    
}

基本類型、參考類型

基本類型:也就是 Java 的基礎八大類型(布林、整數、浮點數...),在記憶體中佔有實體位置,數據多大就佔多少空間位置

參考類型:參考類型包裝多個基礎類型(也可以包裝其他參考類型),在記憶體中佔固定大小位置(指針),指針指向 JVM 堆區中的實體位置


// 參考類型
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();
    }

}

參考類型須使用 new 關鍵字創建

下圖中,類別「參考類型」在 JVM 模型中的位置,類別「參考類型」是放在方法區的 對象(物件)類型

物件的預設參考:this

「物件」說的是實體資料(instance),反觀「類」則是物件的模板

● 當物件建立好後,JVM 會分類一個參考自身物件得引用 this,透過 this 可以傳遞、訪問當前物件(instance)

● 物件建立完成後才會建立出 this 引用,如果在物件尚未建立完成時去使用子類,則很可能導致無法取得當前引用


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 區塊內的變數只存在 {}

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();
        }

    }

}

當基礎類型、引用類型傳入方法時,是不同的狀態

● 基礎類型傳入方法時,是使用 複製數值 的方式來將參數傳入,修改方法內的參數,並不會影響外部變數

● 引用類型傳入方法時,是直接將 引用傳入,所以在內部可以直接修改引用的物件的成員

靜態變數:靜態變數 存在 JVM 的方法區之中在方法區中的變數,會被所有的類共同使用,不需要建立物件實體(instance)


class Hello {

    static String HELLO_WORLD = "Hello World";

    public static void main(String[] args) {
        Hello.HELLO_WORLD = "This is main";
    }

}

變數的初始化、預設值

區域變數是沒有初始化數值的!必須手動初始化

● 而 Java 會對類的全域變數給予初始化值,如下表

類型預設初始化值
byteshortintlong0
float0.0f
double0.0d
char\u0000
booleanfalse
參考類型(物件)null
參考陣列(物件)預設也為 null,在初始化過後,如果仍沒有給元素賦值,那就使用該元素預設的初始化值

更多的 Java 語言相關文章

Java 語言深入

● 在這個系列中,我們全方位地探討了 Java 語言的各個核心主題,旨在幫助你徹底掌握這門強大的編程語言。無論你是想深入理解 Java 的基礎類型與變數作用域,還是探索異常處理與運算子的細節,這些文章都將為您提供寶貴的知識

深入 Java 物件導向

● 探索 Java 物件導向的奧妙,掌握介面、抽象類、繼承等重要概念!幫助你針對物件導向設計有更深入的了解!


Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響