1 . 与 运算符
与运算符用符号“&”表示,其使用规律如下:
两个操作数中,位都为1,结果才为1,否则结果为0。与运算符是二元运算符。
需要了解的知识点:
基本数据类型 | java语言中,对应基本数据类型 在内存中所占内存空间大小 (B:byte,字节;b:bit,位;1B=8bit) |
取值范围 |
byte、boolean | 1字节/1B/8b | byte: - 2^7~2^7-1 ;-128-127 ;存储数据量255 |
char、short | 2字节/2B/16b | short: - 2^15~2^15-1;-32768~32767;存储数据量 65535 |
int、float | 4字节/4B/32b | int: - 2^31~2^31-1 |
long、double | 8字节/8B/64b | long: - 2^63~2^63-1 |
实例:
属性 | 值 | 二进制 |
a | 129 | 0000 0000 0000 0000 0000 0000 1000 0001 |
b | 128 | 0000 0000 0000 0000 0000 0000 1000 0000 |
a&b | 128 | 0000 0000 0000 0000 0000 0000 1000 0000 |
代码:
public class Test {
public static void main(String[] args){
int a=129;
int b=128;
System.out.println("a 和b 与的结果是:"+(a&b));
}
}
执行结果:
a 和b 与的结果是:128
Process finished with exit code 0
2. 或运算符
或运算符用符号“|”表示,其运算规律如下:
两个位只要有一个为1,那么结果就是1,否则就为0。 或运算符是二元运算符。
属性 | 值 | 二进制 |
a | 129 | 0000 0000 0000 0000 0000 0000 1000 0001 |
b | 128 | 0000 0000 0000 0000 0000 0000 1000 0000 |
a|b | 129 | 0000 0000 0000 0000 0000 0000 1000 0001 |
代码:
public class Test {
public static void main(String[] args){
int a=129;
int b=128;
System.out.println("a 和b 或的结果是:"+(a|b));
}
}
执行结果:
a 和b 或的结果是:129
Process finished with exit code 0
3. 非运算符
非运算符用符号“~”表示,其运算规律如下:
如果位为0,结果是1,如果位为1,结果是0。非运算符是一元运算符。
实例:(涉及到补码,为-3。最高位是符号位,0-正值,1-负值)
属性 | 值 | 二进制 |
a | 2 | 0000 0000 0000 0000 0000 0000 0000 0010 |
~a | -3 | 1111 1111 1111 1111 1111 1111 1111 1101 |
知识点:源码、反码、补码
电脑的的世界中只有0和1,那么负数怎么表示呢?
二进制的正负是从高位看,最高位如果1则是负数,如果是0则是正数。
如果负数单纯是把最高位变为1的话,在运算中会出现不是我们想要的值,所以引入了:原码,反码,补码。
正数的原码,反码,补码都一样,负数的反码是对除了符号位(最高位)对原码取反,补码是对反码+1
负数的二进制转化,计算机计算是用的补码
1、首先取出这个数的原码的二进制,
2、然后再求出反码
3、最后求出补码
例子: -5
-5的原码是 1000 0000 0000 0101
求出反码的是 1111 1111 1111 1010
求出补码是 1111 1111 1111 1011
本例中:~a二进制表示为 1111 1111 1111 1111 1111 1111 1111 1101,如何得出~a为-3呢
二进制 | |
补码 | 1111 1111 1111 1111 1111 1111 1111 1101 |
反码 | 1111 1111 1111 1111 1111 1111 1111 1100 |
源码 | 1000 0000 0000 0000 0000 0000 0000 0011 |
源码为: 1000 0000 0000 0000 0000 0000 0000 0011,
最高位(红色)是 1 ,表示为负值;数据位(绿色)是 000 0000 0000 0000 0000 0000 0000 0011;十进制表示为3;
所以源码表示的十进制值是 -3 。
代码:
public class Test {
public static void main(String[] args){
int a=2;
System.out.println("a 非的结果是:"+(~a));
System.out.println("~a的二进制表示为:"+Integer.toBinaryString(~a));
}
}
执行结果:
a 非的结果是:-3
~a的二进制表示为:11111111111111111111111111111101
Process finished with exit code 0
4.异或运算符
异或运算符是用符号“^”表示的,其运算规律是:
两个操作数的位中,相同则结果为0,不同则结果为1。异或运算符是二元运算符。
属性 | 值 | 二进制 |
a | 15 | 0000 0000 0000 0000 0000 0000 0000 1111 |
b | 2 | 0000 0000 0000 0000 0000 0000 0000 0010 |
a|b | 13 | 0000 0000 0000 0000 0000 0000 0000 1101 |
代码:
public class Test {
public static void main(String[] args){
int a=15;
int b=2;
System.out.println("a 和b 异或的结果是:"+(a^b));
}
}
执行结果:
a 和b 异或的结果是:13
Process finished with exit code 0
java中有三种移位运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐
实例:(正数在输出二进制表示时,左面的0不输出)
属性 | 值 | 二进制 |
a | 5 | 0000 0000 0000 0000 0000 0000 0000 0101 |
a<<2 | 20 | 0000 0000 0000 0000 0000 0000 0001 0100 |
a>>2 | 1 | 0000 0000 0000 0000 0000 0000 0000 0001 |
a>>>2 | 1 | 0000 0000 0000 0000 0000 0000 0000 0001 |
b | - 5 | 1111 1111 1111 1111 1111 1111 1111 1011 |
b<<2 | - 20 | 1111 1111 1111 1111 1111 1111 1110 1100 |
b>>1 | -3 | 1111 1111 1111 1111 1111 1111 1111 1101 |
b>>2 | - 2 | 1111 1111 1111 1111 1111 1111 1111 1110 |
b>>>2 | 1073741822 | 0011 1111 1111 1111 1111 1111 1111 1110 |
代码:
public class Test {
public static void main(String[] args) {
int a = 5;
System.out.println("a是:" + a + " 二进制表示是:" + Integer.toBinaryString(a));
System.out.println("a<<2的结果是:" + (a << 2) + " 二进制表示是:" + Integer.toBinaryString(a << 2));
System.out.println("a>>2的结果是:" + (a >> 2) + " 二进制表示是:" + Integer.toBinaryString(a >> 2));
System.out.println("a>>>2的结果是:" + (a >>> 2) + " 二进制表示是:" + Integer.toBinaryString(a >>> 2));
int b = -5;
System.out.println("b是:" + b + " 二进制表示是:" + Integer.toBinaryString(b));
System.out.println("b<<2的结果是:" + (b << 2) + " 二进制表示是: " + Integer.toBinaryString(b << 2));
System.out.println("b>>1结果是:" + (b >> 1) + " 二进制表示是: " + Integer.toBinaryString(b >> 1));
System.out.println("b>>2的结果是:" + (b >> 2) + " 二进制表示是: " + Integer.toBinaryString(b >> 2));
System.out.println("b>>>2的结果是:" + (b >>> 2) + " 二进制表示是: " + Integer.toBinaryString(b >>> 2));
}
}
运行结果:
a是:5 二进制表示是:101
a<<2的结果是:20 二进制表示是:10100
a>>2的结果是:1 二进制表示是:1
a>>>2的结果是:1 二进制表示是:1
b是:-5 二进制表示是:11111111111111111111111111111011
b<<2的结果是:-20 二进制表示是: 11111111111111111111111111101100
b>>1结果是:-3 二进制表示是: 11111111111111111111111111111101
b>>2的结果是:-2 二进制表示是: 11111111111111111111111111111110
b>>>2的结果是:1073741822 二进制表示是: 111111111111111111111111111110
Process finished with exit code 0
关系:位运算符比加减运算符运算速度更快。一般能用位运算的就用位运算符。
实际运用:
实例1:输入一个整数,输出该数二进制表示中1的个数,如:9的二进制表示是1001,有2位是1,因此如果输入9,该函数输出2.
输入 | 二进制表示 | 输出 |
9 | 0000 0000 0000 0000 0000 0000 0000 1001 | 2 |
第一种思路:使用 >> 运算符,将数据右移,不断与数字 1做与 & 运算,若与运算结果为1,则该位置二进制表示为1,否则为0;
代码实现:
public class Test {
public static void main(String[] args)
{
int a=9;
System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
" 二进制表示法包含的位数是:"+ getNumOf1(a));
}
public static int getNumOf1(int n)
{
int number=0;
while(n!=0)
{
if ((n&1)==1)
number++;
n=n>>1;
}
return number;
}
}
程序执行结果:
数字 9 在内存中得二进制表示为:1001 二进制表示法包含的位数是:2
Process finished with exit code 0
问题:
存在问题,移位运算中,当n时正数时,右移的话,最左面补0
正值>> 左面补0 负值>> 左面补1 正负<< 右面补0
在本段代码中,当a是负数时,右移的话,最左面补位补的是1,一直右移就一直补1.会造成死循环 (1000)>>(1100)>>(1110)>>(1111)>>...>>1111。思考:有没有什么方法,让正负数 在 >> 操作时,左面均补位0 ? 答案: >>> 操作。
另外,如果我们首先判断数据是否是负值,负值的话,将数据变为相反数,即正值再判断1的个数,这种方法是不可取的,因为在内存中数据存储的是补码。不是源码。我们判断的是数据的补码中 1 出现的次数。将数据变为相反数后,数据在内存中表示,不止符号位发生变化,数据位也发生变化。
第二种思路:使用 >>> 运算符,将数据右移,不断与数字 1做与 & 运算,若与运算结果为1,则该位置二进制表示为1,否则为0;
代码:
public class Test {
public static void main(String[] args)
{
int a=9;
int b=-9;
System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
" 二进制表示法包含的位数是:"+ getNumOf1(a));
System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
" 二进制表示法包含的位数是:"+ getNumOf1(b));
}
public static int getNumOf1(int n)
{
int number=0;
while(n!=0)
{
if ((n&1)==1)
number++;
n=n>>>1;
}
return number;
}
}
程序执行结果:
数字 9 在内存中得二进制表示为:1001 二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111 二进制表示法包含的位数是:31
Process finished with exit code 0
第三种思路:使用 << 运算符,flag=1,将flag左移,不断与数字 n做与 & 运算,若与运算结果不为0,则该位置二进制表示为1,否则为0直至flag为0;
这里红色字体:与运算结果不为0。与运算结果情况有两种
1. 与数据位 & ,结果大于0;
2. 与符号位 & ,若数据是负数,即符号位为1,则与结果- 2147483648(写个实例看看为何是这个数字),即: 是负数 ,若数据是正数,即符号位为0,则与结果为0。
代码:
public class Test {
public static void main(String[] args)
{
int a=9;
int b=-9;
System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
" 二进制表示法包含的位数是:"+ getNumOf1(a));
System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
" 二进制表示法包含的位数是:"+ getNumOf1(b));
}
public static int getNumOf1(int n)
{
int flag=1;
int number=0;
while(flag!=0)
{
if((flag&n) != 0)
number++;
flag=flag<<1;
}
return number;
}
}
程序执行结果:
数字 9 在内存中得二进制表示为:1001 二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111 二进制表示法包含的位数是:31
Process finished with exit code 0
第四种思路:有没有什么方法,只在1出现时计算,0时不计算?
对于任意数据n,n-1操作是将n的二进制表示中,最右面的1变为0,且最右面1的后面部分全取反。n=n & (n-1),则是将n的二进制表示中,最右面的1的后面全变为0。利用这一特性,统计二进制表示中1的个数。
n | n-1 | n=n & (n-1) |
二进制表示中1出现的次数 |
11 - 0001 0011 | 10 - 0001 0010 | 10 - 0001 0010 | 1 |
10 - 0001 0010 | 9 - 0001 0001 | 8 - 0001 0000 | 2 |
8 - 0001 0000 | 7- 0000 1111 | 0 - 0000 0000 | 3 |
0 - 0000 0000 | 结束 | 3 |
代码:
public class Test {
public static void main(String[] args)
{
int a=9;
int b=-9;
System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
" 二进制表示法包含的位数是:"+ getNumOf1(a));
System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
" 二进制表示法包含的位数是:"+ getNumOf1(b));
}
public static int getNumOf1(int n)
{
int number=0;
while(n!=0)
{
number++;
n=n&(n-1);
}
return number;
}
}
程序执行结果:
数字 9 在内存中得二进制表示为:1001 二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111 二进制表示法包含的位数是:31
Process finished with exit code 0
其他应用:
/**用一条语句判断一个整数是不是2的整数次方 */
public static boolean is2(int number)
{
if((number&(number-1))==0)
return true;
return false;
}
/**判断两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n */
public static int Getn(int m,int n)
{
int dif=m^n;//一共有多少不同的位
//求dif中的1的位置
int number=0;
while(dif!=0)
{
number++;
dif=dif&(dif-1);
}
return number;
}
/** 题目:不使用加减乘除,完成两个数字的加法操作*/
public static int add(int num1, int num2) {
int sum = 0;
while (num2 != 0) {
sum = (num1 ^ num2);
num2 = (num1 & num2) << 1;
num1 = sum;
}
return num1;
}
/** n除以2 n/2 ,只取整数部分 (n是正数情况下成立) */
n>>1;
/** n乘以2 n/2 (正负数均成立)*/
n<<1;