最近新起项目,从底层搭建,在写一个发送信息或短信的接口,正好用到了Java的位运算,写博客保存,也是装逼的好方法。
以前了解到位运算,但是实际确很少碰到,这次也是一个特别简单的方法,先上示例:
PS:int 的实际大小是32bit,文中为了方便基本上使用16bit代替
一、java位运算中按位与 实例
public static void main(String[] args) { sendMessage("","",7); } /** * 发送信息的对外接口 * 发送短信flag : 1 * 发送邮件flag : 2 * 发送微信flag : 4 * 同时发送多种 flag : 和(发送短信和邮件就是3) */ private static final int SMS = 1; private static final int MAIL = 2; private static final int WECHAT = 4; public static void sendMessage(String title,String content,int flag){ if((flag & SMS) == 1){ System.out.println("调用发送短信接口"); } if((flag & MAIL) == 2){ System.out.println("调用发送邮件接口"); } if((flag & WECHAT) == 4){ System.out.println("调用发送微信接口"); } }
这个方法是比较简单的,使用java的安位与运算能够使逻辑更简洁和明朗。
二、java位运算
1.按位与 &
描述:两位全是1,结果才为1: 0&0=0; 0&1=0; 1&0=0; 1&1=1;
用途:
①清零:把一个数变为0,只要和0进行与运算。50&0=0;
②取一个数中制定位置:想取哪位,哪位置为1,其余为0;
设 x = 1010 1110 想要取得x的低4位
1010 1110
& 0000 1111
————————————————————
0000 1110
2.按位或 |
描述:只要有一个为1,结果就为1: 0|0=0; 0|1=1; 1|0=1; 1|1=1;
用途:
对一个数的某一位 置1:想要把哪位置1,哪位就是1,其余是0;
设 x = 1010 0000 低4位置1
1010 0000
| 0000 1111
————————————————————
1010 1111
3.按位异或 ^
描述:两个位的值为“异”(值不同),该位结果为1,值相同为0
0^0=0; 0^1=1; 1^0=1; 1^1=0;
用途:
①使特定位翻转:想要翻转的位为1,其余为0;
设 x = 1010 1110 低4翻转
1010 1110
^ 0000 1111
————————————————————
1010 0001
②与0异或保留原值
设 x = 1010 1110 保留原值
1010 1110
^ 0000 0000
————————————————————
1010 1110
③两个值进行交换:比较高效的方法
void swap(int x , int y) { x ^= y; y ^= x; x ^= y; }
4.取反运算 ~
~1 = 0; ~0 = 1;
5.左移运算 <<
描述:将一个运算对象的各二进制位全部左移若干位(左边二进制丢弃,右边补0)
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
2<<1=4;
0000 0010 左移1位 0000 0100 为4; => 2*2=4;
6.右移运算 >>
描述:将一个运算对象的各二进制位全部右移若干位( 正数左补0,负数左补1,右边丢弃)
每右移一位,相当于该数除以2;
4>>2=1;
0000 0100 右移两位 0000 0001 为1; => 4/2/2=1;
7.无符号右移运算 >>>
描述:各个位向右移制定位数(左边补0,右边丢弃)
8.负数移位举例
负数是以其正值的补码形式来表示的
原码:一个正数按照绝对值大小转化成的二进制数称为原码
14原码: 0000 0000 0000 0000 0000 0000 0000 1110
反码:将原码按位取反,所得二进制称为原二进制的反码
14反码: 1111 1111 1111 1111 1111 1111 1111 0001
补码:反码加1称为补码
14补码: 1111 1111 1111 1111 1111 1111 1111 0010 =>即-14的二进制表示
求-14<<2
1111 1111 1111 1111 1111 1111 1111 0010 左移两位,右边补0
1111 1111 1111 1111 1111 1111 1100 1000 然后这个二进制的十进制是多少呢?肯定是一个负数,所以需要按照上面顺序反向求出该二进制的十进制。
先减一:
1111 1111 1111 1111 1111 1111 1100 0111
取反:
0000 0000 0000 0000 0000 0000 0011 1000 =>56
所以 -14<<2 = -56;
以上是负数的取反的分析过程。
从网上找到一个位运算的口诀:
清零取数要用与,某位置一可用或
若要取反和交换,轻轻松松用异或
三、java进制转换
1.进制转换API
//十进制到十六进制 Integer.toHexString(int i); //十进制到八进制 Integer.toOctalString(int i); //十进制到二进制 Integer.toBinaryString(int i); //十六进制到十进制 Integer.parseInt("0xff", 16); //八进制到十进制 Integer.parseInt("0123", 8); //二进制到十进制 Integer.parseInt("1010", 2);
2.Java基本数据类型
byte : 8bit;
short : 16bit;
int : 32bit;
long : 64bit;
float : 32bit;
double : 64bit;
char : unicode字符16bit;
3.java数据类型转换为字节的底层实现原理([color=red]就是使用位运算哦)[/color]
例:8143(00000000 00000000 00011111 11001111)转化为字节数组为:
byte[] b = [-49,31,0,0]
实现:
①低8位: 8143>>0*8 & 0xff; 8143右移0位 和十六进制0xff进行 & 运算。
0xff:11111111 功能就是屏蔽掉前面24位取得最后8位。
结果为:11001111 十进制:207 或者 -49;
②第二低8位: 8143 >> 1*8 & 0xff; 8143右移1位 和十六进制0xff进行 & 运算。
向右移8位,然后与运算屏蔽前面24位取得第二8位。
结果为:00011111 十进制:31;
③第三低8位: 8143 >> 2*8 & 0xff; 8143右移2位 和十六进制0xff进行 & 运算。
结果为0;
③第四低8位: 8143 >> 3*8 & 0xff; 8143右移3位 和十六进制0xff进行 & 运算。
结果为0;
综上,每次进行移位取特定的8位,转化成相应的字节数组,以上的方式是采用小端表示法。
4.javaIO的源码中位运算
java源码中与上面例子十分相似的有一个IO流的writeInt方法。
DataOutputStream中的writeInt()方法:
public final void writeInt(int v) throws IOException { out.write((v >>> 24) & 0xFF); out.write((v >>> 16) & 0xFF); out.write((v >>> 8) & 0xFF); out.write((v >>> 0) & 0xFF); incCount(4); }
java底层的实现很多都用到了位运算,所以掌握好了还是可以帮助查看源码的。
5.大小端
上面举例是采用小端的方式来输出字节数组的。
小端(Little-Endian):低位字节排放到内存的低地址端即该值的起始地址,高位字节排放到内存的高地址端。
大端(Big-Endian):高位字节排放到内存的低地址端即该值的起始地址,低位字节排放到内存的高地址端。
byte b = [-49,31,0,0]
小端
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | -49 | 31 | 0 | 0 |
大端
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | 0 | 0 | 31 | -49 |
先这些吧,累屎,不过每次自己码完博客,对待问题都会有更新的认识,明天继续JAVA IO流。