说明:本示例(下同)中所有的字面值使用的都是十进制表示的,理解的时

简介: 说明:1、本示例(下同)中所有的字面值使用的都是十进制表示的,理解的时候请用二进制思维去理解;2、关于负数之间的位运算本文章统一不做讲述|:按位或操作规则:同为

正文提及位运算,对绝大多数Ja程序员来说,是一种既熟悉又陌生的感觉。

熟悉是因为你在学JaSE时肯定学过,并且在看一些开源框架(特别是JDK源码)时都能看到它的身影;陌生是因为大概率我们不会去使用它。

...小贴士:一般来说,程序让人看懂远比被机器看懂来得更重要些位运算它在low-level的语言里使用得比较多,但是对于Ja这种高级语言它就很少被提及了。

虽然我们使用得很少但Ja也是支持的,毕竟很多时候使用位运算才是最佳实践。

位运算在日常开发中使用得较少,但是巧妙的使用位运算可以大量减少运行开销,优化算法。

二进制在了解什么是位运算之前,十分有必要先科普下二进制的概念。

小贴士:半导体开代表1,关代表0,这也就是CPU计算的最底层原理先看一个例子:12求 1011(二进制)+ 11(二进制) 的和?

结果为:1110(二进制)二进制理解起来非常非常的简单,比10进制简单多了。

有或者往深了继续思考:如何转为八进制、十六进制、三十二进制......进制转换并非本文所想讲述的内容,请有兴趣者自行度娘。

二进制与编码这个虽然和本文内容关联系并不是很大,但顺带捞一捞,毕竟编码问题在开发中还是比较常见的。

计算机能识别的只有1和0,也就是二进制,1和0可以表达出全世界的所有文字和语言符号。

字符编码强行将每一个字符对应一个十进制数字(请注意字符和数字的区别,比如0字符对应的十进制数字是48),再将十进制数字转换成计算机理解的二进制,而计算机读到这些1和0之后就会显示出对应的文字或符号。

ascll编码是美国标准信息交换码的英文缩写,包含了常用的字符,如阿拉伯数字,英文字母和一些打印符号共255个(一般说成共128个字符问题也不大)UTF-8:一套以 8 位为一个编码单位的可变长编码,会将一个码位(Unicode)编码为1到4个字节(英文1字节,大部分汉字3字节)。

Ja中的二进制在Ja7版本以前,Ja是不支持直接书写除十进制以外的其它进制字面量。

但这在Ja7以及以后版本就允许了:二进制:前置0b/0B八进制:前置0十进制:默认的,无需前置十六进制:前置0x/0X结果程序,输出:说明:System.out.println()会先自动转为10进制后再输出的;toBinaryString()表示转换为二进制进行字符串进行输出。

运行程序,输出:如何证明Long是64位的?

小贴士:这算是一道经典面试题,至少我提问过多次~有个最简单的方法:拿到Long类型的最大值,用2进制表示转换成字符串看看长度就行了,代码如下:运行程序,输出:说明:在计算机中,负数以其正值的补码的形式表达。

因此,用同样的方法你可以自行证明Integer类型是32位的(占4个字节)。

Ja中的位运算Ja语言支持的位运算符还是非常多的,列出如下:&:按位与|:按位或~:按位非^:按位异或<<:左位移运算符>>:右位移运算符>>>:无符号右移运算符除~以 外,其余均为二元运算符,操作的数据只能是整型(长短均可)或者char字符型。

小贴士:为了便于理解,字面量例子我就都使用二进制表示了,使用十进制(任何进制)不影响运算结果简单运算简单运算,顾名思义,一次只用一个运算符。

说明:1、本示例(下同)中所有的字面值使用的都是十进制表示的,理解的时候请用二进制思维去理解;2、关于负数之间的位运算本文章统一不做讲述|:按位或操作规则:同为0则0,否则为1。

仅当两个操作数都为0时,输出的结果才为0。

全部的0置为1,1置为0。

小贴士:请务必注意是全部的,别忽略了正数前面的那些0哦~^:按位异或操作规则:相同为0,不同为1。

操作数不同时(1遇上0,0遇上1)对应的输出结果才为1,否则为0。

负数右移:右移用得也比较多,也比较理解:操作其实就是把二进制数右边的N位直接砍掉,然后正数右移高位补0,负数右移高位补1。

>>>:无符号右移注意:没有无符号左移,并没有<<<这个符号的它和>>有符号右移的区别是:无论是正数还是负数,高位通通补0。

所以说对于正数而言,没有区别;那么看看对于负数的表现:我特意把>>的结果放上面了,方便你对比。

因为高位补的是0,所以就没有显示啦,但是你心里应该清楚是怎么回事。

复合运算广义上的复合运算指的是多个运算嵌套起来,通常这些运算都是同种类型的。

这里指的复合运算指的就是和=号一起来使用,类似于+= -=。

以&与运算为例,其它类同:复习一下&的运算规则是:同为1则1,否则为0。

位运算使用场景示例位运算除了高效的特点,还有一个特点在应用场景下不容忽视:计算的可逆性。

最典型的如HashMap:位运算有很多优良特性,能够在线性增长的数据中起到作用。

且对于一些运算,位运算是最直接、最简便的方法。

像这种小小case用十进制加上>/<比较符当然可以做,但用位运算符处理来得更加直接(效率最高):运行程序,输出:falsetrueint类型共32bit,右移31位那么就只剩下1个符号位了(因为是带符号右移动,所以正数剩0负数剩1),再对两个符号位做^异或操作结果为0就表明二者一致。

判断一个数的奇偶性在十进制数中可以通过和2取余来做,对于位运算有一个更为高效的方式:为何&1能判断基偶性?

因为在二进制下偶数的末位肯定是0,奇数的最低位肯定是1。

而二进制的1它的前31位均为0,所以在和其它数字的前31位与运算后肯定所有位数都是0(无论是1&0还是0&0结果都是0),那么唯一区别就是看最低位和1进行与运算的结果喽:结果为1表示奇数,反则结果为0就表示偶数。

交换两个数的值(不借助第三方变量)这是一个很古老的面试题了,交换A和B的值。

本题如果没有括号里那几个字,是一道大家都会的题目,可以这么来解:运行程序,输出(成功交换):3-------55-------3使用这种方式最大的好处是:容易理解。

最大的坏处是:a+b,可能会超出int型的最大范围,造成精度丢失导致错误,造成非常隐蔽ug。

所以若你这样运用在生产环境的话,是有比较大的安全隐患的。

所以本文介绍一种安全的替代方式,借助位运算的可逆性来完成操作:运行程序,输出(成功完成交换):2147483647-------21474836372147483637-------2147483647由于全文都没有对a/b做加法运算,因此不能出现溢出现象,所以是安全的。

这种做法的核心原理依据是:位运算的可逆性,使用异或来达成目的。

位运算用在数据库字段上(重要)这个使用case是极具实际应用意义的,因为在生产上我以用过多次,感觉不是一般的好。

业务系统中数据库设计的尴尬现象:通常我们的数据表中可能会包含各种状态属性, 例如 blog表中,我们需要有字段表示其是否公开,是否有设置密码,是否被管理员,是否被置顶等等。

也会遇到在后期运维中,策划要求增加新的功能而造成你需要增加新的字段,这样会造成后期的维护困难,字段过多,索引增大的情况, 这时使用位运算就可以巧妙的解决。

举个例子:我们在网站上进行认证授权的时候,一般支持多种授权方式,比如:个人认证 0001 -> 1邮箱认证 0010 -> 2微信认证 0100 -> 4超管认证 1000 -> 8这样我们就可以使用1111这四位来表达各自位置的认证与否。

要查询通过微信认证的条件语句如下:select * from xxx wherestatus = status & 4;要查询既通过了个人认证,又通过了微信认证的:select * from xxx wherestatus = status & 5;当然你也可能有排序需求,形如这样:select * from xxx orderbystatus & 1desc这种case和每个人都熟悉的Linux权限控制一样,它就是使用位运算来控制的:权限分为 r 读, w 写, x 执行,其中它们的权值分别为4,2,1,你可以随意组合授权。

比如chomd 7,即7=4+2+1表明这个用户具有rwx权限,注意事项需要你的DB存储支持位运算,比如MySql是支持的请确保你的字段类型不是char字符类型,而应该是数字类型这种方式它会导致索引失效,但是一般情况下状态值是不需要索引的具体业务具体分析,别一味地为了show而用,若用错了容易遭对有喷的流水号生成器(订单号生成器)生成订单流水号,当然这其实这并不是一个很难的功能,最直接的方式就是日期+主机Id+随机字符串来拼接一个流水号,甚至看到非常多的地方直接使用UUID,当然这是非常不推荐的。

UUID是字符串,太长,无序,不能承载有效的信息从而不能给定位问题有效帮助,因此一般属于备选方案今天学了位运算,有个我认为比较优雅方式来实现。

什么叫优雅:可以参考淘宝、京东的订单号,看似有规律,实则没规律:不想把相关信息直接暴露出去。

使用icInteger可提高并发量,降低了(这是不使用UUID另一重要原因,因为数字的效率比字符串高)实现原理简介此流水号构成:日期+Long类型的值 组成的一个一长串数字,形如2020010419492195304210432。

很显然前面是日期数据,后面的一长串就蕴含了不少的含义:当前秒数、商家ID(也可以是你其余的业务数据)、机器ID、一串随机码等等。

占5位第三部分为:shortParam,表示用户自定义的短参数。

最大值定为30,肯定也是足够用了的。

绝大多数足够用了,占30位第五部分:剩余的位数交给随机数,随机生成一个数,占满剩余位数。

一般至少有15位剩余(此部分位数是浮动的),所以能支持2的15次方的并发,也是足够用了的最后,在上面的long值前面加上日期时间(年月日时分秒)这是A哥编写的一个基于位运算实现的流水号生成工具,已用于生产环境。

总结位运算在工程的角度里缺点还是蛮多的,在实际工作中,如果只是为了数字的计算,是不建议使用位运算符的,只有一些比较特殊的场景,使用位运算去做会给你柳暗花明的感觉,如:


以上是文章"

说明:本示例(下同)中所有的字面值使用的都是十进制表示的,理解的时

"的内容,欢迎阅读集优教育网的其它文章