JAVA基礎提高之位運算

海賊王1999 發表於 2020-10-26

:JAVA位運算在閱讀JDK原始碼時候常常會遇到,因此學習位運算相關知識,是非常有必要的,在學習之前,首先需要了解一下二進位制碼相關知識,這裡給大家提供幾篇相關文章:《原始碼,補碼,反碼》

首先,大家都知道,JAVA定義的位運算子可以應用於整數型別(int)長整型(long)短整型(short)字元型(char),和位元組型別(byte)等型別。

Java七種位運算子:

位運算子說明
>>右移運算子,符號左側數值 按位右移 符號右側數值指定的位數,若為正數則高位補0,若為負數則高位補1
<<左移運算子,符號左側數值 按位左移 符號右側數值指定的位數,並在低位處補0
>>>無符號右移運算子,符號左側數值 按位右移 符號右側數值指定的位數,無論正負高位補0
&(AND)運算子,對兩個整型運算元中對應位執行布林代數,兩個位都為1時輸出1,否則0
|(OR)運算子,對兩個整型運算元中對應位執行布林代數,兩個位中只要有一個為1就輸出1,否則為0
^異或(XOR)運算子,對兩個整型運算元中對應位執行布林代數,兩個位相等則為0,不相等則為1
~(NOT)運算子,按位取反運算子翻轉運算元的每一位,即0變成1,1變成0

七種位運算子使用例項程式碼:

/**
 * @Auther: csp1999
 * @Date: 2020/10/26/11:33
 * @Description: 位運算操作符
 */
public class bit_operators {

    @Test
    public void test01() {
        // int max = Integer.MAX_VALUE;
        // int min = Integer.MIN_VALUE;
        int num1 = -20;
        int num2 = 30;
        int bit_operator_1 = num1 << 1;// 正數左移n位擴大2^(n)倍,負數左移n位縮小2^(n)倍
        int bit_operator_2 = num1 >> 1;// 正數右移n位縮小2^(n)倍,負數右移n位擴大2^(n)倍
        int bit_operator_3 = num1 >>> 1;// 無符號右移 切記要明白計算機儲存二進位制都是以補碼的形式儲存
        int bit_operator_4 = num1 & num2;
        int bit_operator_5 = num1 | num2;
        int bit_operator_6 = num1 ^ num2;
        int bit_operator_7 = ~num1;

        // System.out.println(max);
        // System.out.println(min);
        // System.out.println("0"+Integer.toBinaryString(Integer.MAX_VALUE));
        // System.out.println(Integer.toBinaryString(20));
        System.out.println(bit_operator_1);
        System.out.println(bit_operator_2);
        System.out.println(bit_operator_3);
        System.out.println(bit_operator_4);
        System.out.println(bit_operator_5);
        System.out.println(bit_operator_6);
        System.out.println(bit_operator_7);
    }
}

運算結果如下:

-4
-1
2147483647
2
-2
-4
1

原理解析(高位0為手動補齊,方便觀看)

a = 11111111 11111111 11111111 11101100
b = 00000000 00000000 00000000 00011110
------------------------------------------------
a << 1	-->	11111111 11111111 11111111 11011000
a >> 1	-->	11111111 11111111 11111111 11110110
a >>> 1	-->	01111111 11111111 11111111 11110110	
a & b 	= 	00000000 00000000 00000000 00001100
a | b 	= 	11111111 11111111 11111111 11111110
a ^ b 	= 	11111111 11111111 11111111 11110010
~a		= 	00000000 00000000 00000000 00010011

進行位操作時,除long型外,其他型別會自動轉成int型,轉換之後,可接受右運算元長度為32。進行位運算時,總是先將短整型和位元組型值轉換成整型值再進行移位操作的。

資料型別大小
byte8 bit
short16 bit
char16 bit
int32 bit
long64bit

示例:

byte num1 = -128;
byte num2 = 63;
byte bit_operator_1  = (byte)(a << 1);
byte bit_operator_2  = (byte)(a >> 1);
byte bit_operator_3  = (byte)(a >>> 1);
byte bit_operator_4  = (byte)(a & b);
byte bit_operator_5  = (byte)(a | b);
byte bit_operator_6  = (byte)(a ^ b);
byte bit_operator_7  = (byte)(~ a);

​ 上面的程式碼在位運算後型別自動提升為了int,所以需要使用int型別的變數來接受,但是我們可以在進行位運算後進行強轉,但強轉會直接擷取位元組,從而導致丟失精度,最終得到的結果如下:

0
-64
-64
0
-65
-65
127

​ 對於 int 型別的整數移位 num1 >> num2, 當 b>32 時,系統先用 num2 對 32 求餘(因為 int 是 32 位),得到的結果才是真正移位的位數,例如,num1 >> 33 和 num1 >> 1 的結果相同,而 num1>> 32 = num1;

位運算的使用場景如下:

1.判斷奇偶

@Test
public void test02() {
    Integer num = 123;
    // 判斷奇偶普通方式一:
    if (num % 2 == 0) {
        System.out.println("偶數");
    } else {
        System.out.println("奇數");
    }
    // 判斷奇偶位運算方式二:
    if ((num & 1) == 0) {
        System.out.println("偶數");
    } else if ((num & 1) == 1) {
        System.out.println("奇數");
    }
}

偶數的最低位肯定是0,奇數的最低位肯定是1,而1的最低位是1其他位都為零,當進行與運算時:

  • 偶數必然:a&1 == 0
  • 奇數必然:a&1 == 1

2. 不使用中間變數交換兩個數

@Test
public void test03() {
    int a = 10;
    int b = 20;
    // 1.依靠中間變數交換2個數值:
    int c;
    c = a;
    a = b;
    b = c;
    System.out.println("a=" + a + ",b=" + b);
    // 2. 位運算不使用中間變數交換2個數:
    a = 10;
    b = 20;
    a = a ^ b;
    b = b ^ a;
    a = a ^ b;
    System.out.println("a=" + a + ",b=" + b);
}

這裡需要知道兩點:

  1. 任何數和自己進行異或操作結果都為0
  2. 異或符合交換律,即a ^ b = b ^ a

好的,那麼上面程式碼操作就等於:

a = a ^ b;
b = b ^ a = b ^ (a ^ b) = a;
a = a ^ b = (a ^ b) ^ (b ^ (a ^ b)) = (a ^ b) ^ a = b;

3. 判斷一個正整數是不是2的整數次冪

public boolean power(int num) {
    if (num <= 0) {
        System.out.println("這裡不計算負數,直接返回false");
        return false;
    } else {
        return (num & (num - 1)) == 0;
    }
}
@Test
public void test04() {
    int num = 1024;
    System.out.println(power(num));
}

任何正整數如果是2的冪數,都形如下:

10
100
1000
10...0
1234

即首位都為1,往後位數都為0,那麼在減去1後又都形如下:

01
011
0111
01...1
1234

所以大於零的2的冪數和自己減一後的數進行與運算結果必然為0

4. 對稱加密

就是使用一次異或加密,使用兩次異或解密:

@Test
public void test5(){
    String a = "sadfsdfsdfhfghf123dfgfg";
    System.out.println(a);
    int key = 324545231;
    byte[] bytes = a.getBytes();
    for (int i = 0; i < bytes.length-1; i++) {
        bytes[i] = (byte)(bytes[i] ^ key);
    }
    String b = new String(bytes);
    System.out.println(b);

    for (int i = 0; i < bytes.length-1; i++) {
        bytes[i] = (byte)(bytes[i] ^ key);
    }
    String c = new String(bytes);
    System.out.println(c);
}

列印結果:

sadfsdfsdfhfghf123dfgfg
����������������������g
sadfsdfsdfhfghf123dfgfg

以上是JAVA位運算的基本例項和知識點,同樣位運算也廣泛應用於JDK原始碼中,對於初次學習JDK原始碼的小夥伴,位運算基本知識是需要的。最後給大家推薦一個寶藏博主:[棗麵包]