• 为什么要序列化?

    由于需要在不同的主机中进行数据的传递,而这样的数据需要转换成字节序列方可进行传输。这也就是需要将对象先序列化成字节序列,接收方再进行反序列化将字节序列转换成Java对象。

  • 如何进行序列化?

    public class SerializableDemo {
        public static void main(String[] args) throws IOException {
            StudentSerializable studentSerializable = new StudentSerializable();
            studentSerializable.setAge(1);
            studentSerializable.setName("KKGS");
            studentSerializable.setSno("111");
            String path = "C:\\Users\\KKGS\\Desktop\\student.txt";
            serializable(studentSerializable,path);
            Object deserializable = deserializable(path);
            System.out.println(deserializable);
        }
    
        private static void serializable(StudentSerializable studentSerializable,String path) throws IOException {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File(path)));
            try {
                objectOutputStream.writeObject(studentSerializable);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                objectOutputStream.close();
            }
        }
    
        private static Object deserializable(String path) throws IOException {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File(path)));
            Object object = null;
            try {
                object = objectInputStream.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                objectInputStream.close();
            }
            return object;
        }
    
    }
    
  • serializable接口作用
    点入该接口发现,该接口是空的,由此可猜想该接口应该属于标记型接口(类是与RandomAccess)用于标记是否应用某种能力,在此将serizable接口实现去掉,找到报错处发现

    // 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、array、enum、或者实现serizable接口的将会抛出异常,由此serizable接口并不是进行序列化的实际操作,而是标记该对象可进行序列化。

  • serizable所需的序列号有何用?
    用于确保序列化及其反序列化后二者是同一对象。若二者序列号不一致则将不可进行反序列。
    若未声明序列号,系统将会为对应对象生成一个一个默认序列号,当类属性改变了序列号也会随之变化,因此为了保证对象的唯一性最好还是显式的将序列号设置。

  • 注意!!!!
    **对于用 **static 及其 transient 修饰的字段是不会被序列化的

    • 不能序列化static是由于进行序列化时存储的是对象信息,而非类信息,因此static修饰的字段是无法被序列化的。
    • 不能序列化transient是以为,被该关键字修饰的字段仅存在于内存之中。(对于敏感字段入密码可使用该关键字修饰,使得在传输时该字段不会被泄露)
  • 序列化控制
    为了控制由于在数据传输时,中间字节流被窃取并篡改成严重背离真实的值,可以在实体类中新增readObject方法进行成员变量的约束。

        private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
            // 默认反序列化函数
            objectInputStream.defaultReadObject();
            //手工检查 反序列化变量的合法性
            if (age < 0 || age > 100){
                throw new IllegalArgumentException("年龄必须在0到100之间!!!");
            }
        }
    

    在进行反序列化时,若年龄不在规定的范围内则将抛出异常。在进行反序列化时,ObjectStreamClass会通过反射调用readObject方法,由此若实体中有readObject方法则会被调用。