首页  >  问答  >  正文

java - 为什么Serializable中定义的Class 不能序列化?

Serialized 类中的

Fields 本身必须是可序列化的或瞬态的,即使该类从未显式序列化或反序列化。这是因为在负载下,大多数 J2EE 应用程序框架都会将对象刷新到磁盘,而具有非瞬态、不可序列化数据成员的所谓可序列化对象可能会导致程序崩溃,并为攻击者打开大门。

此规则在非可序列化字段以及非私有字段(因为可以在外部为它们分配非序列化值)以及在类内为它们分配非序列化类型时引发集合字段问题。

不合规代码示例

雷雷
欧阳克欧阳克2667 天前1213

全部回复(2)我来回复

  • 扔个三星炸死你

    扔个三星炸死你2017-06-30 09:58:37

    一个对象序列化时,按照Java默认的序列化规则,对象内的所有成员都要序列化,也就是说,这些Class都必须实现Serializable。

    所以,你有两种改法,一是Address实现Serializable接口,二是对Person中的address成员加上transient标记,这样该成员就不会被序列化进去。

    回复
    0
  • typecho

    typecho2017-06-30 09:58:37

    如果 address 成员需要进行序列化的话,则Address类也需要实现Serializable接口。
    如果 address 成员不需要进行序列化的话,可以加上transient关键字,则address成员不做序列化操作,值为null。如下:

    public class Person implements Serializable {
      private static final long serialVersionUID = 1905122041950251207L;
    
      private String name;
      private transient Address address;  // Noncompliant; Address isn't serializable
    }

    当然还有其他方式:
    比如实现Externalizable接口,重写readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法。
    还有一个替代实现Externalizable接口方法,还是实现Serializable接口,添加writeObject(ObjectOutputStream obs)和readObject(ObjectInputStream ois)方法。


    再说说为什么Address一定要实现Serializable,或者加上transient关键字Person才能进行序列化?
    先看看不做处理,使用 ObjectOutputStream 来持久化对象,抛出的异常

    Exception in thread "main" java.io.NotSerializableException

    ObjectOutputStream源码:

    /**
         * Underlying writeObject/writeUnshared implementation.
         */
        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());
                    }
                }
            } finally {
                depth--;
                bout.setBlockDataMode(oldMode);
            }
        }

    从此可知, 如果被写对象类型是String、Array、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。且在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化。

    回复
    0
  • 取消回复