int轉byte數組以及相關原理

零、前言

本文由int轉byte數組這樣的題目代碼引發的思考,其中涉及到多個讓我混淆的地方。

直接上代碼

	 public byte[] toBytes(int number){
	        byte[] bytes = new byte[4];
	        bytes[3] = (byte)number;
	        bytes[2] = (byte) ((number >> 8) & 0xFF);
	        bytes[1] = (byte) ((number >> 16) & 0xFF);
	        bytes[0] = (byte) ((number >> 24) & 0xFF);
	        return bytes;
	 }

如果對這段代碼瞭然於胸,原理思路清洗,就可以不用閱讀本文了。如果對這段代碼的原理不是那麼清晰,那麼很可能是某個基礎知識模糊遺忘了。
我們知道int強轉成short,甚至char。但byte數組給人的感覺就是二進制的東西,好像和那些基本類型“不在一個維度上”,甚至想象不出轉換出來是個什麼效果,能打印出來嗎。。。之所以會有這些疑問,因爲我們對byte的一些基本概念混淆了,或許是從來沒真正的理解透過。

一、先思考幾個基礎問題

問題一:byte是java基本類型嗎,如果是,那它屬於哪一類基本類型

問題二:byte是計算機中最小的單位嗎,char呢? 能清晰的想出來java裏8種基本類型的字節長度以及取值長度嗎

問題三:我們知道java四個整數類型可以相互強轉,比如,int轉byte是怎麼做的

問題四:如何理解程序中的& 0xFF是否必要,可否去掉

問題五:把int切成四個byte,放在byte[]裏,順序怎麼擺放

問題六:我們應該學過“原碼”、“反碼”,“補碼”,那麼java的基礎數值類型在計算機裏是用的補碼嗎

以上這些問題先不要看答案,自己是否能很清晰的回答出來。


問題一答案

byte是java基本類型,而且是整數型(它不止可以用來表示二進制數據,還能表示數字哦)。
工作中我們會經常用byte數組,基本都是在涉及IO流傳輸數據相關代碼中使用。也許就是用的場景太單一順手了,byte給我們的感覺就是二級制的數據流,漸漸的就忽略了 這個類型居然和int之類 是一類東西,它的大小範圍是-128~127,是不是好像在哪聽過這個範圍。是short嗎,答案是否定的。這個範圍還出現在Integer使用地址比較相等的時候,因爲Integer會把-128-127範圍內的數字放入常量池,我們可以直接使用==來比較是否相等。

問題二答案

byte 名字節,大該是因爲他的英文名字聽起來好像叫“比特”,和bit很像,然後又經常被用在數據流中,給人感覺就好像這是一個不可分割的最小單位。但實際上byte佔8位,即 1byte = 8 bit。所以不是計算機中最小單位。
也許你在用數據庫的時候,使用過bit這個數據類型,它只有true和false兩個值(也就是1和0),這個單位我們可以稱之爲“位”,也就是我們所謂的最小單位。但不要和byte搞混了,他們之間差了8倍。 char就更大了,java中佔兩個字節,c語言中佔一個字節。
8種基本類型字節數以及取值範圍

類型 字節大小 取值範圍
byte 1 -(2^7) ~ (2^7) - 1
short 2 -(2^15) ~ (2^15) - 1
int 4 -(2^31) ~ (2^31) - 1
long 8 -(2^63) ~ (2^63) - 1
char 2 -
boolean 4或1 true,false
float 4 6位有效數字
double 8 15位有效數字

在這張表裏,你可能會對boolean這個類型產生疑問。對於本題答案,你甚至會想byte和boolean是不是有什麼關聯(你可能隱約感覺兩者都是用來表示0和1的)。 當然這是完全錯的,即使是bit和boolean也沒什麼聯繫,boolean在java中也不是隻佔1位。因爲java中並沒有規定boolean的表示方法,實際中boolean佔了4字節(和int一樣大),boolean[]中bolean元素則佔1字節,所以上面表格裏寫4或1。

問題三答案

首先注意這裏是說int強轉成byte,而不是本文所說的int轉byte[]。通過前一個問題我們知道int佔32位,而byte佔8位。比如int a10 = 261對應的二進制就是100000101【左邊剩下的23個0就省略了】,想轉成byte也很簡單,就是直接截取最右邊8位即可:00000101【十進制的5】,也就是261強轉byte後等於5。

問題四答案

十六進制0xFF 也就是二進制的11111111, &運算的作用就是把兩個二進制右邊對齊,然後逐個&運算,只有兩者都是1時結果纔是1,
等效於截取右邊8位二進制數。通過問題三,可知int強轉byte就是簡單截取右邊8位,沒有必要再& 0xFF。 所以上面的代碼可以這麼寫

	 public byte[] toBytes(int number){
	        byte[] bytes = new byte[4];
	        bytes[3] = (byte)number;
	        bytes[2] = (byte) (number >> 8);
	        bytes[1] = (byte) (number >> 16);
	        bytes[0] = (byte) (number >> 24);
	        return bytes;
	 }

問題五答案

通過理解這段代碼我們知道,int的32位二進制,最終被截斷成4段,然後把這4段以byte的形式放入byte數組。如下所示
在這裏插入圖片描述
也許會有疑問,爲什麼不是這樣
在這裏插入圖片描述
因爲在二進制截取、排序的過程中 不要帶入“意義”。也就是說,在排這些二進制段的時候不要想着這段二進制原本是什麼意思(比如這裏就不要想着這32位二進制是一個int數字),可以簡單認爲,這就是一串毫無意義字符串。而這串字符串就是從左往右讀的,左邊是開始,右邊是結束。
另一個更準確的理解方式是:通過一個實際的例子畫出圖就能很容易看出
在這裏插入圖片描述
如果把00000101放在b0中,那麼這個byte[]無論正着讀還是反着讀,二進制數字都是亂的。

問題六答案

首先給出答案:是的。 如果這個問題這麼問:計算機中是使用原碼還是補碼錶示數字,基本大家都會答:補碼。這個問題之所以這麼問,就是看概念是否清晰準確。如果關於補碼的概念模糊的話,甚至可能會覺得補碼應用於計算機內的所有信息,整數是不是用的補碼反而不確定了。 下面就複習一下關於補碼的知識:
上面說了,byte的取值範圍是-128~127,這個範圍我們應該不陌生,但爲什麼呢,爲什麼不是“兩頭對稱”的呢,這就是補碼造成的。
問題三答案,我們明白了int是如何轉成byte的,例子大家應該也都能看明白。現在換一個例子int a = -216,二進制爲1111111111111111111111111111111111111111111111111111111011111011,同樣截取後8位,結果是11111011,表示十進制的-5。如果對這個計算過程以及結果有點意外,說明對補碼認識不足。下面就以byte類型(8位二進制)爲例,講解一下補碼 首先說一下概念及結論:

  1. java中的數字都是有符號的,即有正有負
  2. 有符號數字表示的二進制中左邊第一位是符號位,0表示正數,1表示負數
  3. 原碼:8位有符號的二級制的原始表達方式
  4. 反碼:原碼符號位不變,其他位取反(就是0變1,1變0)
  5. 補碼:反碼+1
  6. 計算機中的數字使用補碼錶示
  7. 使用規則: 正數的原碼和補碼一樣(也就不存在反碼),負數的反碼根據上面的規則計算,即反碼+1

爲什麼產生反碼

  1. 原碼中的 0000000【+0】 1000000【-0】
    有正0和負0,這兩個本來一樣的數卻有兩個不同的表示方式
  2. 原碼的符號位不方便計算,而補碼卻能巧妙的把符號位帶着一起做加法運算(這個就不細講)。
    根據上面的規則,byte的256個數字對應關係如下
二進制表示 00000000 0000001 01111111
無符號十進制數字 0 1 127
補碼十進制數字 0 1 127

10000000 1000001 11111111
128 129 255
-128 -127 -1

函數圖如下
在這裏插入圖片描述

通過表格以及函數圖,很明顯看出byte的表示範圍是-128 ~ 127【y軸。雖然畫的是連續直線,其實是離散點】

二、總結

本程序簡單講就是:把32位的int像“切甘蔗”一樣平均切成4段byte,從左至右依次放入byte[]。 前提是要理解byte、int等基本類型的本質,以及補碼的作用及使用方式。懂了這些原理,不僅僅可以解決int轉byte數組,反過來轉換也是比較容易理解的。

發佈了19 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章