深入学习java位运算的基础知识

相信大家和我一样,接触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,避免了频繁的创建对象,更省内存,让我等对位运算不了解的拍手称秒哇。

总结

不同于其他文章讲解位运算的概念,本文更侧重于运用位运算的作用及优势。前人筚路蓝缕,以启山林,以聪明才智发明了位运算这种简洁高效的运算符,希望你能理解并正确发挥其作用,走上人生的巅峰~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详谈Java中的二进制及基本的位运算

    二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二",由18世纪德国数理哲学大师莱布尼兹发现.当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的.计算机中的二进制则是一个非常微小的开关,用"开"来表示1,"关"来表示0. 那么Java中的二进制又是怎么样的呢?让我们一起来揭开它神秘的面纱吧. 一.Java内置的进

  • Java位运算知识点详解

    在日常的Java开发中,位运算使用的不多,使用的更多的是算数运算(+.-.*./.%).关系运算(<.>.<=.>=.==.!=)和逻辑运算(&&.||.!),所以相对来说对位运算不是那么熟悉,本文将以Java的位运算来详细介绍下位运算及其应用. 1. 位运算起源 位运算起源于C语言的低级操作,Java的设计初衷是嵌入到电视机顶盒内,所以这种低级操作方式被保留下来.所谓的低级操作,是因为位运算的操作对象是二进制位,但是这种低级操作对计算机而言是非常简单直接,友好高效

  • Java 通过位运算求一个集合的所有子集方法

    Java没有自带的求一个集合的所有子集的方法,我们可以通过集合的子集规律来求. 一个集合的所有子集等于2^该集合的长度.比如{c,b,a}的长度为3,这个集合的子集就有8个. 这句话看起来很简单,但同时也隐含着高深的哲理.其实一个集合的所有集合,和2^该集合的长度这个数字有关.比如上面的例子,{c,b,a}的长度为3,则可以用0-7表示其所有子集.如下所示,改数字所对应的位置为1,则说明我需要这个数字形成子集.从0-7的二进制表示,刚好代表完,一个长度为3,子集个数为8的所有子集. 0(000)

  • 浅谈java中的移动位运算:,>>>

    1. 概念 << 左移运算符,左移是在后面补0, num << 1,相当于num乘以2 >> 右移运算符, 右移是在前面补1或0,num >> 1, 相当于num除以2 >>> 无符号右移,是在前面补0, 忽略符号位,空位都以0补齐 另外, 不论是左右还是右移32位,相当于不移动,还是原值. 实际上 在java虚拟机执行这句代码的时候如下这样执行的: 5>>(n%32)--->结果 你这里n=32 :所以5>>

  • Java利用移位运算将int型分解成四个byte型的方法

    package 移位运算; public class 移位运算 { public static void main(String[] args) { //00000111 01011011 11001101 00010101 int n=123456789; //n为需要进行移位处理的32位int型初始值 byte[] a =chai(n); for (int i = 0; i < a.length; i++) { byte b=a[i]; System.out.print(b+" &qu

  • 深入学习java位运算的基础知识

    相信大家和我一样,接触java这门语言的时候就听过java位运算的鼎鼎大名,当然也仅限于听说过.日常开发过程中使用过么?使用位运算的好处是什么? 想要真正理解java位运算,首先要搞清楚,这个"位"代表的含义. 一切的起源:二进制 位:二进制位,简称"位".是二进制记数系统中表示小于2的整数的符号,一般用1或 0表示,是具有相等概率的两种状态中的一种.二进制位的位数可表示一个机器字的字长,一个二进制位包含的信息量称为一比特(bit). 举个栗子: int占4个字节(

  • JAVA位运算的知识点总结

    一.在计算机中数据是如何进行计算的? 1.1:java中的byte型数据取值范围 我们最开始学习java的时候知道,byte类型的数据占了8个bit位,每个位上或0或1,左边第一位表示符号位,符号位如果为1表示负数,为0则表示正数,因此要推算byte的取值范围,只需要让数值位每一位上都等于1即可. 我们来用我们的常规思维来分析下byte类型的取值范围: 图1 如果按照这种思路来推算,七个1的二进制数转换为十进制是127,算上符号位,取值范围应为:-127~+127,但事实上我们知道,byte的取

  • java二维数组基础知识详解

    目录 1. 查找 2. 顺序查找 3. 二分查找 4. 多维数组 4.1 二维数组 175 4.2 二维数组细节 5. 二维数组的使用方式 176 6. 二维数组的动态初始化 1.先声明:类型 数组名[][]; 再定义(开辟空间) 数组名 = new 类型[大小][大小] 2.动态初始化-列数不确定 178 7. 二维数组的静态初始化 179 8. 二维数组练习 180 8.1 int arr[][]={{4,6},{1,4,5,7},{-2}}; 遍历该二维数组,并得到和 1. 查找 1) 顺

  • Java Swing最详细基础知识总结

    一.简介 Swing 是一个为Java设计的GUI工具包. Swing是JAVA基础类的一部分. Swing包括了图形用户界面(GUI)器件如:文本框,按钮,分隔窗格和表. Swing提供许多比AWT更好的屏幕显示元素.它们用纯Java写成,所以同Java本身一样可以跨平台运行,这一点不像AWT.它们是JFC的一部分.它们支持可更换的面板和主题(各种操作系统默认的特有主题),然而不是真的使用原生平台提供的设备,而是仅仅在表面上模仿它们.这意味着你可以在任意平台上使用JAVA支持的任意面板.轻量级

  • Kotlin基础学习之位运算

    什么是位运算? 程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算说穿了,就是直接对整数在内存中的二进制位进行操作.比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算.举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理): 110 AND 1011 ---- 0010 –> 2 由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度

  • 学习shell脚本之前的基础知识[图文]

    日常的linux系统管理工作中必不可少的就是shell脚本,如果不会写shell脚本,那么你就不算一个合格的管理员.目前很多单位在招聘linux系统管理员时,shell脚本的编写是必考的项目.有的单位甚至用shell脚本的编写能力来衡量这个linux系统管理员的经验是否丰富.笔者讲这些的目的只有一个,那就是让你认真对待shell脚本,从一开始就要把基础知识掌握牢固,然后要不断的练习,只要你shell脚本写的好,相信你的linux求职路就会轻松的多.笔者在这一章中并不会多么详细的介绍shell脚本

  • java位运算加密示例

    创建一个类,通过位运算中的"^"异或运算符把字符串与一个指定的值进行异或运算,从而改变字符串每个字符的值,这样就可以得到一个加密后的字符串.当把加密后的字符串作为程序输入内容,再与那个指定的值进行异或运算,实现把加密后的字符串还原为原有字符串的值. 复制代码 代码如下: import java.util.Scanner;public class Example {    public static void main(String[] args) {        Scanner sc

  • Java中的数组基础知识学习教程

    数字 通常情况下,当我们处理数字时,使用原始数据类型,如 byte,int,long,double 等. 示例 int i = 5000; float gpa = 13.65; byte mask = 0xaf; 然而,在开发中,我们会遇到需要使用对象而不是原始数据类型的情况.为了实现这个, Java 为每个原始数据类型提供包装类. 所有的包装类 (Integer, Long, Byte, Double, Float, Short) 是抽象类 Number 的子类. 这种包装是由编译器处理,这个

  • 一些java二进制的相关基础知识

    说明 任何东西都有规范,提到JAVA就会提到2个规范,JAVA语言规范.JVM规范.JAVA语言规范主要定义JAVA的语法.变量.类型.文法等等,JVM规范主要定义Class文件类型.运行时数据.帧栈.虚拟机的启动.虚拟机的指令集等等. JAVA语言规范主要定义什么是JAVA语言. JVM规范主要定义JVM内部实现,二进制class文件和JVM指令集等. 规范中数字的内部表示和存储 JAVA八种基本数据类型: 整形:byte,short,int,long 浮点型:float,double 布尔型

随机推荐