目录
java序列化反序列化,实体类实现Serializable接口:
实现序列化和反序列化实现Serializable接口原因及注意事项:
-
java序列化反序列化,实体类实现Serializable接口:
java序列化和反序列化
序列化:将对象转换成字节序列的过程,可以将对象持久化到本地磁盘或通过网络传输到远程机器。
反序列化:将字节序列转换回对象的过程
在网络传输中,数据需要以字节流的形式进行传输。字节流是指以字节为单位进行读取和写入的数据流。
而字节序列是指一系列字节的排列顺序。在对象序列化过程中,将对象转换为一连串的字节序列,这些字节按照特定的顺序组织起来,表示对象的各个属性和状态。因此,字节序列可以看作是对象序列化后得到的字节流。
序列化和反序列化的应用场景(本地JVM中运行java实例不需要序列化):
实现序列化和反序列化实现Serializable接口原因及注意事项:
如果要将一个 Java 对象序列化为字节流,那么该对象必须实现Serializable接口。该接口中没有任何方法,只是作为一个标记接口,告诉 JVM,该类可以被序列化,并且需要把哪些属性序列化到字节流中。
而实现Serializable接口还需要注意下面2个点:
-
网络通信:在网络通信中,可以将对象进行序列化后通过网络传输到远端机器,这样可以实现远程过程调用(RPC)、分布式系统的协作等功能。如前端和后端之间需要进行交互和数据传输,这就需要通过网络实现通信。
-
数据持久化:通过序列化,可以将对象保存到磁盘或者数据库中,以便之后读取和恢复对象的状态。这在需要长期存储和恢复对象的情况下非常有用,例如将对象保存到文件系统、缓存、关系型数据库或NoSQL数据库中。
-
缓存机制:序列化和反序列化也广泛应用于缓存中,对于频繁访问的数据,可以将其序列化后存储到缓存中,减少数据库的访问次数,提高系统性能。
-
分布式计算:在分布式计算中,可以通过序列化和反序列化实现数据的传递和共享。例如,通过序列化和反序列化可以将任务对象发送给不同的计算节点,并在节点上执行任务。
-
消息队列:消息队列中的消息通常需要进行序列化和反序列化处理。生产者将消息对象序列化后发送到消息队列,消费者从队列中接收消息并进行反序列化,以获取消息内容。如RabbitMQ 进行消息传递时,序列化和反序列化是非常重要的步骤。
-
进程间通信:在进程间通信(IPC)中,通过序列化和反序列化可以在不同的进程之间传递数据。例如,通过管道、套接字等方式进行进程间通信时,可以将对象序列化后发送给接收方进程进行反序列化处理。
-
-
1.序列化版本号:即指定serialVersionUID的值。如果不显示指定serialVersionUID, JVM在序列化时会根据类的细节自动生成一个serialVersionUID值, 然后与属性一起序列化。(具体来说,JVM 会基于类的名称、访问修饰符、非瞬态和非静态字段以及方法和构造函数等细节计算出一个长整型数值,并将其作为默认的 serialVersionUID 值。) 再进行持久化或网络传输. 在反序列化时, JVM会再根据细节自动生成一个新版serialVersionUID, 然后将这个新版serialVersionUID与序列化时生成的旧版serialVersionUID进行比较, 相同则反序列化成功, 否则报InvalidClassException 异常。
但是这种自动生成的serialVersionUID 可能因为修改类定义而发生改变,从而导致无法正确地进行对象的反序列化。因此,虽然 serialVersionUID 的确可以被自动生成,但是我们无法避免版本不会迭代,类不再修改。因此我们可以指定一个serialVersionUID,防止这种情况的发送。
显式地定义serialVersionUID 常量,可以是任意的 long 常量,但建议使用固定的值,通常是 1L。
@Data @EqualsAndHashCode(callSuper = false) @TableName("t_order") public class Order implements Serializable { //设置了serialVersionUID private static final long serialVersionUID = 1L; /** * 订单ID */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户ID */ private Long userId; /** * 商品ID */ private Long goodsId; /** * 收货地址ID */ private Long deliveryAddrId; /** * 支付时间 */ private Date payDate; }
-
2.transient 关键字:某些属性不应该被序列化到字节流中,可以使用 transient 关键字来标记这些属性。
假设我们有一个需要进行序列化的 Java 类,而因为密码很敏感,不希望在网络传输或存储时暴露出来。所以password 属性被标记为 transient 关键字,意味着该属性不会被序列化到字节流中。
public class User implements Serializable { private String username; private transient String password; public User(String username, String password) { this.username = username; this.password = password; } // 省略 getter 和 setter 方法 }
创建一个 User 对象进行序列化和反序列化操作:
User user = new User("Alice", "123456"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.obj")); out.writeObject(user); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.obj")); User newUser = (User) in.readObject(); in.close();
在序列化时,JVM 会自动将 username 和 password 属性编码为二进制数据流,并写入到字节流中。但是,在反序列化时,JVM 只能恢复 username 属性,而 password 属性则被忽略了。此时,新建的 User 对象的 password 属性值为 null。
解答:现在前后端数据传输和存储都使用JSON这种数据格式了,让公共返回对象实现Serializable接口,实现序列化成字节序列。这是使用@responseBody,又转成json返回形式进行网络传输,JSon序列后的字符串会自动转成字节流吗?是怎么样的流程?
JSON序列化:将Java对象转换为JSON字符串的过程。
JSON反序列化是将JSON字符串转换为Java对象的过程。
JSON 是存储和交换文本信息的语法。类似 XML。Spring Boot 中,通常使用 Jackson 库来处理 JSON 数据的序列化和反序列化。在 TCP/IP 网络传输中;JSON 字符串会被转换为字节流进行传输。传输过程中,JSON 字符串会被转换为字节流进行传输:
例如:序列化
ObjectMapper mapper = new ObjectMapper(); User user = new User(); String json = mapper.writeValueAsString(user); byte[] bytes = json.getBytes("UTF-8");
反序列化
byte[] bytes = receiveJsonFromNetwork(); String json = new String(bytes, "UTF-8"); ObjectMapper mapper = new ObjectMapper(); USer user = mapper.readValue(json, User.class);
我们也可以使用JSON工具类
@Component public class JsonUtil { private static ObjectMapper objectMapper = new ObjectMapper(); /** * Object转json字符串 * * @param obj * @param <T> * @return */ public static <T> String object2JSonStr(T obj) { if (obj == null) { return null; } try { return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); } catch (Exception e) { System.out.println("Parse object to String error"); e.printStackTrace(); return null; } } /** * Object转json字符串并格式化美化 * * @param obj * @param <T> * @return */ public static <T> String obj2StringPretty(T obj) { if (obj == null) { return null; } try { return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); } catch (Exception e) { System.out.println("Parse object to String error"); e.printStackTrace(); return null; } } /** * string转object * * @param str json字符串 * @param clazz 被转对象class * @param <T> * @return */ public static <T> T JsonStr2Object(String str, Class<T> clazz) { if (StringUtils.isEmpty(str) || clazz == null) { return null; } try { return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz); } catch (IOException e) { System.out.println("Parse String to Object error"); e.printStackTrace(); return null; } } /** * string转object 用于转为集合对象 * * @param str json字符串 * @param collectionClass 被转集合class * @param elementClasses 被转集合中对象类型class * @param <T> * @return */ public static <T> T JsonStr2Object(String str, Class<?> collectionClass, Class<?>... elementClasses) { JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); try { return objectMapper.readValue(str, javaType); } catch (IOException e) { System.out.println("Parse String to Object error"); e.printStackTrace(); return null; } } }
-
步骤:先使用Jackson 的
ObjectMapper
将 Java 对象转换为 JSON 数据时,ObjectMapper
会将 JSON 数据序列化为字节序列。当从网络中接收到字节序列后,ObjectMapper
可以将其反序列化为 JSON 数据,再转换为对应的 Java 对象。
-
首先,将 JSON 字符串按照某种编码方式(如 UTF-8)进行编码,将字符串中的每个字符转换为对应的字节序列;
-
然后,将编码后的字节序列按照客户端和服务器之间的约定进行传输;
-
最后,在接收端,根据约定的编码方式将接收到的字节序列解码为 JSON 字符串。
-