文章目录

  • 对象流
  • 1、定义:
  • 2、注意事项:
  • 序列化和反序列化
  • 1、定义:
  • 2、序列化特点:
  • 3、serialVersionUID
  • 4、Transient关键字
  • 5、ArrayList源码再研究
  • 6、Serializable 接口
  • 7、序列化实例


对象流

1、定义:

对象流:ObjectXXX 例:ObjectInputStream 字节输入流/ObjectOutputStream 字节输出流
例如:ObjectInputStream 、ObjectOutputStream

2、注意事项:

对象流使用时,自定义对象必须实现Serializable接口,否则会抛出java.io.NotSerializableException

序列化和反序列化

1、定义:

序列化是一种对象持久化的手段:指将对象转换为字节流的过程
反序列化是根据字节流回复对象的过程

2、序列化特点:

1、在java中,只要一个类实现了java.io.Serializable接口,那么他就可以被序列化,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种
2、通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化
3、虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致
一个非常重要的一点是两个类的序列化ID是否一致(就是 private static final long serialVersionUID)
4、序列化并不保存静态变量 (假如类中的变量是静态的,则不会做出相应的序列化)
5、要想将父类对象也序列化,就需要让父类也实现Serial接口
6、Transient关键字的作用是控制变量的序列化,在变量声明的前面加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被替代 如:int 0,对象型是null
7、transient的作用:服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的秘钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化安全
8、在类中增加writeObject和readObject方法可以实现自定义序列化策略

3、serialVersionUID

作用:(用来表明 串行化版本统一标识符)
有两种生成方式:
1、一个是默认的1L
2、另一种是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。UID的默认值依赖JAVA编译器,对同一个类,不同编译器编译可能导致UID不同

显示定义UID的作用:
1、确保不同版本有不同的UID
2、序列化一个类实例,更改一个字段或者添加一个字段,不设置UID,任何更改导致无法反序列化实例,并抛出异常,如果添加UID,反序列化旧实例,新增加或者 更改字段被设为初始值,对象为null

4、Transient关键字

**Transient关键字的作用:**是控制变量的序列化,在变量声明的前面加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被替代 如:int 0,对象型是null
**transient的使用场景:**服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的秘钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化安全

5、ArrayList源码再研究

在ArrayList中,element被transient修饰,为什么还能反序列化呢?
(自定义的writeObject 和 readObject 方法)

在序列化中,如果序列化的类中定义了writeObject 和 readObject 方法
虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化 如果没有这样的方法,则默认调用ObjectOutputStream 的 defaultWriteObject 方法以及用户自定义的writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态的改变序列化的数值
增加writeObject方法的跟踪:
writeObject — writeObject0— writeOrdinaryObject—writeSerialData—invokeWriteObject
通过反射来调用ArrayList中的writeObject和readObject方法

transient修饰element的作用:
假如 ArrayList是100个空间,add了一个数据,还剩99个空间为null,直接对element进行序列化,真正有用的只有一个,效率太低,源码按需序列化(size)

6、Serializable 接口

Serializable 接口是一个空接口,是如何保证序列化和反序列化
在writeObject()-》writeObject0()

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());
                }
7、序列化实例

创建一个person类

import java.io.Serializable;

/**
 *
 */
public class Person  implements Serializable  {
    private static final long serialVersionUID = 2664435368610967824L;
    //private static final long serialVersionUID=1800778823450372341L;
    private int id;
    private String name;


    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

读写person类

import java.io.*;
import java.util.ArrayList;

public class ObjectDome {
    public static void main(String[] args) {
        String path="C:\\Users\\Administrator\\Desktop\\Test.txt";
        writeObject(path );
        readObject(path );

    }
    //读对象操作
    public static  void readObject(String path){
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
            ArrayList <Integer > person =(ArrayList  )objectInputStream .readObject() ;
            //System.out.println(person.getId() +person .getName() );
            System.out.println(person.toString() );
            objectInputStream .close() ;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }



    }
    //写对象操作
    public static  void writeObject(String path){
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path) );
            //Person person = new Person(1, "zhangsan");
            ArrayList <Integer >integers =new ArrayList<>() ;
            integers .add(1);

            objectOutputStream .writeObject(integers   );
            objectOutputStream .close() ;


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //
}

控制台输出写入的【1】