Java中的位运算机制详解

来源:互联网 发布:传奇霸业翅膀升级数据 编辑:程序博客网 时间:2024/05/01 05:20

今天在读Think in Java的时候,读到位运算的地方有这么一段话不理解:

“对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有数值右侧的5个低位才会有用。这样可防止我们在一个int数里移动不切实际的位数。若对一个long值进行处理,最后得到的结果也是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。”

刚看到这句话特别不理解为什么char,byte,short,进行移位处理的时候只有数值右端的低五位才有用?
后来查阅资料后才发觉应该是翻译错误,让自己理解出了偏差,这句话其实应该是这样的:对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有右侧数值的5个低位才会有用。翻译把右侧数值翻译成了数值右侧,让我们的思维进入了误区,其实这句话跟上一句话没有直接联系。
对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。 这是独立的一句话,意思说这三个类型进行移位运算的时候会选转换成int,并且值也会存在int里面。
而后面一句话:只有右侧数值的5个低位才会有用。
理解这句话我们需要知道位运算操作符的格式:
格式:左侧数字 - 移位运算符 - 右侧数字 —— 把左侧数字的二进制移动右侧数字的位数
例如: 10 >> 3 表示10 带符号右移3位
弄懂规则就好理解我们的疑惑了:为什么只有右侧数值的5个低位才会有用?
首先:int 所占的最大内存是32位,那么如果我们把一个int类型的数字移32位,那么就会移出原本数字的所有位数,这样是没有意义的,那么我们就要保证移位不会超过32位,那我们就取右侧数字的低五位(最大值为31)来保证我们的移位操作不会移动该类型最大的位数。
我们可以用代码来验证这句话:

public static void main(String[] args) {        //定义目标数字用于移位操作        int a = 1000000;        //我想要移动的位数,即移位符号右侧数字        short b = 50;        //令c = a 无符号右移50位        int c = (a >>> b);        //18是50转换为二进制后取低五位的值        int d = a >>> 18;        System.out.println(c);        System.out.println(d);    }

测试代码发现c 与 d的值相同,证明我们的结论是正确的。下面是我在网上找的一点拓展知识:

引用网上内容:
请用最有效率的方法计算出2乘以8等于几?

这里所谓的最有效率,实际上就是通过最少、最简单的运算得出想要的结果,而移位是计算机中相当基础的运算了,用它来实现准没错了。左移位“<<”把被操作数每向左移动一位,效果等同于将被操作数乘以2,而2*8=(2*2*2*2),就是把2向左移位3次。因此最有效率的计算2乘以8的方法就是“2<<3”。

最后,我们再来考虑一种情况,当要移位的位数大于被操作数对应数据类型所能表示的最大位数时,结果会是怎样呢?比如,1<<35=?呢?

这里就涉及到移位运算的另外一些规则:

byte、short、char在做移位运算之前,会被自动转换为int类型,然后再进行运算。
byte、short、int、char类型的数据经过移位运算后结果都为int型。
long经过移位运算后结果为long型。
在左移位(<<)运算时,如果要移位的位数大于被操作数对应数据类型所能表示的最大位数,那么先将要求移位数对该类型所能表示的最大位数求余后,再将被操作数移位所得余数对应的数值,效果不变。比如1<<35=1<<(35%32)=1<<3=8。
对于有符号右移位(>>)运算和无符号右移位(>>>)运算,当要移位的位数大于被操作数对应数据类型所能表示的最大位数时,那么先将要求移位数对该类型所能表示的最大位数求余后,再将被操作数移位所得余数对应的数值,效果不变。。比如100>>35=100>>(35%32)=100>>3=12。

下面的测试代码验证了以上的规律:

public abstract class Test {           public static void main(String[] args) {            System.out.println("1 << 3 = " + (1 << 3));            System.out.println("(byte) 1 << 35 = " + ((byte) 1 << (32 + 3)));            System.out.println("(short) 1 << 35 = " + ((short) 1 << (32 + 3)));            System.out.println("(char) 1 << 35 = " + ((char) 1 << (32 + 3)));            System.out.println("1 << 35 = " + (1 << (32 + 3)));            System.out.println("1L << 67 = " + (1L << (64 + 3)));            // 此处需要Java5.0及以上版本支持            System.out.println("new Integer(1) << 3 = " + (new Integer(1) << 3));            System.out.println("10000 >> 3 = " + (10000 >> 3));            System.out.println("10000 >> 35 = " + (10000 >> (32 + 3)));            System.out.println("10000L >>> 67 = " + (10000L >>> (64 + 3)));        }      } 
public abstract class Test {           public static void main(String[] args) {            System.out.println("1 << 3 = " + (1 << 3));            System.out.println("(byte) 1 << 35 = " + ((byte) 1 << (32 + 3)));            System.out.println("(short) 1 << 35 = " + ((short) 1 << (32 + 3)));            System.out.println("(char) 1 << 35 = " + ((char) 1 << (32 + 3)));            System.out.println("1 << 35 = " + (1 << (32 + 3)));            System.out.println("1L << 67 = " + (1L << (64 + 3)));            // 此处需要Java5.0及以上版本支持            System.out.println("new Integer(1) << 3 = " + (new Integer(1) << 3));            System.out.println("10000 >> 3 = " + (10000 >> 3));            System.out.println("10000 >> 35 = " + (10000 >> (32 + 3)));            System.out.println("10000L >>> 67 = " + (10000L >>> (64 + 3)));        }      } 

运行结果:

1 << 3 = 8
(byte) 1 << 35 = 8
(short) 1 << 35 = 8
(char) 1 << 35 = 8
1 << 35 = 8
1L << 67 = 8
new Integer(1) << 3 = 8
10000 >> 3 = 1250
10000 >> 35 = 1250
10000L >>> 67 = 1250

0 0