java位运算

Java位运算符
位运算符用来对二进制位进行操作,Java中提供了如下所示的位运算符:
位运算符:

(>>,<<,>>>,&,|,^,~),

位运算符中,除~以外,其余均为二元运算符。操作数只能为整型和字符型数据。

基础知识
补码:
所有的整数类型(除了char类型之外)都是有符号的整数。这意味着他们既能表示正数,又能表示负数。Java使用补码来表示二进制数,在补码表示中,最高位为符号位,正数的符号位为0,负数为1。补码的规定如下:
对正数来说,最高位为0,其余各位代表数值本身(以二进制表示),如+42的补码为00101010。
对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。如-42的补码为11010110(00101010按位取反11010101+1=11010110)
用补码来表示数,0的补码是唯一的,都为00000000。(而在原码,反码表示中,+0和-0的表示是不唯一的,可参见相应的书籍)。而且可以用111111表示-1的补码(这也是补码与原码和反码的区别)。

【对于有符号的数,二进制的最高位时符号位:0表示正数,1表示负数
正数的原码,反码,补码都一样
负数的反码=它的原码符号位不变,其它位取反
负数的补码=它的反码+1
0的反码,补码都是0
java中的数都是有符号的】

下面我们来看一个例子:

public class BinaryTest {
 
    public static void main(String[] args) {
 
        int init = -10;
        int initPositive = 10 - 1;
 
        // 记录负的int值的长度
        final int theLength = Integer.toBinaryString(init).length();
        System.out.println("-10的二进制表示长度是:" + theLength);
        for (int i = 0; i < 10; i++) {
            System.out.println(init + "的二进制表示:" + Integer.toBinaryString(init));
            // 临时字符串,保存整数int值的二进制表现形式
            String temp = Integer.toBinaryString(initPositive);
            // 需要补的位数
            int complement = theLength - temp.length();
            StringBuffer complementStr = new StringBuffer();
            for (int j = 0; j < complement; j++) {
                // 用“0”补足
                complementStr.append("0");
            }
            System.out.println(initPositive + "的二进制表示:    " + complementStr
                    + temp);
            init++;
            initPositive--;
        }
 
        System.out.println("---------------------");
        String longBinaryStr = Long.toBinaryString(-5L);
        System.out.println("long类型(-5L):" + longBinaryStr);
        System.out.println("长度是:" + longBinaryStr.length());
    }
}

运行结果

-10的二进制表示长度是:32
-10的二进制表示:11111111111111111111111111110110
9的二进制表示:    00000000000000000000000000001001
-9的二进制表示:11111111111111111111111111110111
8的二进制表示:    00000000000000000000000000001000
-8的二进制表示:11111111111111111111111111111000
7的二进制表示:    00000000000000000000000000000111
-7的二进制表示:11111111111111111111111111111001
6的二进制表示:    00000000000000000000000000000110
-6的二进制表示:11111111111111111111111111111010
5的二进制表示:    00000000000000000000000000000101
-5的二进制表示:11111111111111111111111111111011
4的二进制表示:    00000000000000000000000000000100
-4的二进制表示:11111111111111111111111111111100
3的二进制表示:    00000000000000000000000000000011
-3的二进制表示:11111111111111111111111111111101
2的二进制表示:    00000000000000000000000000000010
-2的二进制表示:11111111111111111111111111111110
1的二进制表示:    00000000000000000000000000000001
-1的二进制表示:11111111111111111111111111111111
0的二进制表示:    00000000000000000000000000000000
---------------------
long类型(-5L):1111111111111111111111111111111111111111111111111111111111111011
长度是:64

我们发现: -X 和 X-1 按位取反,正好是相同的(如:-10 和 9、-9 和 8)
所以说:对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。

-9 的补码是:9的补码(00000000000000000000000000001001)取反(11111111111111111111111111110110)+1=

(11111111111111111111111111110111)

类型长度
整型
整型常量在机器中占32位,具有int型的值,对于long型值,则要在数字后加L或l,如123L表示一个长整数,它在机器中占64位。整型变量的类型有byte、short、int、long四种。

基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127
 
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767
 
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647
 
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807
 
基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
 
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
 
基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

int类型是最常使用的一种整数类型。它所表示的数据范围足够大,而且适合于32位、64位处理器。但对于大型计算,常会遇到很大的整数,超出int类型所表示的范围,这时要使用long类型。
由于不同的机器对于多字节数据的存储方式不同,可能是从低字节向高字节存储,也可能是从高字节向低字节存储,这样,在分析网络协议或文件格式时,为了解决不同机器上的字节存储顺序问题,用byte类型来表示数据是合适的。

而通常情况下,由于其表示的数据范围很小,容易造成溢出,应避免使用。

short类型则很少使用,它限制数据的存储为先高字节,后低字节,这样在某些机器中会出错。


整型变量的定义,如:

byte b;//指定变量b为byte型
shor ts;//指定变量s为short型
int i;//指定变量i为int型
long l;//指定变量l为long型
浮点型(实型)数据
实型变量的类型有float和double两种,下表列出这两种类型所占内存的位数和其表示范围。
数据类型所占位数数的范围
float323.4e-038~3.4e+038
double641.7e-308~1.7e+308
双精度类型double比单精度类型float具有更高的精度和更大的表示范围,常常使用。
(三)实型变量定义,如
floa tf;//指定变量f为float型
double d;//指定变量d为double型
[注]与C、C++不同,Java中没有无符号型整数,而且明确规定了整型和浮点型数据所占的
内存字节数,这样就保证了安全性、健壮性和平台无关性。

Java位运算符
Java定义的位运算(bitwise operators)直接对整数类型的位进行操作,这些整数类型包括long,int,hort,char,and byte。
运算符
~
按位非(NOT)(一元运算);
&
按位与(AND);
|
按位或(OR);
^
按位异或(XOR);
>>
右移;
>>>
右移,左边空出的位以0填充;无符号右移;
<<
左移;
&=
按位与赋值;
|=
按位或赋值;
^=
按位异或赋值;
>>=
右移赋值;
>>>=
右移赋值,左边空出的位以0填充;无符号左移;
<<=
左移赋值;


详细解释
按位非(NOT)
按位非也叫做补,一元运算符NOT“~”是对其运算数的每一位取反。例如,数字42,它的二进制代码为:
00101010
经过按位非运算成为
11010101

例:


~5=-6
0000 0000 0000 0000 0000 0000 0000 0101
1111 1111 1111 1111 1111 1111 1111 1010

~-5=4
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0100


按位与(AND)
按位与运算符“&”,如果两个运算数都是1,则结果为1。其他情况下,结果均为零。看下面的例子:
00101010  42 & 00001111  15
0000101010
例:


5&3=1
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0001

-5&3=3
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0011


按位或(OR)
按位或运算符“|”,任何一个运算数为1,则结果为1。如下面的例子所示:
00101010  42 | 00001111  15
0010111147
例:


5|3=7
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0111

-5|3=-5
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0011
1111 1111 1111 1111 1111 1111 1111 1011


按位异或(XOR)
按位异或运算符“^”,只有在两个比较的位不同时其结果是1。否则,结果是零。下面的例子显示了“^”运算符的效果。这个例子也表明了XOR运算符的一个有用的属性。注意第二个运算数有数字1的位,42对应二进制代码的对应位是如何被转换的。第二个运算数有数字0的位,第一个运算数对应位的数字不变。当对某些类型进行位运算时,你将会看到这个属性的用处。
00101010  42 ^ 00001111  15
0010010137
例:


5^3=6
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0011
0000 0000 0000 0000 0000 0000 0000 0110

-5^3=-8
1111 1111 1111 1111 1111 1111 1111 1011
0000 0000 0000 0000 0000 0000 0000 0011
1111 1111 1111 1111 1111 1111 1111 1000

位逻辑运算符的应用

public class BitLogic {
    public static void main(String args[]) {
 
        String binary[] = { "0000", "0001", "0010", "0011", "0100", "0101",
                "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101",
                "1110", "1111" };
        int a = 3; // 0 + 2 + 1 or 0011 in binary
        int b = 6; // 4 + 2 + 0 or 0110 in binary
        int c = a | b;
        int d = a & b;
        int e = a ^ b;
        int f = (~a & b) | (a & ~b);
        int g = ~a & 0x0f;
 
        System.out.println(" a = " + binary[a]);
        System.out.println(" b = " + binary[b]);
        System.out.println(" a|b = " + binary[c]);
        System.out.println(" a&b = " + binary[d]);
        System.out.println(" a^b = " + binary[e]);
        System.out.println("~a&b|a&~b = " + binary[f]);
        System.out.println(" ~a = " + binary[g]);
    }

}

在本例中,变量a与b对应位的组合代表了二进制数所有的4种组合模式:0-0,0-1,1-0,和1-1。“|”运算符和“&”运算符分别对变量a与b各个对应位的运算得到了变量c和变量d的值。对变量e和f的赋值说明了“^”运算符的功能。字符串数组binary代表了0到15对应的二进制的值。在本例中,数组各元素的排列顺序显示了变量对应值的二进制代码。数组之所以这样构造是因为变量的值n对应的二进制代码可以被正确的存储在数组对应元素binary[n]中。例如变量a的值为3,则它的二进制代码对应地存储在数组元素binary[3]中。~a的值与数字0x0f(对应二进制为00001111)进行按位与运算的目的是减小~a的值,保证变量g的结果小于16。因此该程序的运行结果可以用数组binary对应的元素来表示。该程序的输出如下:

a = 0011
 b = 0110
 a|b = 0111
 a&b = 0010
 a^b = 0101
~a&b|a&~b = 0101
 ~a = 1100

左移运算符
左移运算符<<使指定值的所有位都左移规定的次数。它的通用格式如下所示:
value<<num
这里,num指定要移位值value移动的位数。也就是,左移运算符<<使指定值的所有位都左移num位。每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int类型时,每移动1位它的第31位就要被移出并且丢弃;当左移的运算数是long类型时,每移动1位它的第63位就要被移出并且丢弃。
在对byte和short类型的值进行移位运算时,你必须小心。因为你知道Java在对表达式求值时,将自动把这些类型扩大为int型,而且,表达式的值也是int型。对byte和short类型的值进行移位运算的结果是int型,而且如果左移不超过31位,原来对应各位的值也不会丢弃。但是,如果你对一个负的byte或者short类型的值进行移位运算,它被扩大为int型后,它的符号也被扩展。这样,整数值结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将结果转换为byte型。下面的程序说明了这一点:

public class ByteShift {
    public static void main(String args[]) {
        byte a = 64;
        byte b;
        int i;
 
        i = a << 2;
        b = (byte) (a << 2);
        System.out.println("Original value of a: " + a);
        System.out.println("i: " + i + "
b: " + b);
    }
}

运行结果

Original value of a: 64
i: 256
b: 0

因变量a在赋值表达式中,故被扩大为int型,64(01000000)被左移两次生成值256(100000000)被赋给变量i。然而,经过左移后,变量b中惟一的1被移出,低位全部成了0,因此b的值也变成了0。
既然每次左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。下面的程序说明了这一点:

public class MultByTwo {
    public static void main(String args[]) {
        int i;
        int num = 0xFFFFFFE;
 
        for (i = 0; i < 4; i++) {
            num = num << 1;
            System.out.println(num);
        }
    }
}

运行结果

536870908
1073741816
2147483632
-32

初值经过仔细选择,以便在左移4位后,它会产生-32。正如你看到的,当1被移进31位时,数字被解释为负值。
右移运算符
右移运算符>>使指定值的所有位都右移规定的次数。它的通用格式如下所示:
value>>num
这里,num指定要移位值value移动的位数。也就是,右移运算符>>使指定值的所有位都右移num位。下面的程序片段将值32右移2次,将结果8赋给变量a:
inta=32;
a=a>>2;//a  now  contains 8
当值中的某些位被“移出”时,这些位的值将丢弃。例如,下面的程序片段将35右移2次,它的2个低位被移出丢弃,也将结果8赋给变量a:
inta=35;
a=a>>2;//astillcontains8
用二进制表示该过程可以更清楚地看到程序的运行过程:
0010001135
>>2
000010008
将值每右移一次,就相当于将该值除以2并且舍弃了余数。你可以利用这个特点将一个整数进行快速的2的除法。当然,你一定要确保你不会将该数原有的任何一位移出。
右移时,被移走的最高位(最左边的位)由原来最高位的数字补充。例如,如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0,这叫做符号位扩展(保留符号位)(signextension),在进行右移操作时用来保持负数的符号。例如,–8>>1是–4,用二进制表示如下:
11111000–8>>111111100–4
一个要注意的有趣问题是,由于符号位扩展(保留符号位)每次都会在高位补1,因此-1右移的结果总是–1。有时你不希望在右移时保留符号。例如,下面的例子将一个byte型的值转换为用十六进制表示。注意右移后的值与0x0f进行按位与运算,这样可以舍弃任何的符号位扩展,以便得到的值可以作为定义数组的下标,从而得到对应数组元素代表的十六进制字符。

public class HexByte {
    static public void main(String args[]) {
 
        char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
                'b', 'c', 'd', 'e', 'f' };
        byte b = (byte) 0xf1;
        System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);
    }
}
该程序的输出如下:

b = 0xf1

无符号右移
正如上面刚刚看到的,每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号。但有时这并不是我们想要的。例如,如果你进行移位操作的运算数不是数字值,你就不希望进行符号位扩展(保留符号位)。当你处理像素值或图形时,这种情况是相当普遍的。在这种情况下,不管运算数的初值是什么,你希望移位后总是在高位(最左边)补0。这就是人们所说的无符号移动(unsignedshift)。这时你可以使用Java的无符号右移运算符>>>,它总是在左边补0。
下面的程序段说明了无符号右移运算符>>>。在本例中,变量a被赋值为-1,用二进制表示就是32位全是1。这个值然后被无符号右移24位,当然它忽略了符号位扩展,在它的左边总是补0。这样得到的值255被赋给变量a。
inta=-1;a=a>>>24;
下面用二进制形式进一步说明该操作:
11111111111111111111111111111111int型-1的二进制代码>>>24无符号右移24位00000000000000000000000011111111int型255的二进制代码
由于无符号右移运算符>>>只是对32位和64位的值有意义,所以它并不像你想象的那样有用。因为你要记住,在表达式中过小的值总是被自动扩大为int型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。这样,对第7位以0开始的byte型的值进行无符号移动是不可能的,因为在实际移动运算时,是对扩大后的32位值进行操作。下面的例子说明了这一点:

public class ByteUShift {
    static public void main(String args[]) {
        int b = 2;
        int c = 3;
        int a = 0;
        
        a |= 4;
        b >>= 1;
        c <<= 1;
        a ^= c;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);
 
    }
}

运行结果

a = 2
b = 1
c = 6

对于java移位运算的总结:
1.对于左移运算,每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int类型时,每移动1位,它的第31位就要被移出并且丢弃;当左移的运算数是long类型时,每移动1位它的第63位就要被移出并且丢弃。
2.左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。
3.在对byte和short类型的值进行移位运算时,Java将自动把这些类型扩大为int型,而且,移位后的值也是int型;如果左移不超过31位,原来对应各位的值不会丢弃。但是,如果你对一个负的byte或者short类型的值进行移位运算,它被扩大为int型后,它的符号也被扩展,结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将移位运算的结果再转换成byte型。
4.每右移一次,就相当于将该值除以2并且舍弃了余数。你可以利用这个特点将一个整数进行快速的2的除法。当然,你一定要确保你不会将该数原有的任何一位移出。
5.无符号右移(>>>)与右移的区别:
(1)每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号
(2)无符号移动总是在高位(最左边)补0。
6.与C、C++不同,Java中没有无符号型整数,而且明确规定了整型和浮点型数据所占的内存字节数,这样就保证了安全性、鲁棒性和平台无关性。
其他:
(1)BCD码(二到十进制编码)
人们通常习惯使用十进制数,而计算机内部多采用二进制表示和处理数值数据,因此在计算机输入和输出数据时,就要进行由十进制到二进制的转换处理。
把十进制数的每一位分别写成二进制形式的编码,称为二进制编码的十进制数,即二到十进制编码或BCD(BinaryCodedDecimal)编码。
BCD码编码方法很多,通常采用8421编码,这种编码方法最自然简单。其方法使用四位二进制数表示一位十进制数,从左到右每一位对应的权分别是23、22、21、20,即8、4、2、1。例如十进制数1975的8421码可以这样得出
1975(D)=0001100101110101(BCD)
用四位二进制表示一位十进制会多出6种状态,这些多余状态码称为BCD码中的非法码。BCD码与二进制之间的转换不是直接进行的,当需要将BCD码转换成二进制码时,要先将BCD码转换成十进制码,然后再转换成二进制码;当需要将二进制转换成BCD码时,要先将二进制转换成十进制码,然后再转换成BCD码。
(2)字符编码
在计算机中,对非数值的文字和其他符号进行处理时,首先要对其进行数字化处理,即用二进制编码来表示文字和符号。字符编码就是以二进制的数字来对应字符集的字符,目前用得最普遍的字符集是ANSI,对应ANSI字符集的二进制编码就称为ANSI码,DOS和Windows系统都使用了ANSI码。在输入过程中,系统自动将用户输入的各种数据按编码的类型转换成相应的二进制形式存入计算机存储单元中;在输出过程中,再由系统自动将二进制编码数据转换成用户可以识别的数据格式输出给用户。
(3)ASCⅡ码
用七位二进制表示字符的一种编码,使用一个字节表示一个特殊的字符,字节高位为0或用于在数据传输时的校验。附表为标准的ASCⅡ码表。

int a=0x14020304(十六进制)
实际a的值已经转换为int型值为335676164
十六进制 0x14020304
十进制 335676164
二进制 10100000000100000001100000100
位于运算都是基于二进制来进行的


Ps:转自https://blog.csdn.net/limenghua9112

原文链接:https://blog.csdn.net/limenghua9112/article/details/8984662

原文地址:https://www.cnblogs.com/lhq1996/p/11375764.html