測試格式
前言 以前瞭解過位運算符,左移<<
等於乘以2,右移>>
等於除以2。可是我在看jdk源碼的時候發現了一個>>>
三個符號的,不明白這是什麼意思,就去搜了一下,發現還挺多的知識點的,就整理了一下。 首先咱們知道,咱們編寫的程序最終都是在計算機底層進行的,計算機底層也僅支持0、1兩種符號。因此當時網上有個鍵盤只有0、1兩個鍵,那纔是大佬用的鍵盤。扯遠了。。。 先來複習一下java的基本類型都佔多少字節,佔多少位(1字節等於8位): | 類型 | 字節數 | 位數 | 大小範圍 | | ------- | ------ | ---- | ---------------------------- | | byte | 1 | 8 | -2^8^~2^8^-1 | | short | 2 | 16 | -2^16^~2^16^-1 | | int | 4 | 32 | -2^32^~2^32^-1 | | long | 8 | 64 | -2^64^~2^64^-1 | | float | 4 | | | | double | 8 | | | | char | 2 | 16 | 一個char類型能夠存儲一個漢字 | | boolean | 1 | | true or false | 移位操做是把數據看做二進制數,而後將其向左或向右移動若干位的運算。在Java中,移位操做符包含三種:<<
左移運算符,>>
帶符號右移運算符,>>>
無符號右移運算符。這三種操做符都只能做用於long
,int
,short
,byte
這四種基本整形類型上和char
類型上。其餘類型如double都沒法使用位運算符,你們能夠在ide中自行試驗一下。 在java中,第一位用來表示數字的正負,第一位爲零時表示正數,第一位爲1時表示負數。咱們拿最簡單的8位byte類型舉例:0000 0000
表示0,0111 1111
這個表示最大值(2^8^-1),再進行加一後就變成了1000 0000
這時就變成了最小值(-2^8^)。再加一後變成1000 0001
這時的值爲-127。也就是從0到最大值而後轉爲最小值,而後再從最小值向零靠近。 # 左移操做符<<
左移操做符<<
是將數據轉換爲二進制後,向左移動若干位,高位丟棄,低位補零。 首先咱們能夠利用java中的方法獲取一個數的二進制:Integer.toBinaryString(int val)
。 而後咱們看下面這個例子: java public static void main(String[] args) { int a = 10; System.out.println("左移前的二進制:"+Integer.toBinaryString(a)); a <<= 2; System.out.println("左移後的二進制:"+Integer.toBinaryString(a)); System.out.println("左移後的十進制:"+a); }
首先定義一個數,值爲10,打印它的二進制(1010),而後進行左移操做2位。打印移位後的結果和二進制。 java 左移前的二進制:1010 左移後的二進制:101000 左移後的十進制:40
能夠看出,將原來的二進制向左移動了兩位,後面進行了補零。40=10 * 2 * 2。因此一次左移等於將這個數擴大了兩倍。再來看一個負數的左移: java int b = -8; System.out.println("左移前的二進制:" + Integer.toBinaryString(b)); b <<= 2; System.out.println("左移後的二進制:" + Integer.toBinaryString(b)); System.out.println("左移後的十進制:" + b);
咱們定義了一個負數(-8),打印出它的二進制,進行左移2位,左移後打印它的二進制,再將10進制打印出來查看。 java 左移前的二進制:11111111111111111111111111111000 左移後的二進制:11111111111111111111111111100000 左移後的十進制:-32
能夠明顯的看出二進制向左移動了兩位,前面的位置丟棄,後面的位置補零。轉換爲10進制也符合咱們以前的運算:-32 = -8 * 2 *2。 # 帶符號右移操做符>>
剛纔的左移中,它向左移動,高位進行了丟棄,低位進行補零。可是右移操做時有一個符號位,操做不當將形成答案與預期結果不一樣。 帶符號右移就是在向右移動若干位,低位進行丟棄,高位按照符號位進行填補。對於正數作右移操做時,高位補充0
;負數進行右移時,高位補充1
。 再來用例子證實一下: java public static void main(String[] args) { int a = 1024; System.out.println("a右移前的二進制:" + Integer.toBinaryString(a)); a >>= 4; System.out.println("a右移後的二進制:" + Integer.toBinaryString(a)); System.out.println("a右移後的十進制:"+a); int b = -70336; System.out.println("b右移前的二進制:" + Integer.toBinaryString(b)); b >>= 4; System.out.println("b右移後的二進制:" + Integer.toBinaryString(b)); System.out.println("b右移後的十進制:"+b); }
定義了兩個變量,a=1024,而後向右移動4位。b=-70336也向右移動4位。分別將它們的移動先後二進制和十進制打印出來查看。 java a右移前的二進制:10000000000 a右移後的二進制:1000000 a右移後的十進制:64 b右移前的二進制:11111111111111101110110101000000 b右移後的二進制:11111111111111111110111011010100 b右移後的十進制:-4396
a原來的二進制向右移動後,低位被丟棄,高位補充符號位也就是0。b原來的二進制向右移動後,低位被丟棄,高位補充符號位1。這也符號咱們以前的運算規律: 1024 / 2^4^ =16 ;-70336/ 2^4^ = -4396。 # 無符號右移操做符>>>
剛纔的帶符號右移操做符,咱們在向右移動時帶着高位的符號,正數填充0,負數填充0。如今不帶符號的右移操做符大致與右移操做符一致,只不過再也不區分正負數,結果都是高位補零,低位丟棄。 再用例子來證實一下: java public static void main(String[] args) { int a = 1024; System.out.println("a右移前的二進制:" + Integer.toBinaryString(a)); a >>>= 4; System.out.println("a右移後的二進制:" + Integer.toBinaryString(a)); System.out.println("a右移後的十進制:"+a); int b = -70336; System.out.println("b右移前的二進制:" + Integer.toBinaryString(b)); b >>>= 4; System.out.println("b右移後的二進制:" + Integer.toBinaryString(b)); System.out.println("b右移後的十進制:"+b); }
仍是剛纔帶符號右移的例子:此次咱們僅僅把操做符換成無符號的右移操做符。 按照定義,其實在正數時不會有變化,由於在帶符號的右移中正數也是高位補零。只不過當值爲負數時會有變化,讓咱們看一下輸出是否是符合猜測。 java a右移前的二進制:10000000000 a右移後的二進制:1000000 a右移後的十進制:64 b右移前的二進制:11111111111111101110110101000000 b右移後的二進制:1111111111111110111011010100 b右移後的十進制:268431060
確實正數沒有變化,驗證了咱們的猜測。而後是負數,此次向右移動時高位進行了補零,低位丟棄。改變後的數值再也不符合咱們以前的規律。 在無符號右移中,當值爲正數時,依然符合以前的規律移動一位至關於除以2。可是當值爲負數時再也不符合規律。 # 當移位的位數超過數值所佔用的位數會怎麼樣? 這個問題頗有意思,咱們剛剛都僅僅移動了2位或者4位,若是咱們超過了int的位數也就是32位後會怎麼樣?咱們若是對一個正數左移32位,低位補零補充了32次就變成0了,就如同下面代碼所寫的同樣,最終a的結果會是什麼。會變成0嗎? java public static void main(String[] args) { int a = 10; a <<= 32; System.out.println(a); }
通過咱們運行後發現a的結果最終沒變仍是10。咱們若是改爲左移33位,它的結果會變成20
。那麼它的運算規律會不會是當超過位數後僅僅移動對位數的餘數呢?好比對int作操做,它實際是運算 位數%32
次。 通過屢次試驗發現答案確實就是這個猜測,當對int類型處理時,右移x
位的運算爲x%32
位。 # 對其餘類型也是同樣嗎? 咱們剛纔都是用的int類型,那麼對於byte
,short
,char
,long
都同樣嗎? 先看一下byte類型。 java public static void main(String[] args) { byte b = -1; System.out.println("操做前:"+b); b >>>= 6; System.out.println("操做後:"+b); }
定義了byte的值爲-1,即1111 1111
,而後無符號右移6位,高位補零,低位丟棄,那麼應該變成0000 0011
也就是是3。讓咱們運行一下這段代碼看一下打印出來的信息是否是3呢? java 操做前:-1 操做後:-1
運行結果與咱們預期的結果不一致! 咱們將它的二進制也一塊兒打印出來看一下究竟: java public static void main(String[] args) { byte b = -1; System.out.println("操做前十進制:"+b); System.out.println("操做前二進制:"+Integer.toBinaryString(b)); b >>>= 6; System.out.println("操做後二進制:"+Integer.toBinaryString(b)); System.out.println("操做後十進制:"+b); }
這時再看一下運行結果 java 操做前十進制:-1 操做前二進制:11111111111111111111111111111111 操做後二進制:11111111111111111111111111111111 操做後十進制:-1
原來,Java在對byte
,short
,char
這三種類型進行移位操做前,會將其先轉型爲int
類型,而後再進行位操做。因爲咱們有進行了從新賦值將其賦值爲原來的byte
類型,因此又進行了從int
到byte
的先下轉型,也就是截斷。咱們對上面的例子進行一下修改能夠更直觀的發現運行過程: java public static void main(String[] args) { byte b = -1; System.out.println("操做前十進制:"+b); System.out.println("操做前二進制:"+Integer.toBinaryString(b)); System.out.println("進行無符號右移6位後的十進制:"+(b>>>6)); System.out.println("操做後二進制:"+Integer.toBinaryString(b>>>6)); }
在這裏我沒有使用=
進行從新賦值,而是計算完成後直接打印十進制和二進制的結果。 java 操做前十進制:-1 操做前二進制:11111111111111111111111111111111 進行無符號右移6位後的十進制:67108863 操做後二進制:11111111111111111111111111
從打印結果中能夠明顯的看出是先轉換爲int類型,而後進行位運算,位運算結束後因爲從新賦值因此進行的截斷。 對於long
類型,它是64位,不用先轉換。 # 總結 移位符是Java中的基本操做符,實際支持的類型只有int
和long
。在對byte
,short
,char
類型進行移位操做時,都會先將其轉換爲int
類型再進行操做。左移<<
操做符至關於乘以2。帶符號右移操做符>>
至關於除以2。在Java中使用位運算符會比乘*
,除/
運算符更高效一些。而無符號右移符>>>
在移動時高位補零,低位丟棄,在正數時仍然至關於除以2,可是在負數時結果倒是變大了(由負數變爲正數)。 > 本文由博客一文多發平臺 OpenWrite 發佈! 博主郵箱:liunaijie1996@163.com,有問題能夠郵箱交流。
歡迎關注本站公眾號,獲取更多信息