JAVA基础提高之位运算

180 阅读6分钟

: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源码的小伙伴,位运算基本知识是需要的。最后给大家推荐一个宝藏博主:[枣面包]