深入学习java位运算的基础知识
作者:移不动丶 发布时间:2021-06-01 07:10:40
相信大家和我一样,接触java这门语言的时候就听过java位运算的鼎鼎大名,当然也仅限于听说过。日常开发过程中使用过么?使用位运算的好处是什么?
想要真正理解java位运算,首先要搞清楚,这个“位”代表的含义。
一切的起源:二进制
位:二进制位,简称“位”。是二进制记数系统中表示小于2的整数的符号,一般用1或 0表示,是具有相等概率的两种状态中的一种。二进制位的位数可表示一个机器字的字长,一个二进制位包含的信息量称为一比特(bit)。
举个栗子:
int占4个字节(byte)
1byte = 8bit
换算下来,一个int类型即占32bit
int i = 88; 这里的88为十进制,转换为二进制为:1011000,使用完整的32位表示即为:00000000 00000000 00000000 01011000
上文中的00000000 00000000 00000000
01011000即为十进制88转为二进制的 原码 ,与其相关的定义还有 反码 和 补码
关于原码、反码和补码
在计算机内,有符号数有三种表示法:原码、反码以及补码。
原码:就是二进制定点表示法,即最高位为符号位,“0”正负“1”,其余位表示数值的大小。
反码:正数的反码与其原码相同;负数的反码是对正数逐位取反,符号位保持为1。
补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
同样的,我们使用 “88” 举例说明原码、反码以及补码。
“88”的原码:00000000 00000000 00000000 01011000
“88”的反码:00000000 00000000 00000000 01011000
“88”的补码:00000000 00000000 00000000 01011000
对于负数 “-88”,其原码、反码以及补码如下:
“-88”的原码:10000000 00000000 00000000 01011000
“-88”的反码:11111111 11111111 11111111 10100111
“-88”的补码:11111111 11111111 11111111 10101000
为什么要使用补码?
简单来说,就是计算机计算减法时有各种不方便,于是发明了反码,结果发现反码也有缺陷(有两个零存在:“+0”和“-0”),进而发明了补码解决这个问题。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
有关补码的意义及作用在上面的链接里讨论的非常详尽,我这里就不班门弄斧了,理解就好~
对原码、反码以及补码有一个初步的认知后,我们接下来再看位运算就会清晰很多。
关于位运算
关于位运算,这里运用哲学上三个究极问题试图讲解清楚位运算究竟是何方神圣:什么是位运算?位运算的作用?位运算有什么优势?
什么是位运算
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。
下表列出了位运算符的基本运算(A = 8, B = 9)
操作符 | 描述 | 例子 |
---|---|---|
按位与& | 如果相对应位都是1,则结果为1,否则为0 | A&B=8,即1000 |
按位或| | 如果相对应位都是0,则结果为0,否则为1 | A|B=9,即1001 |
按位异或^ | 如果相对应位值相同,则结果为0,否则为1 | A^B=1,即0001 |
按位取反~ | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0 | ~A=7,即0111 |
左移 << | 按位左移运算符。左操作数按位左移右操作数指定的位数 | A << 2 = 32,即1000 00 |
右移 >> | 按位右移运算符。左操作数按位右移右操作数指定的位数 | A >> 2 = 2,即0010 |
位运算的作用及优势
我尝试脱离实际应用场景描述清楚位运算的作用及优势,然后发现脱离实际讲应用是件非常困难的事情,其难度不亚于买彩票。所以这里结合Android原码中的MeasureSpec类来描述位运算的作用和优势。
熟悉Android View体系的小伙伴应该都对MeasureSpec不陌生。不熟悉的请自行Google,不然下面你看起来可能就会有些云里雾里。我们来看它的代码:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
代码不难理解,上面就运用了很多位运算。我们都知道MeasureSpec是用来操作View的测量模式以及测量大小的。这个测量模式和测量大小在系统中使用一个32位的int类型的参数表示。如果让我们自己去实现这样一个操作测量模式和测量大小的类,我们大概会这么写:
public class MeasureSpec{
public static final int UNSPECIFIED = 0;
public static final int EXACTLY = 1;
public static final int AT_MOST = 2;
/**
* 测量模式
*/
private int mode;
/**
* 测量大小
*/
private int size;
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
然后每次对View进行操作的时候都会 new 一个MeasureSpec对象,对其的mode和size参数进行相应的操作。
这里原码就很巧妙的运用了位运算简化了相应的操作,使用32位的二进制来操作mode和size:高两位表示mode,低30位表示size,避免了频繁的创建对象,更省内存,让我等对位运算不了解的拍手称秒哇。
总结
不同于其他文章讲解位运算的概念,本文更侧重于运用位运算的作用及优势。前人筚路蓝缕,以启山林,以聪明才智发明了位运算这种简洁高效的运算符,希望你能理解并正确发挥其作用,走上人生的巅峰~
来源:https://juejin.im/post/5cf0e303f265da1bb679fede


猜你喜欢
- 1. 启动入口本系列RocketMQ4.8注释github地址,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈前面我们已经分析完
- CancellationTokenCancellationToken有一个构造函数,可以传入一个bool类型表示当前的Cancellatio
- 问题描述利用选择排序把一列数组按从小到大或从大到小排序(一)、选择排序思想以从小到大为例:1、第一轮选择,从第一个数开始,依次比较后面所有的
- 前言:最新Servlet 3.0 * 的使用1.pom.xml添加需要使用的依赖<project xmlns="http:/
- 一:日志:1、配置日志级别日志记录器(Logger)的行为是分等级的。如下表所示:分为:OFF、FATAL、ERROR、WARN、INFO、
- 本文实例为大家分享了Android实现语音播放与录音的具体代码,供大家参考,具体内容如下项目用到的技术点和亮点语音录音 (单个和列表)语音播
- collect通知flow执行public suspend inline fun <T> Flow<T>.colle
- 本文实例为大家分享了struts2实现多文件上传的具体代码,供大家参考,具体内容如下首先搭建好struts2的开发环境,导入struts2需
- 之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源。在单数据源的情况下,Spring Boot的配置
- 数据存储与访问常用方式:文件SharedPreferences(偏好参数设置)SQLite数据库内容提供者(Content provider
- IDEA全局替换通过快捷键 Ctrl+Shift+r 或这点击 Edit 》Find 》Replace In Path有些IDEA版本按了快
- 前言本文主要给大家介绍了关于Spring Boot集成之异步调用Async的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细
- 本文为大家分享了C#基于Socket套接字的网络通信封装代码,供大家参考,具体内容如下摘要之所以要进行Socket套接字通信库封装,主要是直
- 由于处理器核心的增长及较低的硬件成本允许低成本的集群系统,致使如今并行编程无处不在,并行编程似乎是下一个大事件。Java 8 针对这一事实提
- 依然使用IE9的捕获参数,做了一个12306的登录功能。参照了网上童鞋们的做法。其他都和前面几篇读取余票、票价一样,不过登录要用到证书的问题
- 引言在项目中,时间的使用必不可少,而java 8之前的时间api Date和Calander等在使用上存在着很多问题,于是,jdk1.8引进
- 这篇文章主要介绍了JavaWeb如何实现禁用浏览器缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- WPF中有一个DrawingContext类,该类提供了很多画法方法,例如DrawLine,DrawText,DrawRectangle等。
- 本文实例讲述了Java实现批量导入excel表格数据到数据库中的方法。分享给大家供大家参考,具体如下:1、创建导入抽象类package co
- 一、问题场景使用Logger.error方法时只能打印出异常类型,无法打印出详细的堆栈信息,使得定位问题变得困难和不方便。二、先放出结论Lo