`
zl378837964
  • 浏览: 190148 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

Entity实体类为什么要实现Serializable接口才能被序列化

阅读更多

       引起这个疑问,还是从Hibernate使用查询缓存说起;对象实例除了存在于内存,二级缓存还会将对象写进硬盘在需要的时候再读取出来使用,此时就必须提到一个概念:序列化。

 

       程序在运行时实例化出对象,这些对象存在于内存中,随着程序运行停止而消失,但如果我们想把某些对象(一般都是各不相同的属性)保存下来或者传输给其他进程,在程序终止运行后这些对象仍然存在,可以在程序再次运行时读取这些对象的信息,或者在其他程序中利用这些保存下来的对象信息恢复成实例对象。这种情况下就要用到对象序列化和反序列化

       其实很早就知道的,在Java中常见的几个类,如:Interger/String等,都实现了java.io.Serializable接口。这个序列化接口没有任何方法和域,仅用于标识序列化语意;实现 Serializable 接口的类是可序列化的,没有实现此接口的类将不能被序列化和反序列化。序列化类的所有子类本身都是可序列化的,不再需要显式实现 Serializable 接口。只有经过序列化,才能兼容对象在磁盘文本以及在网络中的传输,以及恢复对象的时候反序列化等操作。

 

问题一:为何要实现序列化?

答:序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。

简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象

  

// 经常使用如下:
public static void main(String[] args) throws Exception {
    File file = new File("user.ser");

    ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
    User user = new User("zhang", 18, Gender.MALE);
    oout.writeObject(user);
    oout.close();

    ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
    Object newUser = oin.readObject();
    oin.close();
    System.out.println(newUser);
}

 

       如没有 实现Serializable接口,在序列化时,使用ObjectOutputStream的write(object)方法将对象保存时将会出现异常。其实 java.io.Serializable 只是一个没有属性和方法的空接口,但是问题来了。。。

 

问题二:为何一定要实现 Serializable 才能进行序列化呢?

 

使用 ObjectOutputStream 来持久化对象, 对于此处抛出的异常,查看该类中实现如下:

 

private void writeObject0(Object obj, boolean unshared) throws IOException {
    // ...
            // remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
    // ...
}
 从此可知,如果被写对象类型是String、数组、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。

  

最后提点注意:

1、在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,如此引用传递序列化。如果一个对象包含的成员变量是容器类等并深层引用,那么序列化过程开销也较大。

2、当字段被声明为 transient 后,默认序列化机制就会忽略该字段。(还有方法就是自定义writeObject方法,见下代码示例)

3、在单例类中添加一个readResolve()方法(直接返回单例对象),以保证在序列化过程仍保持单例特性。

 

此外补充一下,

    在路径下jdk中还有另外一种形式的对象持久化,即:外部化(Externalization)。

public interface Externalizable extends java.io.Serializable {
  void writeExternal(ObjectOutput out) throws IOException;
  void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

    外部化和序列化是实现同一目标的两种不同方法。

 

       通过 Serializable 接口对对象序列化的支持是jdk内支持的 API ,但是java.io.Externalizable的所有实现者必须提供读入和写出的具体实现,怎么实现完全由你自定义。序列化(Serializable )会自动存储所有必要的信息(如属性以及属性类型等),用以反序列化成原来一样的实例,而外部化(Externalizable)则只保存被存储实例中你需要的信息。

示例代码如下:

 

public class User implements Externalizable {
    private String name;
    transient private Integer age;  // 屏蔽字段
    private Gender gender;

    public User() {
        System.out.println("none constructor");
    }

    public User(String name, Integer age, Gender gender) {
        System.out.println("arg constructor");
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    // 实现读写
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(age);
        // 屏蔽gender
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        age = in.readInt();
    }

    // 具体重写
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);
        // 屏蔽gender
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = in.readInt();
    } 
}
 注意,用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。实现Externalizable接口的类必须要提供一个无参的构造器,且访问权限为 public。

 

        

 

分享到:
评论

相关推荐

    java 序列化 将实体类本地序列化

    首先,要实现序列化,一个Java类必须实现`Serializable`接口。这是一个标记接口,没有定义任何方法,只是告诉Java虚拟机(JVM)这个类的对象是可以序列化的。例如: ```java public class MyEntity implements ...

    mysql自动生成实体类

    此外,实体类还需要实现序列化接口(Serializable),以便于在网络传输或持久化时使用。在生成的实体类中,可能会包含一些特殊的注解,如`@Entity`(表示这是一个实体类)、`@Table`(指定对应的数据库表名)、`@Id`...

    oracle实体类代码生成器

    5. **注释和序列化支持**:为了提高代码可读性和与其他框架的兼容性,生成的实体类可能还包括JPA的注解(如@Entity、@Id等)和Java Serializable接口。 6. **文件输出**:最后,工具将生成的实体类代码保存到指定的...

    实体类生成器

    4. **序列化接口**:为了支持对象的序列化和反序列化,实体类通常会实现`java.io.Serializable`接口。 5. **注解**:在ORM框架中,实体类通常需要使用注解来指定与数据库表的关系。例如,Hibernate使用的`@Entity`...

    Java实现实体类拷贝[深,很深的那种...]

    - **序列化和反序列化**:如果实体类实现了`Serializable`接口,可以利用`ObjectOutputStream`和`ObjectInputStream`进行序列化和反序列化实现深拷贝。这种方式的优点是简单,但缺点是效率较低,且所有属性都需要...

    MySql转Java实体类

    - **序列化**:如果你希望实体类可以被序列化,记得添加`Serializable`接口。 - **注解配置**:ORM框架如Hibernate或MyBatis可能需要额外的注解来配置实体类,如`@Table`、`@Column`等。 5. **辅助工具和框架** ...

    根据数据库生成实体类的一个工具

    这可能包括字段的命名规则、是否生成equals()和hashCode()方法、是否添加序列化注解`@Serializable`,甚至是否启用Lombok库来简化getter和setter的生成。模板配置的灵活性确保了代码风格的一致性,同时也满足了不同...

    asp.net 自跟踪实体模板 解决删除实体后保存不成功的问题

    - 在生成自跟踪实体模板时,需要确保实体类实现了必要的接口(例如`IObjectWithChangeTracker`),以便正确地管理这些状态。 2. **变化跟踪接口** - **IObjectWithChangeTracker**:这个接口定义了用于管理实体...

    c# XML 与实体相互转换 源代码

    在压缩包中的`xmlEntity`文件可能是实现这些转换的源代码,包括实体类定义和转换方法的具体实现。分析并理解这些代码可以帮助我们更深入地了解作者是如何处理XML和实体对象的转换的。 总的来说,XML与实体对象的...

    微服务组件(nacos、dubbo、gateway、sentinal、rocketmq、redis、docker等)

    在微服务架构中,实体类需要实现Serializable接口,以便在不同服务之间传递数据。但是,如果项目中不需要将实体对象传输或存储在不同的系统之间,那么可能不需要让每个实体类都实现Serializable。 在分布式系统中,...

    Array老师-快速开发技术视频配套模板代码entity.txt

    5. **序列化支持**:为了在不同组件间传递或者持久化对象,Entity类往往需要实现`Serializable`接口。 6. **JPA规范**:如果使用了JPA,那么Entity类需要遵循其规定,如避免使用final、static和transient关键字,...

    hello-dubbo-entity:起源

    因此,实体类需要实现Serializable接口,确保能被正确序列化和反序列化。 五、总结 "hello-dubbo-entity"项目揭示了在Dubbo环境中如何有效地利用实体类进行数据交换。理解实体类在Dubbo框架下的工作原理和使用方式...

    Hibernate复合主键

    Serializable 接口是用于标识一个类可以被序列化的。在复合主键类中,我们需要实现 Serializable 接口,以便 Hibernate 框架可以正确地序列化和反序列化复合主键。 复合主键在 Hibernate 中的使用 在 Hibernate 中...

    MVC总结大全

    如果某个类实现了序列化接口(通常是`Serializable`),那么它的子类也可以被序列化。值得注意的是,`static`和`transient`修饰的成员变量不会被序列化,因为它们分别表示类的状态和对象的临时数据,不是对象持久化...

    通过 ViewState 保存 Self-Tracking Entities

    默认情况下,由于生成的STE类没有实现`[Serializable]`接口或`ISerializable`接口,所以无法直接通过ViewState进行序列化和反序列化。 为了解决这个问题,我们需要对STE的T4模板进行修改,以使它们支持序列化。T4是...

    java基于socket网络编程的超市收银管理系统.doc

    2. **Serializable接口**: 在`Cash`、`ShangPin`和`User`类中都实现了Serializable接口,这是因为在Java中,当对象需要在网络间传输或者持久化存储时,必须实现Serializable接口。序列化允许将对象的状态转换为字节...

    持久层课件

    在Java中,可以通过实现Serializable接口来实现对象的序列化,将对象转换为字节流并写入文件,然后在需要时反序列化读取。然而,对象间的引用关系处理起来较为复杂,需要确保整个对象引用闭包(Persistence closure...

    表生成代码MybatisGenerator

    1. Entity类:对应数据库表的字段,包含getter/setter方法,实现了Serializable接口,便于序列化。 2. Mapper接口:定义了数据库操作的CRUD方法。 3. Mapper XML文件:包含了SQL语句的具体实现,与Mapper接口相对应...

Global site tag (gtag.js) - Google Analytics