【方向盘】Java二进制和位运算,这一万字准能喂饱你(上)

简介: 【方向盘】Java二进制和位运算,这一万字准能喂饱你(上)

前言


你好,我是YourBatman。


本号正在连载Jackson深度解析系列,虽然目前还只讲到了其流式API层面,但已接触到其多个Feature特征。更为重要的是我在文章里赞其设计精妙,处理优雅,因此就有小伙伴私信给我问这样的话:


image.png

题外话:Jackson这个话题本就非常小众,看着阅读量我自己都快没信心写下去。但自己说过的话就是欠下的债,熬夜也得把承诺的付费内容给公开完了,毕竟还有那么几个人在白嫖不是😄。


话外音:以后闷头做事,少吹牛逼┭┮﹏┭┮


虽然小众,竟然还有想深入了解一波的小伙伴,确实让我为之振奋了那么三秒。既然如此那就干吧,本文就先行来认识认识Java中的位运算。位运算在Java中很少被使用,那么为何Jackson里爱不释手呢?一切就为两字:性能/高效。用计算机能直接看懂的语言跟它打交道,你说快不快,不用多想嘛。


image.png


正文


提及位运算,对绝大多数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个字节)。



相关文章
|
4月前
|
编解码 算法 Java
Java中的位运算详解
Java中的位运算详解
|
6月前
|
Java
Java中整数(负数)的二进制表示
Java中整数(负数)的二进制表示
|
6月前
|
Java
Java打印二进制
Java打印二进制
119 0
|
6月前
|
Java
Java中将一个数转化为二进制
Java中将一个数转化为二进制
71 0
|
5月前
|
算法 Java Go
【经典算法】LeetCode 67. 二进制求和(Java/C/Python3/Golang实现含注释说明,Easy)
【经典算法】LeetCode 67. 二进制求和(Java/C/Python3/Golang实现含注释说明,Easy)
66 2
|
5月前
|
Java
剑指offer_3_前n个数字二进制形式中1的个数(java)
剑指offer_3_前n个数字二进制形式中1的个数(java)
|
5月前
|
Java
剑指offer_2_二进制加法(java)
剑指offer_2_二进制加法(java)
|
5月前
|
算法 Java
Java数据结构与算法:位运算之位移操作
Java数据结构与算法:位运算之位移操作
|
5月前
|
算法 Java
Java数据结构与算法:位运算之与、或、异或运算
Java数据结构与算法:位运算之与、或、异或运算
|
6月前
|
算法 Java C++
【Java 刷题记录】位运算
【Java 刷题记录】位运算
55 2
下一篇
无影云桌面