详解java序列化

合集下载

java 序列化失败 方法引用

java 序列化失败 方法引用

文章标题:探究Java序列化失败的原因及方法引用的解决方案1. 前言在Java编程中,我们经常会遇到对象序列化和反序列化的问题。

而在序列化的过程中,有时会遇到序列化失败的情况,特别是在涉及到方法引用的情况下更是常见。

本文将从深度和广度的角度对Java序列化失败方法引用的问题进行全面探讨,并提出解决方案。

2. Java序列化失败的原因分析在Java中,对象序列化是将对象转换为字节流的过程,以便于在网络传输或者持久化存储。

而有时候,在序列化对象中包含方法引用时,就会出现序列化失败的情况。

这主要是因为方法引用是无法被序列化的,所以在序列化的过程中就会出现问题。

3. 方法引用的特点和问题方法引用是Java 8引入的新特性,它使得我们可以直接引用已有的方法,而不需要去编写Lambda表达式。

但是,方法引用的引入也给序列化带来了一些问题。

因为方法引用是指向方法的引用,而在序列化的过程中,是无法将方法引用直接转换成字节流的。

4. 序列化失败的常见场景在实际编码中,我们可能会遇到一些常见的场景,导致了序列化失败。

当我们序列化一个包含方法引用的对象时,就有可能出现序列化失败的情况。

另外,当我们将包含方法引用的对象进行网络传输或者存储到文件时,同样也会遇到序列化失败的情况。

5. 解决方案探讨针对Java序列化失败方法引用的问题,我们可以考虑一些解决方案。

我们可以尝试使用Lambda表达式来替代方法引用,因为Lambda表达式是可以被序列化的。

我们还可以考虑使用序列化代理来解决方法引用的序列化问题。

通过序列化代理,我们可以将方法引用替换为Lambda表达式,再进行序列化操作。

6. 个人观点和总结在我看来,Java序列化失败方法引用的问题是一个需要重视的话题。

通过深入探讨和解决这个问题,我们可以更好地提高程序的稳定性和可靠性。

我们也需要不断地学习和研究新的解决方案,以应对日益复杂的编程环境。

7. 结语通过本文的探讨,我们对Java序列化失败方法引用的问题有了更深入的理解和认识。

java序列化和serialVersionUID的使用方法实例

java序列化和serialVersionUID的使用方法实例

java序列化和serialVersionUID的使⽤⽅法实例java序列化和serialVersionUID的使⽤⽅法实例1、序列化:序列化可以将⼀个java对象以⼆进制流的⽅式在⽹络中传输并且可以被持久化到数据库、⽂件系统中,反序列化则是可以把之前持久化在数据库或⽂件系统中的⼆进制数据以流的⽅式读取出来重新构造成⼀个和之前相同内容的java对象。

2、序列化的作⽤:第⼀种:⽤于将java对象状态储存起来,通常放到⼀个⽂件中,使下次需要⽤到的时候再读取到它之前的状态信息。

第⼆种:可以让java对象在⽹络中传输。

3、序列化的实现:1)、需要序列化的类需要实现Serializable接⼝,该接⼝没有任何⽅法,只是标⽰该类对象可被序列化。

2)、序列化过程:使⽤⼀个输出流(如:FileOutputStream)来构造⼀个ObjectOutputStream(对象流)对象,接着,使⽤ObjectOutputStream对象的writeObject(Object obj)⽅法就可以将参数为obj的对象写出(即保存其状态)3)、反序列化过程:使⽤⼀个输⼊流(如:FileInputStream)来构造⼀个ObjectInputStream(对象流)对象,接着,使⽤ObjectInputStream对象的readObject(Object obj)⽅法就可以将参数为obj的对象读出(即获取其状态)4、静态long类型常量serialVersionUID的作⽤:如果没有设置这个值,你在序列化⼀个对象之后,改动了该类的字段或者⽅法名之类的,那如果你再反序列化想取出之前的那个对象时就可能会抛出异常,因为你改动了类中间的信息,serialVersionUID是根据类名、接⼝名、成员⽅法及属性等来⽣成⼀个64位的哈希字段,当修改后的类去反序列化的时候发现该类的serialVersionUID值和之前保存在问价中的serialVersionUID 值不⼀致,所以就会抛出异常。

Java对象序列化和反序列化(实现Serializable接口)

Java对象序列化和反序列化(实现Serializable接口)

Java对象序列化和反序列化(实现Serializable接⼝)序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化。

 把字节序列恢复为对象的过程称为对象的反序列化。

对象的序列化主要有两种⽤途: 1)把对象的字节序列永久地保存到硬盘上,通常存放在⼀个⽂件中; 2)在⽹络上传送对象的字节序列。

JDK类库中的序列化API java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)⽅法可对参数指定的obj对象进⾏序列化,把得到的字节序列写到⼀个⽬标输出流中。

java.io.ObjectInputStream代表对象输⼊流,它的readObject()⽅法从⼀个源输⼊流中读取字节序列,再把它们反序列化为⼀个对象,并将其返回。

只有实现了Serializable和Externalizable接⼝的类的对象才能被序列化。

Externalizable接⼝继承⾃ Serializable接⼝,实现Externalizable接⼝的类完全由⾃⾝来控制序列化的⾏为, ⽽仅实现Serializable接⼝的类可以采⽤默认的序列化⽅式。

对象序列化包括如下步骤:  1)创建⼀个对象输出流,它可以包装⼀个其他类型的⽬标输出流,如⽂件输出流; 2)通过对象输出流的writeObject()⽅法写对象。

对象反序列化的步骤如下: 1)创建⼀个对象输⼊流,它可以包装⼀个其他类型的源输⼊流,如⽂件输⼊流; 2)通过对象输⼊流的readObject()⽅法读取对象。

⽰例:新建⼀个Car 类1package com.oukele.redis2.entity;23import java.io.Serializable;45public class Car implements Serializable {67/*8 * 编号9*/10private int id;11/*12 * 车名13*/14private String name;15/*16 * 车速17*/18private double speed;1920public Car(String name,double speed ){ = name;22this.speed = speed;23 }2425public Car(int id, String name, double speed) {26this.id = id; = name;28this.speed = speed;29 }3031public int getId() {32return id;33 }3435public void setId(int id) {36this.id = id;37 }3839public String getName() {40return name;41 }4243public void setName(String name) { = name;45 }4647public double getSpeed() {48return speed;49 }5051public void setSpeed(double speed) {52this.speed = speed;53 }5455 @Override56public String toString() {57return "Car{" +58 "id=" + id +59 ", name='" + name + '\'' +60 ", speed=" + speed +61 '}';62 }63 }新建⼀个 SerializeUtil 类(封装序列化和反序列化的⽅法,便于调⽤)1package com.oukele.redis2.util;23import java.io.ByteArrayInputStream;4import java.io.ByteArrayOutputStream;5import java.io.ObjectInputStream;6import java.io.ObjectOutputStream;78public class SerializeUtil {910/*11 * 序列化12 * */13public static byte[] serialize(Object object){14//序列化流(输出流) --> 表⽰向⼀个⽬标写⼊数据15 ObjectOutputStream objectOutputStream =null;16//字节数组输出流17 ByteArrayOutputStream byteArrayOutputStream = null;18try{19//创建⼀个缓冲区20 byteArrayOutputStream = new ByteArrayOutputStream();21//将对象序列化成字节后输⼊缓冲区中22 objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);23//序列化对象24 objectOutputStream.writeObject(object);25//得到序列化字节26byte[] bytes = byteArrayOutputStream.toByteArray();2728//清空输出流29 objectOutputStream.flush();30//释放资源31 objectOutputStream.close();3233return bytes;34 }catch (Exception e){35 System.out.println("出现异常:"+e.getMessage());36 }37return null;38 }3940/*41 * 反序列化42 * */4344public static <T> T deserialize(byte[] bytes,Class<T> clazz){45//字节数组46 ByteArrayInputStream byteArrayInputStream = null;47try{48//将得到的序列化字节丢进缓冲区49 byteArrayInputStream = new ByteArrayInputStream(bytes);50//反序列化流(输⼊流)--> 表⽰着从⼀个源头读取数据,(读取缓冲区中的字节)51 ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); 52//反序化成⼀个对象53return (T)objectInputStream.readObject();54 }catch (Exception e){55 System.out.println("出现异常:"+e.getMessage());56 }57return null;58 }596061 }测试:1 //序列化⼀个对象2 byte[] serialize = SerializeUitl.serialize(new Car(2,"宝马",88.8));3 //反序列化4 Car deserialize = SerializeUitl.deserialize(serialize, Car.class);5 System.out.println(deserialize);测试结果:⽰例源码地址:。

java对象的序列化以及反序列化详解

java对象的序列化以及反序列化详解

java对象的序列化以及反序列化详解⼀、概念序列化:把创建出来的对象(new出来的对象),以及对象中的成员变量的数据转化为字节数据,写到流中,然后存储到硬盘的⽂件中。

反序列化:可以把序列化后的对象(硬盘上的⽂件中的对象数据),读取到内存中,然后就可以直接使⽤对象。

这样做的好处是不⽤再⼀次创建对象了,直接反序列化就可以了。

使⽤场景:在创建对象并给所创建的对象赋予了值后,当前创建出来的对象是存放在堆内存中的,当JVM停⽌后,堆中的对象也被释放了,如果下⼀次想要继续使⽤之前的对象,需要再次创建对象并赋值。

然⽽使⽤序列化对象,就可以把创建出来的对象及对象中数据存放到硬盘的⽂件中,下次使⽤的时候不⽤在重新赋值,⽽是直接读取使⽤即可。

对象直接转换为字节的形式进⾏⽹络传输⼆、相关API介绍序列化序列化对象所属的类是ObjectOutputStream,如下图所⽰:说明:ObjectOutputStream类可以把对象及其数据写⼊到流中或⽹络中。

构造函数如下图所⽰:写出功能:反序列化反序列化对象所属的类是ObjectInputStream类:说明:序列化输出流对象和反序列化输出流对象都不具备读写能⼒,分别依赖FileOutputStream和FileInputStream类来进⾏读写⽂件。

构造函数如下图所⽰:读取功能:三、实战序列化需求:把Student类创建的对象持久化保存。

分析和步骤:1)⾃定义⼀个Student类,并定义name和age属性;2)定义⼀个测试类,在这个测试类中创建Student类的对象s;3)创建序列化对象objectOutputStream,同时创建输出流并关联硬盘上的⽂件;4)使⽤序列化对象objectOutputStream调⽤writeObject()函数持久化保存学⽣对象s;5)释放序列化对象流的资源;Student类如下:需要实现序列化接⼝Serializable,它是⼀个标记性接⼝。

java中的序列化的方法

java中的序列化的方法

java中的序列化的方法Java中的序列化方法序列化是指将对象转换为字节流的过程,可以将对象保存到文件、数据库或进行网络传输。

而在Java中,提供了一种简单且方便的序列化机制,即Java序列化(Java Serialization)。

本文将介绍Java中的序列化方法,包括实现序列化接口、序列化与反序列化过程、自定义序列化和版本控制等内容。

一、实现序列化接口在Java中,要使一个类可以被序列化,需要实现Serializable接口。

Serializable接口是一个标记接口,没有任何方法定义,只是用来标记一个类可以被序列化。

当一个类实现了Serializable接口后,就可以将该类的对象转换为字节流进行存储或传输。

二、序列化与反序列化过程Java序列化的过程可以简单地分为两个步骤:将对象转换为字节流(序列化)和将字节流转换为对象(反序列化)。

1. 序列化:将对象转换为字节流在Java中,可以通过使用ObjectOutputStream类来实现对象的序列化。

具体步骤如下:(1)创建一个ObjectOutputStream对象,用于将对象转换为字节流;(2)调用ObjectOutputStream对象的writeObject()方法,将需要序列化的对象写入输出流中。

2. 反序列化:将字节流转换为对象与序列化相反,反序列化是将字节流转换为对象的过程。

在Java中,可以通过使用ObjectInputStream类来实现对象的反序列化。

具体步骤如下:(1)创建一个ObjectInputStream对象,用于从字节流中读取对象;(2)调用ObjectInputStream对象的readObject()方法,将字节流转换为对象。

三、自定义序列化在某些情况下,我们可能不希望将一个类的所有属性都进行序列化,或者希望对序列化进行一些特殊处理。

在Java中,可以通过自定义序列化方法来实现对序列化过程的控制。

1. 实现writeObject()方法可以在类中添加一个私有的writeObject()方法,通过该方法自定义对象的序列化过程。

java序列化与反序列化的使用方法汇总

java序列化与反序列化的使用方法汇总

java序列化与反序列化的使⽤⽅法汇总⼀、概念java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再⽣成相同状态的对象。

对象序列化是对象持久化的⼀种实现⽅法,它是将对象的属性和⽅法转化为⼀种序列化的形式⽤于存储和传输。

反序列化就是根据这些保存的信息重建对象的过程。

序列化:将java对象转化为字节序列的过程。

反序列化:将字节序列转化为java对象的过程。

⼆、为什么要序列化和反序列化我们知道,当两个进程进⾏远程通信时,可以相互发送各种类型的数据,包括⽂本、图⽚、⾳频、视频等,⽽这些数据都会以⼆进制序列的形式在⽹络上传送。

那么当两个Java进程进⾏通信时,能否实现进程间的对象传送呢?答案是可以的。

如何做到呢?这就需要Java序列化与反序列化了。

换句话说,⼀⽅⾯,发送⽅需要把这个Java对象转换为字节序列,然后在⽹络上传送;另⼀⽅⾯,接收⽅需要从字节序列中恢复出Java对象。

当我们明晰了为什么需要Java序列化和反序列化后,我们很⾃然地会想Java序列化的好处。

其好处⼀是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在⽂件⾥),⼆是,利⽤序列化实现远程通信,即在⽹络上传送对象的字节序列。

三、涉及到的javaAPI java.io.ObjectOutputStream表⽰对象输出流,它的writeObject(Object obj)⽅法可以对参数指定的obj对象进⾏序列化,把得到的字节序列写到⼀个⽬标输出流中。

java.io.ObjectInputStream表⽰对象输⼊流,它的readObject()⽅法源输⼊流中读取字节序列,再把它们反序列化成为⼀个对象,并将其返回。

只有实现了Serializable或Externalizable接⼝的类的对象才能被序列化,否则抛出异常。

四、序列化和反序列化的步骤序列化:步骤⼀:创建⼀个对象输出流,它可以包装⼀个其它类型的⽬标输出流,如⽂件输出流:ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“⽬标地址路径”));步骤⼆:通过对象输出流的writeObject()⽅法写对象:out.writeObject("Hello");out.writeObject(new Date());反序列化:步骤⼀:创建⼀个对象输⼊流,它可以包装⼀个其它类型输⼊流,如⽂件输⼊流:ObjectInputStream in = new ObjectInputStream(new fileInputStream(“⽬标地址路径”));步骤⼆:通过对象输出流的readObject()⽅法读取对象:String obj1 = (String)in.readObject();Date obj2 = (Date)in.readObject();说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输⼊流中读对象的顺序⼀致。

javaserializable深入了解(java序列化深入了解)

java serializable深入了解(java序列化深入了解)IntroductionThe Java object serialization for Java binary file serialization technology is an important technical point Java series technology, in most cases, developers only need to understand the serialized needs to implement the Serializable interface, using ObjectInputStream and ObjectOutputStream object to read and write. However, in some cases, only know that is not enough, this paper enumerates some real situation the author met them, and through the analysis of Java serialization, the situation appears, make readers easily remember some advanced understanding of Java serialization.--------------------------------------------------------------------------------Back to topArticle structureThis article will introduce each of the following scenarios, such as the following list.The problem of serializing IDStatic variable serializationThe serialization of the parent class and the Transient keywordSensitive to field encryptionSerialization storage rulesEach part of the list tells a separate situation where the reader can view them separately.--------------------------------------------------------------------------------Back to topSerialize the ID problemSituation: two clients, A and B, attempt to pass object data over the network. The A side serialize the object C to binary data, and then pass it to B, where the B gets C.Problem: the entire classpath of the C object is assumed to be com.inout.Test, and there is such a class file at the A and B ends, and the function code is exactly the same. Serializable interfaces are also implemented, but anti serialization always prompts unsuccessful.Solution: the virtual machine is allowed to deserialize, not only depends on the class path and the function code is consistent, a very important point is the two class of ID serialization is consistent (that is, private static final long serialVersionUID = 1L). In Listing 1, although the function code of the two classes is exactly the same, the serialization ID is different, and they cannot serialize and serialize each other.Listing 1. class comparison of serialization ID with the same function codePackage com.inout;Import java.io.Serializable;Public, class, A, implements, Serializable {Private, static, final, long, serialVersionUID = 1L;Private String name;Public, String, getName (){Return name;}Public void setName (String name){ = name;}}Package com.inout;Import java.io.Serializable;Public, class, A, implements, Serializable {Private, static, final, long, serialVersionUID = 2L;Private String name;Public, String, getName (){Return name;}Public void setName (String name){ = name;}}The serialized ID provides two generation strategy in Eclipse, a 1L is fixed, one is randomly generated a duplicate long datatype (actually generated using the JDK tool), there is a suggestion here, if there is no special requirement, is to use the default 1L on it, so you can make sure that the code is consistent to deserialize success. So what happens to randomly generated serialized ID? Sometimes, by changing serialization, ID can be used to restrict the use of certain users.Feature usage caseThe reader should have heard of the Fa ade pattern, which provides a unified access interface for the application, which is used by the Client client in the case program, and the case program diagram is shown in figure 1.Figure 1. case program structureThe Client side interacts with the business logic object through the Fa ade Object.The client of Fa? Ade Object cannot be generated directly by Client, but Server generates a binary object, will transmit data to the Client and then serialized through the network, Client is responsible for the anti Fa ade object serialization?. This pattern allows the use of Client end programs requiring server side permissions, while the Fa and Client ade Object classes on the server side need to be consistent. When the server wants to update, as long as the server of Fa? Ade Object ID serialization generated again, when Client end Fa ade Object deserialization? Will fail, i.e. Client side access to the latest program from the server.--------------------------------------------------------------------------------Back to topStatic variable serializationSituation: check the code in listing 2.Listing 2. static variable serialization problem codePublic, class, Test, implements, Serializable {Private, static, final, long, serialVersionUID = 1L;Public, static, int, staticVar = 5;Public, static, void, main (String[], args) {{tryWhen the initial staticVar was 5 / /ObjectOutputStream out = new ObjectOutputStreamNew FileOutputStream ("result.obj"));Out.writeObject (new, Test ());Out.close ();After the modification was 10 / serializationTest.staticVar = 10;ObjectInputStream oin = new ObjectInputStream (new, FileInputStream)"Result.obj");Test t = (Test) oin.readObject ();Oin.close ();/ / then read through t.staticVar printing, new valueSystem.out.println (t.staticVar);} catch (FileNotFoundException, e) {E.printStackTrace ();} catch (IOException, e) {E.printStackTrace ();} catch (ClassNotFoundException, e) {E.printStackTrace ();}}The main method in Listing 2, after serializing the object, modifies the value of the static variable, reads the serialized object, and then gets the value of the static variable and prints it out of the object that is read. According to Listing 2, does this System.out.println (t.staticVar) statement output 10 or 5?The final output is 10, and for the incomprehensible reader, the printed staticVar is obtained from the object that is read, and should be the state of being saved. The reason is that the 10 print serialization, does not store static variables, it is easier to understand, serialization preservation is the state of the object, static variables belonging to the class of the state, so the serialization does not save static variables.--------------------------------------------------------------------------------Back to topSerialization and Transient keywords for the parent classSituation: a class implements the Serializable interface, its parent class does not implement the Serializable interface, the subclass object serialization and deserialization, numerical output after a variable definition of the parent class, and the values of variables at different serialization.Solution: if you want to serialize a parent class object, you also need to let the parent class implement the Serializable interface. If the parent class does not implement, it needs a default, no argument constructor. When the parent class does not implement the Serializable interface, the virtual machine does not serialize the parent object, and the construction of a Java object must have a parent object and only a child object, and the serialization is no exception. Therefore, in order to construct a parent object, only the nonparametric constructor of the parent class can be invoked as the default parent object for serialization. So when we take the variable value of the parent object,Its value is the value that calls the parent class without the argument constructor. If you consider the serialization, to initialize a variable in the parent class parameterless constructor, otherwise, the parent variable value is the default value of the statement, such as the default int type is 0, the default is null type string.The Transient keyword is used to control the variable serialization of variables in the statement added before the keyword, can prevent the variables to be serialized to files, in be deserialized, the value of the transient variable is set to the initial value, such as the int type is 0, the object type is null.Feature usage caseWe're familiar with using the Transient keyword to make thefields not serialized, so what else? According to the superclass object serialization rules, we will need not be serialized field extracted in a parent class, sub class implements the Serializable interface, the parent is not achieved, according to the parent class serialization rules, the field data of the superclass will not be serialized, the formation of the class diagram as shown in figure 2.Figure 2. case program class diagramYou can see in the image above, attr1, attr2, attr3, attr5 will not be serialized in the parent class, the advantage is that when there is also a Child class, attr1, attr2, attr3 still will not be serialized, do not repeat the description of transient, the code is simple.--------------------------------------------------------------------------------Back to topEncrypt sensitive fieldsSituation: the server to the client sends the serialized object data object, some data is sensitive, such as password string, encryption of the password field hope in serialization, and, if the client has a decryption key, only deserialized on the client, can read out the password, it can to a certain extent to ensure the safety of data serialization of objects.Solution: during serialization, virtual writeObject andreadObject methods to try to call the object class,user-defined serialization and deserialization, if there is no such method, the default method is called defaultWriteObject ObjectOutputStream and defaultReadObject ObjectInputStream method. User - defined writeObject and readObject methods allow users to control the serialization process, such as dynamically changing serialization values during serialization. Based on this principle, it can be used in practical applications for encryption of sensitive fields, and Listing 3 shows the process.Listing 3. static variable serialization problem codePrivate, static, final, long, serialVersionUID = 1L;Private String password = "pass"";Public, String, getPassword () {Return password;}Public, void, setPassword (String, password) {This.password = password;}Private, void, writeObject (ObjectOutputStream, out) {PutField putFields = out.putFields ();System.out.println (original password: + password); Password = "encryption"; / / analog encryptionPutFields.put ("password", password);System.out.println (encrypted password + password);Out.writeFields ();} catch (IOException, e) {E.printStackTrace ();}}Private, void, readObject (ObjectInputStream, in) {{tryGetField readFields = in.readFields ();"Object object = readFields.get" ("password", "" "); System.out.println (the string to decrypt: '+ object.toStringPassword = "pass"; / / simulation need to obtain the local key decryption.} catch(IOException e){E. printstacktrace();} catch(ClassNotFoundException e){E. printstacktrace();}}public static void main(String [] args){尝试{新的对象(对象=新的输出(结果)。

JAVA基础4---序列化和反序列化深入整理(JDK序列化)

JAVA基础4---序列化和反序列化深⼊整理(JDK序列化)⼀、什么是序列化和反序列化?序列化:将对象状态信息转化成可以存储或传输的形式的过程(Java中就是将对象转化成字节序列的过程)反序列化:从存储⽂件中恢复对象的过程(Java中就是通过字节序列转化成对象的过程)⼆、为什么要序列化和反序列化?Java中对象都是存储在内存中,准确地说是JVM的堆或栈内存中,可以各个线程之间进⾏对象传输,但是⽆法在进程之间进⾏传输。

另外如果需要在⽹络传输中传输对象也没有办法,同样内存中的对象也没有办法直接保存成⽂件。

所以需要对对象进⾏序列化,序列化对象之后⼀个个的Java对象就变成了字节序列,⽽字节序列是可以传输和存储的。

⽽反序列化就可以通过序列化⽣产的字节序列再恢复成序列化之前的对象状态及信息。

总结:1、进程之间传输对象(如RPC、RMI通信)2、⽹络通信时进⾏传输对象3、持久化对象时需要将对象序列化三、怎么序列化和反序列化?实现序列化的⽅式有很多种,常⽤的⽅式有如下⼏种:3.1、JDK序列化JDK序列化时JDK⾃带的序列化⽅式,使⽤其他也⽐较⽅便,只需要序列化的类实现了Serializable接⼝即可,Serializable接⼝没有定义任何⽅法和属性,所以只是起到了标识的作⽤,表⽰这个类是可以被序列化的。

如果没有实现Serializable接⼝⽽进⾏序列化操作就会抛出NotSerializableException异常。

能够序列化的字段:属性变量、⽗类的属性变量(⽗类也需要实现Serializablie接⼝)不能序列化的字段:静态变量、⽗类的属性变量、关键字transient修饰的变量、没有实现Serializable接⼝的对象属性3.1.1、Serializable接⼝案例定义类User、Person、Home、School分别如下1public class Home implements Serializable {2private String address;34public String getAddress() {5return address;6 }78public void setAddress(String address) {9this.address = address;10 }11 }1public class School {2private String schoolName;34public String getSchoolName() {5return schoolName;6 }78public void setSchoolName(String schoolName) {9this.schoolName = schoolName;10 }11 }1public class Person implements Serializable {23public static String parentType = "Person"; //⽗类静态变量45private String sex;//性别67public String getSex() {8return sex;9 }1011public void setSex(String sex) {12this.sex = sex;13 }14 }public class User extends Person implements Serializable {public static boolean alive; //静态变量private Long userId;//Long 类型private int age; //int 类型private String userName; //string 类型private String password; //string 类型private transient String IDCard; //不序列化的字段private Date birth; //Date类型private Home home; // 可以序列化的对象类型private School school; //不可以序列化的对象类型List<User> friends; //List类型/** set get ⽅法省略*/}案例中序列化的类为User,继承类Person,分别含有类Home和School的对象属性,序列化测试代码如下:1public class MainTest {2public static void main(String[] args) throws Exception{3 User user = new User();4 user.setAge(10);5 user.setBirth(new Date());6 user.setPassword("123456");7 user.setUserName("Jack");8 user.setUserId(100L);9 user.setSex("男");10 user.setIDCard("131313131313113");11 user.parentType = "son";//修改⽗类静态变量12 user.alive = true; //修改User类的静态变量1314 Home home = new Home();15 home.setAddress("中国浙江");16 School school = new School();17 school.setSchoolName("清华⼤学");18 user.setHome(home);//设置对象属性19// user.setSchool(school);//设置对象属性 (因为School类没有实现Seriliazable接⼝,所以如果设置就会报错)2021 List<User> friends = new ArrayList<User>();22 User userF = new User();23 userF.setUserId(101L);24 userF.setUserName("Friend");25 friends.add(userF);26 user.setFriends(friends);2728//序列化29 serializer(user);30//反序列化31 User newUser = derializer();32//验证33 System.out.println("验证两个对象是否相等");34 System.out.println("原对象地址:"+user.toString());35 System.out.println("新对象地址:"+newUser.toString());36 System.out.println("******************");37 System.out.println("打印两个对象");38 System.out.println("原对象数据:"+JSON.toJSON(user).toString());39 System.out.println("新对象数据:"+JSON.toJSON(newUser).toString());40 }4142/**序列化对象*/43private static void serializer(User user)throws Exception{44 ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("/Users/xxw/testlog/user.txt")));45 outputStream.writeObject(user);46 outputStream.close();47 }4849/**反序列化对象*/50private static User derializer()throws Exception{51 ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("/Users/xxw/testlog/user.txt")));52 User user = (User) inputStream.readObject();53 inputStream.close();54return user;55 }56 }测试结果为:1验证两个对象是否相等2原对象地址:er@3764951d3新对象地址:er@4783da3f4 ******************5打印两个对象6原对象数据:{"iDCard":"131313131313113","password":"123456","sex":"男","birth":1573203467764,"userName":"Jack","userId":100,"age":10,"friends":[{"userName":"Friend","userId":101,"age":0}],"home":{"address":"中国浙江" 7新对象数据:{"password":"123456","sex":"男","birth":1573203467764,"userName":"Jack","userId":100,"age":10,"friends":[{"userName":"Friend","userId":101,"age":0}],"home":{"address":"中国浙江"}}这⾥User类的School属性没有实现Serializable接⼝,所以如果给school属性赋值然后进⾏序列化就会报错,结果如下:1 Exception in thread "main" java.io.NotSerializableException: com.lucky.demo.base.seralizer.demo.School2 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)3 at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)4 at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)5 at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)6 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)7 at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)8 at com.lucky.demo.base.seralizer.demo.MainTest.serializer(MainTest.java:59)9 at com.lucky.demo.base.seralizer.demo.MainTest.main(MainTest.java:43)⽽如果User类的⽗类Person没有实现Serializable接⼝,那么序列化的时候不会报错,但是⽗类中的属性在反序列化之后字段就会没有,结果如下:1打印两个对象2原对象数据:{"iDCard":"131313131313113","password":"123456","sex":"男","birth":1573203839905,"userName":"Jack","userId":100,"age":10,"friends":[{"userName":"Friend","userId":101,"age":0}],"home":{"address":"中国浙江" 3新对象数据:{"password":"123456","birth":1573203839905,"userName":"Jack","userId":100,"age":10,"friends":[{"userName":"Friend","userId":101,"age":0}],"home":{"address":"中国浙江"}}这⾥就没有了⽗类的属性sex字段3.1.2、Serializable接⼝实现原理Serializable接⼝是⼀个空接⼝,没有定义任何的⽅法和属性,所以Serialiazable接⼝的作⽤就是起到⼀个标识的作⽤,源码如下1public interface Serializable {2 }Serializable接⼝既然是标识的作⽤,那么就需要在实际序列化操作的时候进⾏识别,⽽实际的序列化操作是通过ObjectOutputStream和ObjectInputStream实现的,那么接下来就看下这两个类的是如何实现序列化和反序列化的3.1.2.1、ObjectOutputStream源码解析构造函数如下1public ObjectOutputStream(OutputStream out) throws IOException {2 verifySubclass();3 bout = new BlockDataOutputStream(out);4 handles = new HandleTable(10, (float) 3.00);5 subs = new ReplaceTable(10, (float) 3.00);6 enableOverride = false;7 writeStreamHeader();8 bout.setBlockDataMode(true);9if (extendedDebugInfo) {10 debugInfoStack = new DebugTraceInfoStack();11 } else {12 debugInfoStack = null;13 }14 }OutoutStream表⽰保存的⼆进制流,也就是将序列化的对象保存到这个⼆进制流中,再看下具体的序列化⽅法源码如下:1public final void writeObject(Object obj) throws IOException {2if (enableOverride) {//enableOverride 表⽰是否可以被覆盖,默认为false3 writeObjectOverride(obj);4return;5 }6try {7//执⾏具体的序列化操作8writeObject0(obj, false);9 } catch (IOException ex) {10if (depth == 0) {11 writeFatalException(ex);12 }13throw ex;14 }15 }最终执⾏了writeObject0(obj, false)⽅法,代码如下:1private void writeObject0(Object obj, boolean unshared)2throws IOException3 {4boolean oldMode = bout.setBlockDataMode(false);5 depth++;6try {7// handle previously written and non-replaceable objects8// 处理已经处理过的和不可替换的对象,这些是不可序列化的9int h;10if ((obj = subs.lookup(obj)) == null) {11 writeNull();12return;13 } else if (!unshared && (h = handles.lookup(obj)) != -1) {14 writeHandle(h);15return;16 } else if (obj instanceof Class) {17 writeClass((Class) obj, unshared);18return;19 } else if (obj instanceof ObjectStreamClass) {20 writeClassDesc((ObjectStreamClass) obj, unshared);21return;22 }2324// check for replacement object25 Object orig = obj;26//获取对象的Class对象27 Class<?> cl = obj.getClass();28 ObjectStreamClass desc;29for (;;) {30// REMIND: skip this check for strings/arrays?31 Class<?> repCl;32//获取Class的描述信息,并且判断是否是Serializable接⼝33 desc = ObjectStreamClass.lookup(cl, true);34if (!desc.hasWriteReplaceMethod() ||35 (obj = desc.invokeWriteReplace(obj)) == null ||36 (repCl = obj.getClass()) == cl)37 {38break;39 }40 cl = repCl;41 }4243//如果允许被替换的情况44if (enableReplace) {45 Object rep = replaceObject(obj);46if (rep != obj && rep != null) {47 cl = rep.getClass();48 desc = ObjectStreamClass.lookup(cl, true);49 }50 obj = rep;51 }5253// if object replaced, run through original checks a second time54// 如果对象被替换了,只有是ObjectOutputStream的⼦类才会出现55if (obj != orig) {56 subs.assign(orig, obj);57if (obj == null) {58 writeNull();59return;60 } else if (!unshared && (h = handles.lookup(obj)) != -1) {61 writeHandle(h);62return;64 writeClass((Class) obj, unshared);65return;66 } else if (obj instanceof ObjectStreamClass) {67 writeClassDesc((ObjectStreamClass) obj, unshared);68return;69 }70 }7172// remaining cases73/**序列化不同类型的对象,分别序列化String、数组、枚举类型74 * 对于Integer、Long等都属于实现了Serializable接⼝的数据类型*/75 if (obj instanceof String) {//序列化字符串类型对象76 writeString((String) obj, unshared);77 } else if (cl.isArray()) {//序列化数组类型对象78 writeArray(obj, desc, unshared);79 } else if (obj instanceof Enum) {//序列化枚举类型对象80 writeEnum((Enum<?>) obj, desc, unshared);81 } else if (obj instanceof Serializable) {//序列化实现了Serializable接⼝的数据类型82 writeOrdinaryObject(obj, desc, unshared);83 } else {//抛出不可序列化异常84 if (extendedDebugInfo) {85 throw new NotSerializableException(86 cl.getName() + "\n" + debugInfoStack.toString());87 } else {88 throw new NotSerializableException(cl.getName());89 }90 }91 } finally {92 depth--;93 bout.setBlockDataMode(oldMode);94 }95 }96 }前⾯都是在做各种检查,实际有效的代码就是从75⾏开始,分别针对不同类型的对象分别执⾏不同的序列化⽅法writeString⽅法的逻辑就是将字符串按字节的⽅式进⾏序列化,底层就是通过数组复制的⽅式获取到char[],然后写⼊到缓存的序列化的byte[]数组中1 System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);writeArray⽅法的逻辑就是先判断数组的数据类型是什么,如果是基本数据类型之间写⼊byte数字,如果是对象类型就调⽤writeObject0⽅法writeEnum⽅法的逻辑是直接写⼊枚举的值⽽对于对象类型是⽐较复杂的,也就是writeOrdinaryObject⽅法,逻辑如下:1private void writeOrdinaryObject(Object obj,2 ObjectStreamClass desc,3boolean unshared)4throws IOException5 {6if (extendedDebugInfo) {7 debugInfoStack.push(8 (depth == 1 ? "root " : "") + "object (class \"" +9 obj.getClass().getName() + "\", " + obj.toString() + ")");10 }11try {12//检查ObjectStreamClass对象13 desc.checkSerialize();1415//写⼊标记表⽰是Object类型16 bout.writeByte(TC_OBJECT);17//写⼊Class对象的描述信息18 writeClassDesc(desc, false);19 handles.assign(unshared ? null : obj);20if (desc.isExternalizable() && !desc.isProxy()) {21//写⼊实现了Externalizable接⼝的对象22 writeExternalData((Externalizable) obj);23 } else {24//写⼊实现了Serializable25 writeSerialData(obj, desc);26 }27 } finally {28if (extendedDebugInfo) {29 debugInfoStack.pop();30 }31 }32 }最终按实现了Externalizable接⼝或Serializable接⼝分别执⾏writeExternalData和writeSerialData⽅法,writeSerialData⽅法如下:1private void writeSerialData(Object obj, ObjectStreamClass desc)2throws IOException3 {4//获取类的描述信息对象5 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();6for (int i = 0; i < slots.length; i++) {7 ObjectStreamClass slotDesc = slots[i].desc;8//判断该类是否⾃定义类writeObject⽅法,如果重写了该⽅法则按重写的逻辑处理9if (slotDesc.hasWriteObjectMethod()) {10 ObjectOutputStream.PutFieldImpl oldPut = curPut;11 curPut = null;12 SerialCallbackContext oldContext = curContext;1314if (extendedDebugInfo) {15 debugInfoStack.push(16 "custom writeObject data (class \"" +17 slotDesc.getName() + "\")");18 }19try {20 curContext = new SerialCallbackContext(obj, slotDesc);21 bout.setBlockDataMode(true);22//通过反射的⽅式执⾏⾃定义的writeObejct⽅法23 slotDesc.invokeWriteObject(obj, this);24 bout.setBlockDataMode(false);25 bout.writeByte(TC_ENDBLOCKDATA);26 } finally {27 curContext.setUsed();28 curContext = oldContext;30 debugInfoStack.pop();31 }32 }3334 curPut = oldPut;35 } else {36//如果没有⾃定义writeObject⽅法则按默认的⽅法写⼊属性数据37 defaultWriteFields(obj, slotDesc);38 }39 }40 }先是根据类的描述信息判断是否⾃定义了序列化⽅法writeObejct⽅法,如果⾃定义了就通过反射执⾏invokeWriteObejct⽅法,如果没有⾃定义则执⾏defaultWriteFields⽅法,defaultWriteFields⽅法逻辑如下:1private void defaultWriteFields(Object obj, ObjectStreamClass desc)2throws IOException3 {4 Class<?> cl = desc.forClass();5//校验对象的类信息是否和类描述信息⼀致6if (cl != null && obj != null && !cl.isInstance(obj)) {7throw new ClassCastException();8 }910//11 desc.checkDefaultSerialize();1213int primDataSize = desc.getPrimDataSize();14if (primVals == null || primVals.length < primDataSize) {15 primVals = new byte[primDataSize];16 }17 desc.getPrimFieldValues(obj, primVals);18 bout.write(primVals, 0, primDataSize, false);1920 ObjectStreamField[] fields = desc.getFields(false);//获取所有属性21 Object[] objVals = new Object[desc.getNumObjFields()];//获取对象类型属性22int numPrimFields = fields.length - objVals.length;23 desc.getObjFieldValues(obj, objVals);24for (int i = 0; i < objVals.length; i++) {//遍历对象类型属性数组25if (extendedDebugInfo) {26 debugInfoStack.push(27 "field (class \"" + desc.getName() + "\", name: \"" +28 fields[numPrimFields + i].getName() + "\", type: \"" +29 fields[numPrimFields + i].getType() + "\")");30 }31try {32//递归写⼊对象类型的属性33 writeObject0(objVals[i],34 fields[numPrimFields + i].isUnshared());35 } finally {36if (extendedDebugInfo) {37 debugInfoStack.pop();38 }39 }40 }41 }总结:序列化的整体逻辑就是遍历对象的所有属性,递归执⾏序列化⽅法,直到序列化的对象是String、Array或者是Eunm类,则按String、Array、Enum的序列化⽅式写⼊字节流中。

java jackson序列化bigdecimal科学计数法

java jackson序列化bigdecimal科学计数法在Java中,BigDecimal是用于精确表示和计算浮点数的类。

当涉及到非常大或非常小的数字时,BigDecimal可以避免浮点数计算中的精度丢失问题。

然而,在使用Java中的JSON库Jackson对BigDecimal进行序列化时,可能会遇到一个问题:科学计数法。

科学计数法是一种表示非常大或非常小的数字的方法,它使用指数来表示数字。

例如,1.23E+5表示1.23乘以10的5次方,即12.3万。

Jackson默认情况下会使用科学计数法来序列化BigDecimal,这样可以减小JSON的大小。

但在某些情况下,我们可能需要将BigDecimal以其完全形式进行序列化,而不是使用科学计数法。

那么该如何在Jackson中实现这一需求呢?首先,我们需要引入Jackson的依赖。

在Maven项目中,我们可以在pom.xml中添加以下依赖:```xml<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.12.4</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.4</version></dependency>```接下来,我们可以通过自定义一个BigDecimal序列化器来改变默认的序列化行为。

JAVA基于SnakeYAML实现解析与序列化YAML

JAVA基于SnakeYAML实现解析与序列化YAML这篇⽂章主要介绍了JAVA基于SnakeYAML实现解析与序列化YAML,⽂中通过⽰例代码介绍的⾮常详细,对⼤家的学习或者⼯作具有⼀定的参考学习价值,需要的朋友可以参考下1.概述本⽂,我们将学习如何使⽤库将YAML⽂档转换为Java对象,以及JAVA对象如何序列化为YAML⽂档。

2.项⽬设置要在项⽬中使⽤SnakeYAML,需要添加Maven依赖项(可在此处找到最新版本):<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.25</version></dependency>3.⼊⼝点该YAML类是API的⼊⼝点:Yaml yaml = new Yaml()由于实现不是线程安全的,因此不同的线程必须具有⾃⼰的Yaml实例。

4.加载YAML⽂档SnakeYAML⽀持从String或InputStream加载⽂档,我们从定义⼀个简单的YAML⽂档开始,然后将⽂件命名为customer.yaml:firstName: "John"lastName: "Doe"age: 204.1基本⽤法现在,我们将使⽤Yaml类来解析上述YAML⽂档:Yaml yaml = new Yaml();InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("customer.yaml");Map<String, Object> obj = yaml.load(inputStream);System.out.println(obj);上⾯的代码⽣成以下输出:{firstName=John, lastName=Doe, age=20}默认情况下,load()⽅法返回⼀个Map对象。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

我们可以通过序列化来保存一个对象的状态(实例变量)到文件中,也可以从这个格式化的文件中很容易地读取对象的状态从而可以恢复我们保存的对象。

用来实现序列化的类都在java.io包中,我们常用的类或接口有:ObjectOutputStream:提供序列化对象并把其写入流的方法ObjectInputStream:读取流并反序列化对象Serializable:一个对象想要被序列化,那么它的类就要实现此接口下面我们先通过一个简单的例子演示一起序列化/反序列化的过程Book.javapackage kevin.seria;import java.io.Serializable;public class Book implements Serializable{private int isbn;public Book(int isbn) {super();this.isbn = isbn;}public int getIsbn() {return isbn;}public void setIsbn(int isbn) {this.isbn = isbn;}@Overridepublic String toString() {return"Book [isbn=" + isbn + "]";}}package kevin.seria;import java.io.Serializable;public class Student implements Serializable {private Book book;private String name;public Student(Book book, String name) {super();this.book = book; = name;}public Book getBook() {return book;}public void setBook(Book book) {this.book = book;}public String getName() {return name;}public void setName(String name) { = name;}@Overridepublic String toString() {return"Student [book=" + book + ", name=" + name + "]";}}package kevin.seria;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class Simulator {public static void main(String[] args) {new Simulator().go();}private void go(){Student student = new Student(new Book(2011),"kevin");try {ObjectOutputStream out = newObjectOutputStream(new FileOutputStream("seria"));out.writeObject(student); //System.out.println("object has been written..");out.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}try{ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria"));Student studentRead = (Student) in.readObject();System.out.println("object read here:");System.out.println(studentRead);}catch(FileNotFoundException e){e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}这个程序运行的结果如下:我们可以看到,读取到的对象与保存的对象状态一样。

这里有几点需要说明一下:1、基本类型的数据可以直接序列化2、对象要被序列化,它的类必须要实现Serializable接口;如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。

比如上面的例子中,Student类中有一个Book类型的实例就是,要想让Student的对象成功序列化,那么Book也必须要实现Serializable接口。

3、我们看这个语句:ObjectOutputStream out = new ObjectOutputStream(newFileOutputStream("seria"));我们知道 FileOutputStream类有一个带有两个参数的重载Constructor——FileOutputStream(String,boolean),其第二个参数如果为true且String代表的文件存在,那么将把新的内容写到原来文件的末尾而非重写这个文件,这里我们不能用这个版本的构造函数,也就是说我们必须重写这个文件,否则在读取这个文件反序列化的过程中就会抛出异常,导致只有我们第一次写到这个文件中的对象可以被反序列化,之后程序就会出错。

下面的问题是如果我们上面用到的Book类没有实现Serializable接口,但是我们还想序列化Student类的对象,怎么办。

Java为我们提供了transient这个关键字。

如果一个变量被声明成transient,那么在序列化的过程中,这个变量是会被无视的。

我们还是通过对上面的代码进行小的修改来说明这个问题。

新的Book类不实现Serializable接口package kevin.seria;public class Book{private int isbn;public Book(int isbn) {super();this.isbn = isbn;}public int getIsbn() {return isbn;}public void setIsbn(int isbn) {this.isbn = isbn;}@Overridepublic String toString() {return"Book [isbn=" + isbn + "]";}}因为我们要序列化Student类的对象,所以我们必须实现Serializable接口,然而Book 是Student的一个实例变量,它的类没有实现Serializable接口,所以为了顺序完成序列化,我们把这个实例变量声明为transient以在序列化的过程中跳过它。

package kevin.seria;import java.io.Serializable;public class Student implements Serializable {private transient Book book;private String name;public Student(Book book, String name) {super();this.book = book; = name;}public Book getBook() {return book;}public void setBook(Book book) {this.book = book;}public String getName() {return name;}public void setName(String name) { = name;}@Overridepublic String toString() {return"Student [book=" + book + ", name=" + name + "]";}}Simulator.java和上面的一样,我们看一下运行结果:可以看到,student对象被成功的序列化了。

因为序列化过程中跳过了Book实例,所以当恢复对象状态的时候,它被赋予了默认值null,这也就意味着我们不能使用它。

那如果Book类没有实现Serializable接口,但我们还想对它的状态进行保存,这可以实现吗?答案是肯定的,到底如何肯定,请听小弟我慢慢道来。

Java针对这种情况有一种特殊的机制——一组私有(回调)方法(这个我们马上在代码中看到),可以在要被序列化的类中实现它们,在序列化和的序列化的过程中它们会被自动调用。

所以在这组方法中我们可以调用ObjectOutputStream/ObjectInputStream的一些有用方法来实现对象状态的保存。

下面还是通过例子来说明:Book类和Simulator类都不变,我们来看一下新的Student类:package kevin.seria;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class Student implements Serializable {private transient Book book;private String name;public Student(Book book, String name) {super();this.book = book; = name;}public Book getBook() {return book;}public void setBook(Book book) {this.book = book;}public String getName() {return name;}public void setName(String name) { = name;}//下面这两个方法就是那组特殊的私有方法,它们会在序列化、反序列化的过程中被自动调用//我们必须保证方法的签名和下面的两个方法完全相同//这个方法会在序列化的过程中被调用private void writeObject(ObjectOutputStream out){try {out.defaultWriteObject(); //这个方法会把这当前中非静态变量和非transient变量写到流中//在这里我们就把name写到了流中。

相关文档
最新文档