字符编码与序列化
字符编码
概述
-
在计算机世界中,它只能识别二进制数据,每一个二进制位(bit)有0和1两种状态。而为了方便应用计算机,让它可以处理各种信息,便将所有的信息最终都表示为一个二进制的字符串。而各个国家的信息的载体我们可以看作是文字,将各个国家的文字用一连串二进制数据来表示,并一一对应,形成一张表,这张表便是编码表。
-
而编码,就是制定一种规则,将字符映射到唯一一种状态(二进制字符串)。例如在
ASCII
编码中,字符 A 对应的二进制字符串为:01000001
。 -
常见的字符编码如下图:
注意
-
Unicode
可以理解为全世界通用的编码表,这张表包含了可能出现的所有字符,每个字符对应一个数字,这个数字称为码点(Code Point
)。注意它只是一个符号集,只规定的字符所对应的码点,并没有指定如何存储,如何进行存储有不同的编码方案。 -
Unicode
编码方案主要有两条主线:UCS
和UTF
。UTF
主线由Unicode Consortium
进行维护管理,UCS主线由ISO/IEC
进行维护管理。 -
Unicode
码点转化为UTF-8
编码的规则如下:- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的
unicode
码。因此对于英语字母,UTF-8
编码和ASCII
码是相同的。 - 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的
unicode
码。如下:
Unicode符号范围 (十六进制) (十进制) | UTF-8编码方式(二进制) ---------------------------------------------------------------------------------- 0000 0000-0000 007F (0-127) | 0xxxxxxx (兼容ASCII码) 0000 0080-0000 07FF (128-2047) | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF (2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF (65536-1114111) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的
序列化
概述
-
对象序列化机制是指把内存中的Java对象转换成与平台无关的二进制内容,本质上就是一个
byte[]
数组,进而将该二进制内容持久的保存在磁盘上或通过网络传输到远程;当其他程序获取到该二进制内容时,可以恢复为原来的Java对象。简单来说:- 序列化:将对象变为二进制内容,写入到IO流中;
- 反序列化:从IO流获取到二进制内容,将其恢复为对象。
-
意义:序列化机制允许将实现序列化的Java对象转换为字节序列(也就是
byte[]
数组),这些字节序列可以保存在文件上,或通过网络传输,之后可以恢复成原来的对象。该机制使对象可以脱离程序的运行而独立存在。 -
注意:在网络上传输的对象都必须是可序列化的,比如RMI(
remote method invoke
,即远程方法调用),传入的参数或返回的对象都必须是可序列化的;同理,所有需要保存到磁盘的java对象都必须是可序列化的。 -
一个Java对象要能序列化,必须实现
java.io.Serializable
接口或者java.io.Externalizable
接口之一。Serializable
接口没有定义任何方法,是一个空接口。称为“标记接口”。一旦实现了此接口,该类的对象就是可序列化的。public interface Serializable {}
实现
- 序列化:用
ObjectOutputStream
类保存基本类型数据或对象的机制,把一个Java对象变为byte[]
数组,它可以把一个Java对象写入一个字节流。 - 反序列化:用
ObjectInputStream
类读取基本类型数据或对象的机制,它从一个字节流读取Java对象。
注意
实现java.io.Serializable
接口的对象
-
反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。
-
序列化同一对象多次,并不会将此对象序列化多次得到多个对象。
- 因为保存在磁盘或文件中的对象都有一个序列化编码号;程序尝试序列化一个对象时,先检查该对象是否被序列化过,如果没有序列化,则将该对象序列化,如果已经序列化过,会直接输出该对象的序列化编码号。
- 由于记录的是对象的序列化编码号。则当序列化一个可变对象后,也就是说更改了对象内容比如对象的名字由张三改为李四,再次序列化,并不会再次将更改后的对象转换为字节序列,也就是说再次序列化该对象时,通过反序列化得到的对象并非是更改后的李四,而是张三。
-
static
和transient
修饰的成员变量在序列化时,会忽略掉该成员变量。transient
关键字不能修饰方法和类,只能修饰变量;被transient
修饰的字段是默认值,该字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。- 默认值取值 引用类型:null、基本类型:0、boolean类型:false。
- 静态变量不能被序列化,反序列化后类中static型变的值为当前JVM中对应
static
变量的值,并不是反序列化得到的。
实现java.io.Externalizable
接口的对象
-
必须实现接口中的两个方法
writeExternal
、readExternal
实现自定义序列化; -
就算类的变量被
transient
关键字修饰,依然可以序列化,Externalizable
接口在反序列化时通过反射创建对象,因此该对象需要无参的构造方法。
总结
- 在使用转换流
InputStreamReader
和OutputStreamWriter
时,会指定字符编码,不恰当的指定编码格式会造成乱码等问题,字符编码章节是对常见的编码方式进行的对比与整理。 - 在使用对象流
ObjectInputStream
和OjbectOutputSteam
,会把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。这就涉及到了对象序列化的相关知识点,序列化章节整理了序列化时常见问题与注意点。
欢迎关注
公众号三筒记简介:分享各种编程知识、excel相关技巧、读书笔记