Java 中的類型、值和變量 之 基本類型

衆所周知,Java是一門靜態類型的語言,這意味着全部的變量和表達式的類型會在編譯時肯定。同時,Java 仍是一門強類型的語言,所以變量的值或表達式的結果的類型都會受到限制(好比一個聲明爲 String 的變量不的值不多是一個數值 1),類型之間的運算也會被限制,這有助於在編譯時發現絕大多數的錯誤。html

Java中存在兩種類型:基本類型和引用類型。java

PrimitiveType:
    {Annotation} NumericType
    {Annotation} boolean
    
NumericType:
    IntegralType
    FloatingPointType
    
IntegralType:
    (one of)
    byte short int long char

FloatingPointType:
    (one of)
    float double
Java Language Specification (Java SE 8 Edition) §4.2 Primitive Types and Values

值得注意的是,基本類型的值的狀態不會被共享。oracle

好比下面這個例子:code

int i = 0;
int j = i;
i += 1;
System.out.println(j);

AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = i;
i.addAndGet(1);
System.out.println(j);

上述代碼將輸出:orm

0
1

整數類型和它們的值

整數類型 (IntegralType) 包含了如下五種類型:htm

類型 長度 / 位 (bit) 取值範圍
byte 有符號 8 -128 ~ 127
short 有符號 16 -32768 ~ 32767
int 有符號 32 -214783648 ~ 2147483647
long 有符號 64 -9223372036854775808 ~ 9223372036854775807
char 無符號 16 \u0000 ~ \uffff 等價於 0 ~ 65535

整數類型所支持的運算

  • 比較運算符: <<=>>===!= ,其結果爲 boolean 類型;
  • 數值運算符:對象

    • 一元運算符: +-
    • 乘法運算符: */%
    • 加法運算符: +-
    • 自增運算符: ++, 分爲前綴自增 (++i) 和後綴自增 (i++)
    • 自減運算符: --, 分爲前綴自減 (--i) 和後綴自減 (i--)
    • 位移運算符:內存

      • 左移運算符: <<
      • 有符號右移: >>
      • 無符號右移: >>>
    • 按位互補運算符: ~
    • 整數按位運算符: &^|
  • 條件運算符: ? :
  • 類型轉換運算符: cast
  • 字符串拼接運算符: +

這裏面加號出現了好幾回,包括 一元運算符加法運算符自增運算符字符串拼接運算符,後三者運算符就如它們的字面意思般,很好理解。ci

那麼 一元運算符 是什麼意思呢?字符串

讓咱們來看看下面這份代碼:

static void is (short i) {
    System.out.println("short");
}

static void is (int i) {
    System.out.println("int");
}

static void is (long i) {
    System.out.println("long");
}

static void main (String[] args) {
    short i = 5;
    int j = 10;
    is(i);
    is(+i);
    is(-i);
    is(j);
    is(+j);
    is(-j);
}

上述代碼將輸出:

short
int
int
int
int
int

很顯然,第 17~19 行的調用執行的是參數類型爲 int 的方法,然而第 20~21 行的調用執行的並非參數類型爲 long 的方法。

我在 JSL § 15.15.3 Unary Plus Operator + 中並未看出一元運算符的具體影響,根據實驗結果只能推測一元運算符會將低於 int 的數值類型提高到 int 類型 (你能夠聲明一個 byte h = 0is(+h) 仍然會調用參數類型爲 int 的方法),並且對於 +- 都是提高類型的做用,並非直覺意義上的一個升一個降。

關於這個 一元運算符 的用法其實並非不少,有下列幾種:

// 若是方法接受的是 int 類型的話,僅僅用來明確這是個正數仍是負數。
func(+1);
func(-1);

// 輸出字符的代碼值時的小技巧
// 由於字符類型 char 是低於 int 的整數類型
System.out.println(+'c'); // 99

整數運算的溢出和可能引起的異常

對於移位運算符之外的整數運算而言,若是兩個操做數中至少有一個 long ,那麼此次運算將會按 64位精度進行計算,而且其計算結果也是 long 類型,此時若是另外一個操做數不是 long,那麼會將它提高到 long 類型再計算;若是兩個操做數都不是 long,那麼會按 32位精度進行計算,而且計算結果爲 int,若是任何一個操做數不是 int, 那麼都將提高到 int 類型後再計算。

整數運算符會在如下狀況拋出異常:

  • 涉及到對引用類型拆箱時,若是是空引用,那麼會拋出 NullPointerException
  • 若是右操做數爲零,那麼整數除法運算符和整數取餘運算符都會拋出 ArithmeticException
  • 在自增和自減的時候,若是涉及到拆箱裝箱且內存不足,會拋出 OutOfMemoryError

來看看規範中給出的示例代碼:

class Test {
    public static void main (String[] args) {
        int i = 1000000;
        System.out.println(i * i);
        long l = i;
        System.out.println(l * l);
        System.out.println(20296 / (l - i));
    }
}

上述代碼將輸出:

-727379968
1000000000000
ArithmeticException

對於 int 來講,1000000^2 太大了,而因爲以前的運算規則,i * i 只能保存結果的低32位,十進制下也就是 -727379968

浮點類型和它們的值

Java 中的浮點類型遵循 IEEE 754 標準的定義。

IEEE 754-1985 - Wikiwand

IEEE 754_百度百科

IEEE 754 標準中,定義了 32位精度的 float、64位精度的 double,還有正負數、正負0、正負無窮和特殊的 NaN

NaN 用於表示無效操做的結果,好比 0.0 / 0.0 (0 / 0 才適用整數運算中的 右操做數爲0 的異常規則),你能夠在 Float.NaNDouble.NaN 中找到。

NaN 是無序的,所以

  • 若是一次運算中一個或兩個操做數都是 NaN,則比較運算符 (<<=>>=) 都會返回 false
  • 若是操做數是 NaN,則相等運算符 (==) 返回 false

    若是 xyNaN,則 (x < y) == !(x >= y) 將返回 false

  • 若是任一操做數是 NaN, 則不等式運算符 != 將返回 true

    當且僅當 xNaN 時,x != x 將返回 true

浮點類型所支持的運算

  • 比較運算符: <<=>>===!= ,其結果爲 boolean 類型;
  • 數值運算符:

    • 一元運算符: +-
    • 乘法運算符: */%
    • 加法運算符: +-
    • 自增運算符: ++, 分爲前綴自增 (++i) 和後綴自增 (i++)
    • 自減運算符: --, 分爲前綴自減 (--i) 和後綴自減 (i--)
  • 條件運算符: ? :
  • 類型轉換運算符: cast
  • 字符串拼接運算符: +

若是一次計算中,至少有一個二元運算符的操做數是浮點類型的,那麼該操做就是一個浮點運算,即便另外一個操做數是整數。

System.out.println(10 * 0.1); // 1.0

對於浮點運算而言,若是兩個操做數中至少有一個 double,那麼此次運算將會按 64位精度進行計算,而且其計算結果也是 double 類型,此時若是另外一個操做數不是 double,那麼會將它提高到 double 類型再計算;若是兩個操做數都不是 double,那麼會按 32位精度進行計算,而且計算結果爲 float,若是任何一個操做數不是 float, 那麼都將提高到 float 類型後再計算。

浮點運算的溢出和可能引起的異常

浮點運算有溢出 (overflows) 和下溢 (underflows),其中溢出將產生有符號的無窮大,而下溢則產生一個非標準化 (denormalized) 的值或是一個有符號的0。

數學上沒法肯定結果的浮點運算將產生 NaN

全部 NaN 參與的浮點運算都會產生 NaN

在下列狀況中,浮點運算會拋出異常:

  • 計算時須要拆箱,而又是個空引用時,會拋出 NullPointerException
  • 自增自減的狀況下,若是須要拆箱裝箱且內存不夠時,會拋出 OutOfMemoryError

接下來看看規範中給出的示例代碼:

class Test {
    public static void main(String[] args) {
        // 溢出
        double d = 1e308;
        System.out.print("溢出產生了無窮大: ");
        System.out.println(d + "*10==" + d*10);
        // 漸變下溢 (gradual underflow)
        d = 1e-305 * Math.PI;
        System.out.print("漸變下溢: " + d + "\n   ");
        for (int i = 0; i < 4; i++)
            System.out.print(" " + (d /= 100000) + "\n");
        System.out.println();
        // 產生 NaN
        System.out.print("0.0/0.0 產生的不是數字: ");
        d = 0.0/0.0;
        System.out.println(d);
        // 產生不精確結果的四捨五入:
        System.out.print("單精度下的不精確結果:");
        for (int i = 0; i < 100; i++) {
            float z = 1.0f / i;
            if (z * i != 1.0f)
                System.out.print(" " + i);
        }
        System.out.println();
        // 另外一個產生不精確結果的四捨五入:
        System.out.print("雙精度下的不精確結果:");
        for (int i = 0; i < 100; i++) {
            double z = 1.0 / i;
            if (z * i != 1.0)
                System.out.print(" " + i);
        }
        System.out.println();
        // 轉換到整數時發生的結果階段:
        System.out.print("強制轉換到整數: ");
        d = 12345.6;
        System.out.println((int)d + " " + (int)(-d));
    }
}

上述代碼將輸出

溢出產生了無窮大: 1.0e + 308 * 10 == Infinity
漸變下溢: 3.141592653589793E-305
 3.1415926535898E-310
 3.141592653E-315
 3.142E-320 0.0
 0.0 / 0.0 產生的不是數字:NaN
單精度下的不精確結果:0 41 47 55 61 82 83 94 97
雙精度下的不精確結果:0 49 98
強制轉換到整數:12345 -12345

值得注意的是,在 漸變下溢 的例子中,咱們能夠看到精度逐漸喪失。

布爾類型和它們的值

boolean 類型表示兩個邏輯量,truefalse

布爾運算符是:

  • 關係運算符 ==!=
  • 邏輯補足運算符 !
  • 邏輯運算符&^|
  • 條件運算符和條件運算符&&||
  • 條件運算符? :
  • 字符串鏈接運算符 + ,當給定一個String操做數和一個 boolean 操做數時,它將把 boolean 操做符轉換爲一個String"true""false" ),而後產生一個新建立的String,其值爲兩個字符串的鏈接結果。

布爾表達式決定了幾種語句中的控制流:

  • if 語句
  • while 語句
  • do 語句
  • for 語句

一個boolean表達式還決定在 ? : 運算符中使用哪一個子表達式的值做爲結果 。

只有Boolean表達式和Boolean表達式能夠在控制流程語句中使用。

經過表達式 x!=0 ,能夠將整數或浮點表達式 x 轉換爲 boolean 值,這遵循了 C 語言約定,即任何非零值爲true

經過表達式 obj!=null ,能夠將對象引用 obj 轉換爲boolean值,這一樣遵循了 C 語言約定(除null以外的任何引用爲true

參考資料: Java Language Specification (Java SE 8 Edition) § 4.2
相關文章
相關標籤/搜索