作为码农你必须懂的序列化
Posted -早起的码农
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了作为码农你必须懂的序列化相关的知识,希望对你有一定的参考价值。
一、基本概念
-
序列化:将对象写入到IO流中
-
反序列化:从IO流中恢复对象
-
意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
-
使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的
二、序列化实现的方式
JDK类库中的序列化API,只有实现了Serializable或Externalizable接口的类对象才能被序列化,否则会出现java.io.NotSerializableException异常。
实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式
-
Serializable
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable
private static final long serialVersionUID = 1L;
private String userName;
private String password;
private String year;
private transient String addr;
测试:
public class SerializableTest
public static void main(String[] args) throws IOException
, ClassNotFoundException
//序列化
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Student student1 = new Student();
student1.setUserName("lisi");
student1.setPassword("123456");
student1.setYear("2008");
student1.setAddr("beijing");
oos.writeObject(student1);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("object.out");
ObjectInputStream ois = new ObjectInputStream(fis);
Student student2 = (Student) ois.readObject();
System.out.println(student2.getUserName()+ " " +
student2.getPassword() + " " + student2.getYear()
+ " " + student2.getAddr());
输出:
lisi 123456 2008 null
我们会发现被transient修饰的字段,即使设置了值也不会被序列化,反序列化后返回值是null.
serialVersionUID又是什么作用呢?让我们改掉serialVersionUID版本号为2L,执行下面这段代码:
//反序列化
FileInputStream fis = new FileInputStream("object.out");
ObjectInputStream ois = new ObjectInputStream(fis);
Student student2 = (Student) ois.readObject();
System.out.println(student2.getUserName()+ " " +
student2.getPassword() + " " + student2.getYear()
+ " " + student2.getAddr());
发现出现如下错误,大家细品serialVersionUID的作用吧。
Exception in thread "main" java.io.InvalidClassException:
com.manong.test.server.Student; local class incompatible: stream classdesc
serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at com.yeahmobi.easyjob.server.SerializableTest.main(SerializableTest.java:23)
另外我们把Student换成String或者Integer类型去序列化或者反序列化,一样是可以的,原因我们查看其String,Integer源码,他是实现了Serializable接口的
-
Externalizable:强制自定义序列化
通过实现Externalizable接口,必须实现writeExternal、readExternal方法
public class Person implements Externalizable
private String name;
private int age;
//注意,必须加上pulic 无参构造器
public Person()
public Person(String name, int age)
this.name = name;
this.age = age;
@Override
public void writeExternal(ObjectOutput out) throws IOException
//将name反转后写入二进制流
StringBuffer reverse = new StringBuffer(name).reverse();
out.writeObject(reverse);
out.writeInt(age);
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException
//将读取的字符串反转后赋值给name实例变量
this.name = ((StringBuffer) in.readObject()).reverse().toString();
this.age = in.readInt();
public static void main(String[] args) throws IOException,
ClassNotFoundException
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("Person.txt"));
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("Person.txt")))
oos.writeObject(
oos.writeObject(new Person("lisi", 23));
Person ep = (Person) ois.readObject();
System.out.println(ep.name + " " + ep.age);
执行结果:
brady 23
二、kafka的序列化和反序列化
kafka内部发送和接收消息的时候,使用的是byte[]字节数组的方式(RPC底层也是用这种通讯格式)。但是我们在应用层其实可以使用更多的数据类型,比如int,short, long,String等,这归功于kafka的序列化和反序列化机制。
我们来看一个自定义序列化组件的实现的例子,其实核心就是利用fastjson的toJSONBytes把对象转化为byte数组。
@Data
@ToString
public class Company
private String name;
private String address;
public class CompanySerializer implements Serializer<Company>
@Override
public void configure(Map<String, ?> configs, boolean isKey)
@Override
public byte[] serialize(String s, Company company)
return JSON.toJSONBytes(company);
@Override
public void close()
public class CompanyDeserializer implements Deserializer<Company>
@Override
public void configure(Map<String, ?> configs, boolean isKey)
@Override
public Company deserialize(String s, byte[] bytes)
return JSON.parseObject(bytes, Company.class);
@Override
public void close()
设置如下参数即可:
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, CompanyDeserializer.class.getName());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer .class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, CompanySerializer.class.getName());
以上是关于作为码农你必须懂的序列化的主要内容,如果未能解决你的问题,请参考以下文章