字符编码与序列化

字符编码

概述

  • 在计算机世界中,它只能识别二进制数据,每一个二进制位(bit)有0和1两种状态。而为了方便应用计算机,让它可以处理各种信息,便将所有的信息最终都表示为一个二进制的字符串。而各个国家的信息的载体我们可以看作是文字,将各个国家的文字用一连串二进制数据来表示,并一一对应,形成一张表,这张表便是编码表。

  • 而编码,就是制定一种规则,将字符映射到唯一一种状态(二进制字符串)。例如在ASCII编码中,字符 A 对应的二进制字符串为:01000001

  • 常见的字符编码如下图:

image-20201010203454184

注意

  • Unicode可以理解为全世界通用的编码表,这张表包含了可能出现的所有字符,每个字符对应一个数字,这个数字称为码点(Code Point)。注意它只是一个符号集,只规定的字符所对应的码点,并没有指定如何存储,如何进行存储有不同的编码方案。

  • Unicode编码方案主要有两条主线:UCSUTFUTF主线由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
    

序列化

概述

  • 对象序列化机制是指把内存中的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自己生成的对象,不通过构造方法生成。

  • 序列化同一对象多次,并不会将此对象序列化多次得到多个对象。

    • 因为保存在磁盘或文件中的对象都有一个序列化编码号;程序尝试序列化一个对象时,先检查该对象是否被序列化过,如果没有序列化,则将该对象序列化,如果已经序列化过,会直接输出该对象的序列化编码号。
    • 由于记录的是对象的序列化编码号。则当序列化一个可变对象后,也就是说更改了对象内容比如对象的名字由张三改为李四,再次序列化,并不会再次将更改后的对象转换为字节序列,也就是说再次序列化该对象时,通过反序列化得到的对象并非是更改后的李四,而是张三。

    image-20201017154252946

  • statictransient修饰的成员变量在序列化时,会忽略掉该成员变量。

    • transient关键字不能修饰方法和类,只能修饰变量;被transient修饰的字段是默认值,该字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
    • 默认值取值 引用类型:null、基本类型:0、boolean类型:false。
    • 静态变量不能被序列化,反序列化后类中static型变的值为当前JVM中对应static变量的值,并不是反序列化得到的。

实现java.io.Externalizable接口的对象

  • 必须实现接口中的两个方法writeExternalreadExternal实现自定义序列化;

  • 就算类的变量被transient关键字修饰,依然可以序列化,Externalizable接口在反序列化时通过反射创建对象,因此该对象需要无参的构造方法。

总结

  • 在使用转换流InputStreamReaderOutputStreamWriter 时,会指定字符编码,不恰当的指定编码格式会造成乱码等问题,字符编码章节是对常见的编码方式进行的对比与整理。
  • 在使用对象流ObjectInputStreamOjbectOutputSteam,会把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。这就涉及到了对象序列化的相关知识点,序列化章节整理了序列化时常见问题与注意点。

欢迎关注
公众号三筒记简介:分享各种编程知识、excel相关技巧、读书笔记

原文地址:https://www.cnblogs.com/manongxiao/p/14305701.html