byte数组转blob类型_从char、byte互转,到键盘发展史。史上最深刻的字符集知识总结...

作者 | Java圣斗士

a5ea924874c46927150540be29306238.png

文章稍长,赶时间的小伙伴可以直接跳转到“结语”部分。

在 Java 语言的学习之初,每个人都会接触到 8 种基本类型,其中有两个基本类型:charbyte ,在很多情况下是需要经常进行转换的,包括 NIO 的各种应用场景。例如消息指令的传输,就需要将一个 String 转为 byte[] 数组。

其实一直以来,我都有一个疑问,或者说对这两者之间的转化比较模糊。来看看下面的问题:

String name = "abcde";System.out.println(name.length());System.out.println(name.getBytes().length);

我们知道,String 的本质是 char[] 数组,那么第一条输出语句实际上输出的就是 char[] 数组的长度,而我们通过 getBytes()方法获取到了一个 byte[] 数组,长度又是多少呢?

输出结果:

3ffb47a288899587f17a4906369581f2.png

可以看到答案依然是 5。 Why?

Java 中 char 类型的变量占 2 个字节,byte 是 1 个字节,为什么一个长度为5的char[] 数组转成 byte[] 之后长度却依然相同,而不是 10 呢?

一、再谈 Unicode 与 ASCII 编码

在所有的字符集中,最著名的可能要数 ASCII 字符集。它是“美国标准信息交换代码”的缩写。

ASCII 共记录了128个字符。包括英文字母大小写阿拉伯数字、以及各种标点符号等等。

445017bd4ca22d31d54faa47db8ccbc0.png

它的第一次发表是在 1967 年,可有些小伙伴不禁疑惑了,以前学习 Java 或者是计算机导论的时候也有提到过 ASCII 码这东西,但是它究竟为什么被发明出来呢?

原来啊,早在ASCII 发明之前,键盘就已经被发明出来了。

“what ? 你这跨度有点大!咋就突然说起键盘来了?”

d473fcac6bf0f0d5aee14a9ac06cc56d.png

没错,ASCII码的发明和键盘有着千丝万缕的联系

我们知道,键盘实际上是由打字机演变而来,随着计算机设备的逐渐成型,人们通过电路系统将打字机进行重组,变成了一个可供人们向计算机输入内容的设备——keyboard,键盘。

但是这个时候,键盘的制作还没有一个标准的规范,很多能够生产键盘的厂商,可能都会有自己的一套标准。

我们知道计算机内部是通过高低电平来区分1 和 0 的,所以,不论我们输入什么,都必须将其转换为能够让计算机识别的一串0101...

比如说英文字母 A,有的键盘制造商可以把它转成了 1111,而有的转成了1100,那不同的键盘可能都无法通用了,与计算机 CPU 的交互也形成了混乱。怎么办?

聪明的发明家们想出了一个办法,制定一套标准信息交换的编码表,将英语中能用到的所有可打印的字符和一些制表符,如a、B、c、+、@等等,全部规定它们对应的0101串是怎样的,这些01串是不会重复的,有了这个对照表,人们就可以制造出符合同种标准的键盘。

这就是 ASCII 码的诞生。

但是,由于当时的ASCII 的诞生只考虑到了英语和一些西欧国家的语言,使用 7 位01串来表示一个英文字符,在计算机内存中就是通过一个字节(8位)来存储。因此,所能表示的全部字符(包括一些无法打印的制表符)加起来总共也才128个

随着计算机技术的全球化,人们逐渐意识到:

“这个表太小了!”

cf6ce627aeb15fd1e33f3282effee7cf.png

现在,我们知道, ASCII 码的本质就是建立 “01010101”串英文字符的一一对应关系。但是对于很多象形文字或者非拉丁语系的语言来说,语言中单位最小的字符有成千上万,就以汉语来说。“我”、“爱”、“中”、“国”这四个字符就是独一无二的,必须要找到能够表示它们的 01 串!

计算机中的位数是限制取值范围的关键因素,8位的字节最大能够表示出256个符号,怎么办?

还是聪明的发明家们,他们想出的办法是——再加一个字节!

于是 Unicode 诞生了。

Unicode 编码采用 2 个字节的表示方式,总共16位,从16个0,到16个1,所能表示的数字总共有 65535个。

728968571e1229f11d752ab1a9a9d6c4.png

这样,地球上所有语言的字符就都能够表示了!

而且,Unicode也包含了 ASCII,因为Unicode编码表的前128个,就是ASCII 表中的那128个字符,只不过通过补0的方式,变为了两个字节。

在Unicode 编码诞生之后,各大计算机相关厂商纷纷对其进行了支持,而Java 语言本身也是由Unicode 编码的,因此很多中文内容是可以被 Java 语言识别的。

二、再谈 char 与 byte 转化

既然我们已经了解了Unicode 编码和ASCII 码的区别,那我们再回到最初的问题,为什么一个char[] 数组,通过 getBytes()方法获取到 byte[] 数组之后,长度依然不变?

或者考虑这样的场景:

byte a = (byte) '中';byte a1 = '-';byte b = 'a';System.out.println(a);System.out.println(a1);System.out.println(b);

输出结果是:

e86869f09e4c35f608bb60c4013eaa11.png

我们看到,a 和 a1 输出了同样的结果。这就是因为汉字无法用一个字节的 byte 来表示所导致的结果。

a1 所表示的字符,是标准ASCII 码中规定的字符。在刚刚的讨论中,我们已经知道,这些字符都可以通过一个字节来表示,因此即便Java是由Unicode 编码,像这样的字符在char、byte,甚至是int 之间都是可以直接自动转化的,而不需要强转。

因此,前面最开始的“abcde”字符串,通过 getBytes()获取到的byte[] 数组长度依然是 5,就是这个原因。但如果是字符串“祖国我爱你”这 5 个字符,恐怕结果就无法预测了:

dc877ba3fa4ef9253126d3f089887a3a.png

果然,长度不同,可是为什么是15呢?

这个问题就需要涉及到另一种衍生的编码格式UTF-8,简单的说,它是一种更加先进的Unicode编码方式,可以通过变长字节表示一个字符。

我们在前面比较 Unicode 和 ASCII的时候,提到了前者为了表示世界上更多语言的字符,无奈地扩充了字节个数,使用2个字节来表示,而后者在制定之际只考虑到了英语,只用了1个字节来表示。

虽然Unicode标准编码可以解决无法表示象形文字的问题,但是依然存在缺点,就是即便在不需要2个字节表示字符的情况下,如ASCII 码表中的所有字符,也都需要扩展到2个字节以符合Unicode编码,很明显,内存扩大了一倍,这在很多根本不需要那么多复杂字符的北美和西欧国家是不情愿的。

为了解决内存的问题,变长的UTF-8编码诞生了

它可以用1到4个字节来表示字符,也就是说,有时候1个字节能表示的,它就会用1个字节来表示,有时候需要2个字节的字符,就用2个字节,依此类推。

这也就解释了为什么上面的程序输出结果的不同。

三、结语

上面的长篇大论实际一直在描述ASCII、Unicode、UTF-8的一些由来和区别。

其实简单的描述它们的本质,就是一些语言符号如‘a’、‘b’、‘c’、‘汉’、‘字’,与01串的对照表。

ASCII只考虑到了英语,因此无法推广到全世界。

Unicode通过增加一个字节的方式,解决了所有人类语言的字符都能够表示的问题。

UTF-8 本身也是Unicode编码,不过是可以进行变长表示,按需分配字节,节省了内存。

四、写作心得后记

在写这篇文章之前,我的思路并不是这么清晰的,问题的矛头是getBytes(),它得到的byte[]数组长度和char[]长度相同使我产生了疑惑。

于是我开始搜索:为什么char是两个字节,转成byte之后长度不变,等等。

随着答案的逐渐清晰,我也慢慢弄懂了:char、byte的转化方式,再到Unicode 与 ASCII编码,甚至了解了键盘的发展历史,知道了原来键盘的发展也伴随着层层革命,甚至还有“劣势产品驱逐优势产品”的典型现象。

可以说从一个简单的小问题,不仅巩固了以往的编码知识,又学到了这么多历史人文,真的是受益匪浅,堪称令我印象最深刻的编码知识总结了。因此写出来分享给大家。

文章其实挺长,如果你能坚持看到这里,真的非常感谢,也非常感谢各位小伙伴的支持和鼓励。

未来只要是有任何工作或者学习上的心得,都会分享出来,一起讨论。

往期精彩:

对话式情景剖析,String被final修饰的真正原因!一篇足矣

注释有多重要,看了这一篇你还不重新思考一下吗?

被美制裁的华为如今怎么样?华为董事长:上半年收入4013亿

情景对话,一篇带你参悟单例模式

学编程,一定要找到适合自己的学习方法

关注Java圣斗士,收藏有深度、有想法的经典好文。有人说“深度决定高度”,做技术,就应该不断深入。期待与你一同站在巨人的肩膀上,俯瞰着世界!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值