Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Java serialization stream ObjectOutputStream, ByteArrayInputStream, memory operation stream ByteArrayOutputStream, ByteArrayInputStream, and the implementation of deep cloning are introduced in detail.

Serialized streams and deserialized streams

1.1 Concept of serialization

Serialization: Java object data in memory is somehow stored in a disk file or transferred to another network node (over the network). This process is called serialization. In plain English, it is the process of converting data structures or objects into binary strings.

Deserialization: The process of restoring object data from disk files or network nodes to the Java object model. This is the process of converting binary strings generated during serialization into data structures or objects.

How does Java serialize?

  1. Classes that need to serialize objects must implement the serialization interface: java.lang.Serializable interface (which is a token interface without any abstract methods). Most classes in Java implement this interface, such as String and Integer
  2. Serialization is only allowed if the current object is an instanceof Serializable, and instanceof Serializable is used for Java objects.
  3. Serialization and deserialization are done in Java by serializing an object stream:
    1. ObjectOutputStream: Serialization via writeObject().
    2. ObjectInputStrean: Deserialize with the readObject() method.

Issues involved:

  1. If some field data does not need to be deserialized, add transient to the front of the field.
  2. Serialization version problem, after the serialization operation, due to the upgrade or modification of the project, we may modify the serialized object, such as adding a certain field, then we will report an error when deserialization.
  3. Solution: Add a serialVersionUID field to the JavaBean object to fix the version. No matter how we change it, the version is the same and we can deserialize it.
private static final long serialVersionUID =  xxxxxxxxL;  
Copy the code

1.2 ObjectOutputStream Serialized stream

public class ObjectOutputStream
extends OutputStream
implements ObjectOutput.ObjectStreamConstants
Copy the code

ObjectOutputStream writes basic data and objects to OutputStream. Persistent storage of objects can be achieved by using files in the stream.

Storing the object’s information in binary form in external files or streams (also serialized as data is transmitted over the network) is a process called serialization.

1.2.1 the constructor

public ObjectOutputStream(OutputStream out)

Creates an ObjectOutputStream that writes the specified OutputStream. This constructor writes part of the serialized stream to the underlying stream.

1.2.2 API methods

public final void writeObject(Object obj)

Writes the specified object to an ObjectOutputStream. The class of the object, the signature of the class, and the values of the non-transient and non-static fields of the class and all of its supertypes are written.

Note: Serialized objects must implement a serialization interface, Serializable, or else throw a runtime exception NotSerializableException: an object to be serialized does not implement the Java.io.Serializable interface.

1.3 ObjectInputStream Deserializes the stream

public class ObjectInputStream
extends InputStream
implements ObjectInput.ObjectStreamConstants
Copy the code

ObjectInputStream deserializes basic data and objects previously written using ObjectOutputStream.

Reading binary information from an external file into memory is a process called deserialization.

In fact, if we can find an object’s class file, we can deserialize it by calling ObjectInputStream to read the serialized data of the object.

For a JVM to deserialize an object, it must be a class that can find a class file. If the class file for that class is not found, a ClassNotFoundException is thrown.

1.3.1 constructor

public ObjectInputStream(InputStream in)

Creates an ObjectInputStream that reads from the specified InputStream.

1.3.2 API methods

public final Object readObject()

Read objects from ObjectInputStream. The class of the object, the signature of the class, and the values of the class and all non-transient and non-static fields of its supertype are read. Returns the Object Object, which requires strong rotation when used.

Note: This method reconstructs a new object, which is equivalent to a deep clone of the object, but does not trigger a constructor call. Remember to close both streams after use.

2 Memory operation flow

The flow of memory operations is typically used to process temporary information, because temporary information does not need to be saved and can be deleted after use. Can also achieve deep cloning!

2.1 ByteArrayOutputStream ByteArrayOutputStream

public class ByteArrayOutputStream
extends OutputStream
Copy the code

Features:

  1. Internally encapsulates a byte array, which is automatically expanded in size.
  2. The toString() or toByteArray() method retrieves the data inside the array.
  3. The stream is not associated with the underlying file, so it does not need to be closed when used. It can continue to be used even after it is closed.
  4. This stream does not throw ioExceptions because it does not interact with files or the network.

2.1.1 the constructor

public ByteArrayOutputStream()

Create a new byte array output stream. The size of the buffer is initially 32 bytes. Increase its size if necessary.

No purpose is specified; the purpose is an internal byte array.

public ByteArrayOutputStream(int size)

Creates a new byte array output stream with a specified size of buffer capacity in bytes.

2.1.2 API methods

write(int num);

Write a value of type int

write(byte b[] );

Write the byte array B to the array encapsulated inside the object:

toString();

Return: String for the data in the internal array

toByteArray();

Writes the data from the internal array to an external byte array.

writeTo(OutputStream out);

Output internal data to an external file.

close();

Closing is invalid. There is no association with the underlying file.

2.2 ByteArrayInputStream ByteArrayInputStream

public class ByteArrayInputStream
extends InputStream
Copy the code

Features:

  1. An internal byte array is encapsulated.
  2. Instead of specifying a file or input stream, you specify an array of bytes.
  3. The stream is not associated with the underlying file, so it does not need to be closed when used. After it is closed, it can still be used.
  4. This stream does not throw ioExceptions because it does not interact with files or the network.

2.2.1 the constructor

public ByteArrayInputStream(byte[] buf)

Create a ByteArrayInputStream with buF as its buffer array. This buffer array is not copied.

public ByteArrayInputStream(byte[] buf,int offset,int length)

Create ByteArrayInputStream with buF as its buffer array. The initial value of pos is offset, and the initial value of count is the minimum of offset+length and buf.length. This buffer array is not copied. Sets the mark of this buffer to the specified offset.

1.1.1.2. API method

public int read()

Reads the next byte of data from this input stream. Returns an int byte value in the range 0 to 255. If no bytes are available because the end of the stream has been reached, the value -1 is returned. This read method does not block.

public int read(byte[] b)

Read the data into the byte array B

close();

Close the stream. This operation is invalid.

2.3 Other Memory Operation Flows

Operation character array: CharArrayReader, CharArrayWrite

public char[] toCharArray()

Operation strings: StringReader, StringWriter

public StringBuffer getBuffer()

4 cases

4.1 Memory flow Operations

@Test
public void test1(a) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write("How are you?".getBytes());
    baos.write("I'm fine.".getBytes());
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    byte[] bytes = new byte[1024];
    int read;
    while((read = bais.read(bytes)) ! = -1) {
        System.out.println(new String(bytes, 0, read)); }}Copy the code

4.2 Implementing deep cloning

A Java deep clone can be implemented in memory through a combination of serialization and deserialization streams and in-memory operation streams.

/ * * *@author lx
 */
public class ObjectIO {
    public static void main(String[] args) {
        Student stu = new Student("Independently".11);
        Teacher t = new Teacher("WeiLong", stu);
        Teacher t2 = null;
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(t);
            bais = new ByteArrayInputStream(baos.toByteArray());
            ois = new ObjectInputStream(bais);
            t2 = (Teacher) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(baos ! =null) baos.close();
                if(oos ! =null) oos.close();
                if(bais ! =null) bais.close();
                if(ois ! =null) ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                baos = null;
                oos = null;
                bais = null;
                ois = null; }}// Test the deep clone
        System.out.println(t);
        t2.getStu().setName("Xiao li"); System.out.println(t2); }}class Teacher implements Serializable {
    private static final long serialVersionUID = 4L;
    private String name;
    private Student stu;

    public Teacher(String name, Student stu) {
        this.name = name;
        this.stu = stu;
    }

    @Override
    public String toString(a) {
        return "Teacher{" +
                "name='" + name + '\' ' +
                ", stu=" + stu +
                '} ';
    }

    public String getName(a) {
        return name;
    }

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

    public Student getStu(a) {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu; }}class Student implements Serializable {
    private static final long serialVersionUID = 42L;
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName(a) {
        return name;
    }

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

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString(a) {
        return "Student{" +
                "name='" + name + '\' ' +
                ", age=" + age +
                '} '; }}Copy the code

If you need to communicate, or the article is wrong, please leave a message directly. In addition, I hope to like, collect, pay attention to, I will continue to update a variety of Java learning blog!