彻底理解从二进制到序列化、跨平台

首先我们先来看下b(英文:bit,中文:比特/位)、B(英文:Byte,中文:字节) 和字长的定义和换算。

换算 :

1B (Byte) = 8b (bit)

定义:

 :二进制位简称“位”,是二进制记数系统中表示小于2的整数的符号,一般用1或 0表示,是具有相等概率的两种状态中的一种,二进制位的位数可表示一个机器字的字长,一个二进制位包含的信息量称为一比特(BIT,Binary digit)。

字节 :在计算机中,把 8 位聚在一起的二进制数称为一个字节(byte),即 1字节(byte)= 8 位(bit)。

 :现代计算机的机器字长一般都是8位的整数倍,如16位、32位、64位和128位等,即字长分别为2个字节、4个字节、16个字节和16个字节,所以也可以用“字节”来表示机器字长;对于64位操作系统的CPU来说,字长就是64位,而字节是固定8位,所以在64位计算机中,一个字长所占的字节数为8。

首先大家对于一个字节8位有没有疑惑?为什么不是6位、7位或10位呢?可能大部分的人都觉得这不是理所当然的么,就和一日三餐一样,还需要怀疑吗?那些看起来理所当然的事情,其实远不是我们想象的那样简单,经常思考和探究这些看似常识性的问题,有助于我们养成"批判性思维"。

大家在大学期间应该都学习过高等数学中的极限、微积分,单单从概念的角度去理解极限、微积分,想必大多数同学都感觉很头疼,那么当我们将极限、微积分与伽利略、牛顿-莱布尼茨等重要人物联系起来,引入了历史这一重要因素,一切将会变得更加灵性,鲜活;再对极限、微积分的理解将会更加深刻。我们一定要承认,任何知识、应用的产生一定是有需求、有场景、有客观因素存在的,不可能无中生有就蹦出来这样一个概念。

了解历史就会有全局观,那么位、字节、字的理解我们同样以历史的角度出发来为大家解惑,以下我引用了他人的一个回答再加上自己的看法。

---------------------------------------引用开始--------------------------------------------

“所谓字节,原意就是用来表示一个完整的字符的。最初的计算机性能和存储容量都比较差,所以普遍采用4位BCD编码(这个编码出现比计算机还早,最早是用在打孔卡上的)。BCD编码表示数字还可以,但表示字母或符号就很不好用,需要用多个编码来表示。后来又演变出6位的BCD编码(BCDIC),以及至今仍在广泛使用的7位ASCII编码。不过最终决定字节大小的,是大名鼎鼎的System/360。当时IBM为System/360设计了一套8位EBCDIC编码,涵盖了数字、大小写字母和大部分常用符号,同时又兼容广泛用于打孔卡的6位BCDIC编码。System/360很成功,也奠定了字符存储单位采用8位长度的基础,这就是1字节=8位的由来。”

各位看官看到这里是否觉得IBM System/360很牛,是否想起了大牛弗雷德里克·布鲁克斯和他的《人月神话》,那么继续往下追寻,

弗雷德里克·布鲁克斯在哈佛大学取得博士学位以后,进入IBM公司设立在纽约波凯普茜(Poughkeepsie,NewYork)的实验室当工程师。这个实验室从20世纪50年代到80年代一直是IBM公司开发计算机的中心。布鲁克斯在这里参加了Harvest和STRETCH计算机的开发,任体系结构设计师。这两个型号的计算机都引入了一些新技术,在20世纪50年代后期至60年代初期有很大影响,尤其是STRETCH计算机,当前已成标准的8个二进制位的“字节”(byte)就是由布赫霍尔兹(WernerBuchholz)提出,在STRETCH上首次采用的。

原文链接:为什么1个字节(Byte)等8位(Bit)?_书当如酒-CSDN博客_一个字节8位

---------------------------------------引用结束--------------------------------------------

我们做任何一件事情,当然是在能满足需求的前提下,花费的代价越小越好,大家应该要达成这样一个共识。那么从上面最直接的我们就可以知道,最初使用这些概念的是美国人,那么只需要满足美国人的需求就行,"需要存储的信息越来越多"意思着需要更高效的存储方式,也就是在精度不低于要求的情况下,用更少的数据单元存储更多的数据,而8位是刚刚好能满足的。

1、0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。

2、32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字。

3、65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

那么在英语中,用128(2^7)个符号编码便可以表示所有,即7位就能编码美国人会用到的所有字符。

到这时就出现了字节的概念,那为什么字节要用8位表示呢?因为除了英文字母,世界上还有很多的语言字符,光汉字就有10万多个。聪明的美国人就在前面加了一个bit作为扩充位。当这一最高符号位为1时,表示扩展字符集,此时系统会将该字节和其下一字节合并解释,并根据当前使用的字符集显示正确的文字。

而当最高位为0时,就是我们经常说的ASCII编码,ASCII 编码是最简单的西文编码方案。GB2312、GBK、GB18030 是汉字字符编码方案的国家标准,这些标准下,一个汉字要占用两个字节。

到这里我们可以肯定的是最少要用8位来表示一个字节了。那有些人可能会问了,设置这么多编码集多麻烦,直接16位表示一个字节不就简单多了。这是因为计算机发展初期内存十分宝贵,能少用就少用,如果用16bit定义字节的话,一个英语字符就要占用两倍内存,这会造成巨大的浪费。而后来出现的UTF-8编码,一个汉字占3个字节。为什么要出现三个字节的编码呢?两个字节最多编码65535个字符,我们前面说了,光汉字就有10万个,还有其他国家的文字,这两个字节完全不够用啊。


理解了以上的知识点后,我们还需要了解到底什么是Java序列化、反序列化。

1.什么是序列化和反序列化?

序列化:将对象的状态信息转换成可以存储或者传输的二进制格式的过程,在这个过程中二进制格式通常以文件形式来体现。

反序列化:反之,将二级制存储形式转换成对象的过程,就是反序列化了。

2 JAVA中为什么byte而不是bit?

2.1 JAVA序列化将对象转为字节码(byte),为什么不可以直接转为位(bit)?

2.2 为什么java编译器要将java代码转为字节码(byte)?为什么不可以直接转为位(bit)?那样的话计算机不就可以直接读取了么?为什么还需要虚拟机呢?这么做的原因是什么?

2.1、2.1这两个问题其实很相似。

大家在刚接触JAVA的时候,应该就已经知道JAVA是可以跨平台(依靠JVM)的编程语言,那我们首先得知道什么是平台,我们把CPU处理器与操作系统的整体叫平台(平台 = CPU+OS,又因为现在主流的操作系统都支持主流的CPU,所以有时也把操作系统称为平台)

另外我们要知道,编译器负责把Java程序转成class文件(字节码),方便JVM来读取它,JVMjava虚拟机,它是解释器,把class文件中的命令转成某种平台的命令,比如把java命令转成Windows下的命令,直白一点就是,java源文件在不同的操作系统下通过前端编译器生成的class字节码文件是一样的,要不然怎么是“一次编译,到处运行”呢。字节码的设计就是为了充当中间人的角色,javac将源码编译成字节码,然后不同操作系统版本的jvm识别字节码 ,然后翻译成相应机器码(二进制),字节码文件提供了跨平台运行的特性。

class字节码文件在不同的操作系统下运行时,被JVM解释器翻译成操作系统的机器码(二进制)是不一样的。

字节码和机器码是两回事,不同架构的机器的机器码(二进制)是不同的。

以redis为例子来解释序列化,都知道java序列化对象到redis的时候都是转换成byte类型的,数据都是持久化在dump.rdb,比如dump.rdb是在windows上的,那么我们直接将其复制到linux上,一样可以进行数据的完美迁移,因为class字节码对于任何操作系统都是一样的,redis存储字节码,当要进行反序列化的时候不受不同操作系统的影响。

现在我们对2.1、2.2这两个问题提出反问,如果直接转为bit的话,那么将会发生什么呢?

假如序列化的时候我们直接序列化成windows的机器码(二进制),比如就序列化完存储到windows上的redis,这时候redis存储的是windows的机器码(二进制),当我们将dump.rdb迁移到linux的时候,由于取出来的时候是windows的机器码,这时候linux压根就不理解windows的机器码(二进制),无法逆推到字节码,更别说反序列化了。

假如我们将java程序直接编译成windows的机器码(二进制),这时候在windows系统上运行起来是没问题的,那当我们想在linux上运行程序的时候,只能再指定编译成linux的机器码(二进制),在MAC系统上,还需要再次指定编译成MAC的机器码(二进制),想象一下有多麻烦。

 

3.为什么要序列化和反序列化?

在很多应用场景中都需要用到序列化和反序列化,以保存当前对象的状态信息或者用于传输,例如,在常见的web服务中的持久化session就是一个很好的例子,一般session都是入驻内存的,当服务器异常宕机,内存里的session因为掉电而搽除,当我们设置了session持久化特性时,就会把session保存在硬盘上,这就是序列化,等服务器重启后有可以读取硬盘上这个session文件,还原session对象,这就是反序列化。

另一个需要序列化的场景就是进程间的远程通信,远程通信是以二进制字节流传输的,当需要传输对象的时候,首先在发送端需要将对象序列化为二进制形式方便网络传输,然后在接收端将二进制形式反序列化为java对象。比如早期的RMI,甚至现在流行的RPC通信都用到了序列化和反序列化。

 

4.如何序列化和反序列化一个对象?

Java提供了一套API方便用户序列化和反序列化。其中需要用到以下几个接口

  • java.io.Serializable
  • java.io.Externalizable
  • ObjectOutputStream
  • ObjectInputStream
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值