前言
你好,我是YourBatman。
本号正在连载Jackson深度解析系列,虽然目前还只讲到了其流式API层面,但已接触到其多个Feature
特征。更为重要的是我在文章里赞其设计精妙,处理优雅,因此就有小伙伴私信给我问这样的话:
题外话:Jackson这个话题本就非常小众,看着阅读量我自己都快没信心写下去。但自己说过的话就是欠下的债,熬夜也得把承诺的付费内容给公开完了,毕竟还有那么几个人在白嫖不是😄。
话外音:以后闷头做事,少吹牛逼┭┮﹏┭┮
虽然小众,竟然还有想深入了解一波的小伙伴,确实让我为之振奋了那么三秒。既然如此那就干吧,本文就先行来认识认识Java中的位运算。位运算在Java中很少被使用,那么为何Jackson里爱不释手呢?一切就为两字:性能/高效。用计算机能直接看懂的语言跟它打交道,你说快不快,不用多想嘛。
正文
提及位运算,对绝大多数Java程序员来说,是一种既熟悉又陌生的感觉。熟悉是因为你在学JavaSE时肯定学过,并且在看一些开源框架(特别是JDK源码)时都能看到它的身影;陌生是因为大概率我们不会去使用它。当然,不能“流行”起来是有原因的:不好理解,不符合人类的思维,阅读性差……
小贴士:一般来说,程序让人看懂远比被机器看懂来得更重要些
位运算它在low-level的语言里使用得比较多,但是对于Java这种高级语言它就很少被提及了。虽然我们使用得很少但Java也是支持的,毕竟很多时候使用位运算才是最佳实践。
位运算在日常开发中使用得较少,但是巧妙的使用位运算可以大量减少运行开销,优化算法。一条语句可能对代码没什么影响,但是在高重复,大数据量的情况下将会节省很多开销。
二进制
在了解什么是位运算之前,十分有必要先科普下二进制的概念。
二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是逢二进一,借位规则是借一当二。因为它只使用0、1两个数字符号,非常简单方便,易于用电子方式实现。
小贴士:半导体开代表1,关代表0,这也就是CPU计算的最底层原理😄
先看一个例子:
求 1011(二进制)+ 11(二进制) 的和? 结果为:1110(二进制)
二进制理解起来非常非常的简单,比10进制简单多了。你可能还会思考二进制怎么和十进制互转呢?毕竟1110这个也看不到啊。有或者往深了继续思考:如何转为八进制、十六进制、三十二进制…进制转换并非本文所想讲述的内容,请有兴趣者自行度娘。
二进制与编码
这个虽然和本文内容关联系并不是很大,但顺带捞一捞,毕竟编码问题在开发中还是比较常见的。
计算机能识别的只有1和0,也就是二进制,1和0可以表达出全世界的所有文字和语言符号。那如何表达文字和符号呢?这就涉及到字符编码了。字符编码强行将每一个字符对应一个十进制数字(请注意字符和数字的区别,比如0字符对应的十进制数字是48),再将十进制数字转换成计算机理解的二进制,而计算机读到这些1和0之后就会显示出对应的文字或符号。
- 一般对英文字符而言,一个字节表示一个字符,但是对汉字而言,由于低位的编码已经被使用(早期计算机并不支持中文,因此为了扩展支持,唯一的办法就是采用更多的字节数)只好向高位扩展
- 字符集编码的范围utf-8>gbk>iso-8859-1(latin1)>ascll。ascll编码是美国标准信息交换码的英文缩写,包含了常用的字符,如阿拉伯数字,英文字母和一些打印符号共255个(一般说成共128个字符问题也不大)
UTF-8:一套以 8 位为一个编码单位的可变长编码,会将一个码位(Unicode)编码为1到4个字节(英文1字节,大部分汉字3字节)。
Java中的二进制
在Java7版本以前,Java是不支持直接书写除十进制以外的其它进制字面量。但这在Java7以及以后版本就允许了:
- 二进制:前置0b/0B
- 八进制:前置0
- 十进制:默认的,无需前置
- 十六进制:前置0x/0X
@Test public void test1() { //二进制 int i = 0B101; System.out.println(i); //5 System.out.println(Integer.toBinaryString(i)); //八进制 i = 0101; System.out.println(i); //65 System.out.println(Integer.toBinaryString(i)); //十进制 i = 101; System.out.println(i); //101 System.out.println(Integer.toBinaryString(i)); //十六进制 i = 0x101; System.out.println(i); //257 System.out.println(Integer.toBinaryString(i)); }
结果程序,输出:
5 101 65 1000001 101 1100101 257 100000001
说明:System.out.println()
会先自动转为10进制后再输出的;toBinaryString()
表示转换为二进制进行字符串进行输出。
便捷的进制转换API
JDK自1.0
开始便提供了非常便捷的进制转换的API,这在我们有需要时非常有用。
@Test public void test2() { int i = 192; System.out.println("---------------------------------"); System.out.println("十进制转二进制:" + Integer.toBinaryString(i)); //11000000 System.out.println("十进制转八进制:" + Integer.toOctalString(i)); //300 System.out.println("十进制转十六进制:" + Integer.toHexString(i)); //c0 System.out.println("---------------------------------"); // 统一利用的为Integer的valueOf()方法,parseInt方法也是ok的 System.out.println("二进制转十进制:" + Integer.valueOf("11000000", 2).toString()); //192 System.out.println("八进制转十进制:" + Integer.valueOf("300", 8).toString()); //192 System.out.println("十六进制转十进制:" + Integer.valueOf("c0", 16).toString()); //192 System.out.println("---------------------------------"); }
运行程序,输出:
--------------------------------- 十进制转二进制:11000000 十进制转八进制:300 十进制转十六进制:c0 --------------------------------- 二进制转十进制:192 八进制转十进制:192 十六进制转十进制:192 ---------------------------------
如何证明Long是64位的?
我相信每个Javaer都知道Java中的Long类型占8个字节(64位),那如何证明呢?
小贴士:这算是一道经典面试题,至少我提问过多次~
有个最简单的方法:拿到Long类型的最大值,用2进制表示转换成字符串看看长度就行了,代码如下:
@Test public void test3() { long l = 100L; //如果不是最大值 前面都是0 输出的时候就不会有那么长了(所以下面使用最大/最小值示例) System.out.println(Long.toBinaryString(l)); //1100100 System.out.println(Long.toBinaryString(l).length()); //7 System.out.println("---------------------------------------"); l = Long.MAX_VALUE; // 2的63次方 - 1 //正数长度为63为(首位为符号位,0代表正数,省略了所以长度是63) //111111111111111111111111111111111111111111111111111111111111111 System.out.println(Long.toBinaryString(l)); System.out.println(Long.toBinaryString(l).length()); //63 System.out.println("---------------------------------------"); l = Long.MIN_VALUE; // -2的63次方 //负数长度为64位(首位为符号位,1代表负数) //1000000000000000000000000000000000000000000000000000000000000000 System.out.println(Long.toBinaryString(l)); System.out.println(Long.toBinaryString(l).length()); //64 }
运行程序,输出:
1100100 7 --------------------------------------- 111111111111111111111111111111111111111111111111111111111111111 63 --------------------------------------- 1000000000000000000000000000000000000000000000000000000000000000 64
说明:在计算机中,负数以其正值的补码的形式表达。因此,用同样的方法你可以自行证明Integer类型是32位的(占4个字节)。