關於 Java 中的強制型別轉換

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

問題描述

遇到一個題目:

經過強制型別轉換以後,變數a,b的值分別為多少?
short a = 128;
byte b = (byte) a;
a = ?, b = ?

答案是:a = 128, b = -128

這涉及到 Java primitive type 的 conversion,打算藉此稍稍研究一下。

分析過程

下面分析中會涉及到一些與題目無關的細節,想直接看題目解答的請跳到 “題目中的數值在記憶體中的表示”

明確 Java 中的 Conversion

Java 的 conversion 有多種,這裡我們只討論 primitive conversion。

primitive conversion 分為兩種,一是 Widening Primitive Conversion(擴充套件型基本資料型別轉換),二是 Narrowing Primitive Conversion(窄化型基本資料型別轉換)。

ps: 翻譯成中文還是感覺怪怪的,下面還是用英文表示吧。

1. Widening Primitive Conversion

JLS 定義了 19 種 widening pc,簡單來說,就是位數低的向高的轉換,如下

  • byte to short, int, long, float, or double
  • short to int, long, float, or double
  • char to int, long, float, or double
  • int to long, float, or double
  • long to float or double
  • float to double

widening pc 不會丟失數值的整體大小資訊

2. Narrowing Primitive Conversion

這個問題涉及到的 int -> short 和 short -> byte 的轉換就包含在 JLS 定義的 22 種 narrowing pc 之中。

  • short to byte or char
  • char to byte or short
  • int to byte, short, or char
  • long to byte, short, char, or int
  • float to byte, short, char, int, or long
  • double to byte, short, char, int, long, or float

需要注意的是 narrowing pc 是有可能丟失數值的整體資訊以及損失精度和範圍的。

可能有人會注意到,上面的 widening pc 和 narrowing pc 都沒有包含 byte -> char 的轉換

Chapter 5. Conversions and Promotions

The following conversion combines both widening and narrowing primitive conversions:
byte to char
First, the byte is converted to an int via widening primitive conversion (§5.1.2), and then the resulting int is converted to a char by narrowing primitive conversion (§5.1.3).

這是因為這是一種特殊地、同時結合了 widening pc 和 narrowing pc 的轉換,byte 會先轉換成 int(widening pc),然後將這個 int 結果轉換成 char(narrowing pc)。

具體分析

那麼我們還是回到這個問題

short a = 128;
byte b = (byte) a;

首先,在 Java 中,整數預設為 int 型別,也就是說,在 short a = 128; 中,會發生 int -> short 的 narrowing pc,是有可能損失精度的,由於 int 是高位(32位),short 是低位(16位),所以在轉換時會 truncate。同樣,對於 byte b = (byte) a; 也有可能因為 truncate 而損失精度。

先回顧原碼、反碼、補碼的概念

在 Java 中數值是用補碼錶示的,在這裡回顧一下原碼、反碼、補碼的概念(以 3 為例吧):

  1. 原碼(第一個為符號位,1 表示負數,0 表示正數)
  • 3 的原碼:0000 0011
  • -3 的原碼:1000 0011
  1. 反碼(就是反過來,注意符號位不變):
  • 3 的反碼:1111 1100
  • -3 的反碼:1111 1100
  1. 補碼(正數的補碼 = 原碼,負數的補碼 = 反碼 1)
  • 3 的補碼:0000 0011
  • -3 的補碼:1111 1101

題目中的數值在記憶體中的表示

二進位制表示:

int a = 128 00000000 00000000 10000000 00000000

short a = 128 00000000 10000000 (強轉後前面 16 位被截斷)

可以看出來,a 的值輸出應該還是 128

那麼對於 byte b = (byte) a;

二進位制表示:

short a = 128 00000000 10000000

byte b = 128 10000000 (強轉後前面 8 位被截斷)

但是,輸出的 b 的值為什麼不是 128 而是 -128 呢

Primitive Data Types (The Java™ Tutorials > Learning the Java Language > Language Basics)

byte: The byte data type is an 8-bit signed two’s complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable’s range is limited can serve as a form of documentation.

這是因為 byte 是一個 8 位有符號二進位制補碼整數,它包含的值的範圍是 [-128, 127],因此 byte 型別的值是無法表示 128 的

那麼在發生截斷後,1000 0000 表示的就是一個補碼,從這個補碼可以看出來它的原碼肯定是一個負數,那麼我們根據這個補碼反推得到它的反碼:1111 1111,從而得到它的原碼:1000 0000,可以看出這個值就是 -0,但是在我們的生活中是沒有 -0 的,所以在計算機中就把它識別成 -128,因此這就是為什麼 b 的值的輸出是 -128 的原因。

ps: 關於 -0 對應於 -128 的具體解釋,我懶得寫了,因為感覺取決於個人的理解,不想細究,如果有人還有疑問,可以看看這個人的解釋 byte型別取值範圍為什麼是127到-128? – 知乎

那麼到這裡,問題就基本解釋清楚了,感覺自己還是很囉嗦,不過其實是為了回顧一些基礎的知識,也希望對其他人有所幫助啦~

相關文章

開發語言 最新文章