Java tutorial
/* * Copyright (c) 2004 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 3nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose, * including teaching and use in open-source projects. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book, * please visit http://www.davidflanagan.com/javaexamples3. */ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Serializable; /** * A simple class that implements a growable array of ints, and knows how to * serialize itself as efficiently as a non-growable array. */ public class SerialIntList implements Serializable { // These are the fields of this class. By default the serialization // mechanism would just write them out. But we've declared size to be // transient, which means it will not be serialized. And we've // provided writeObject() and readObject() methods below to customize // the serialization process. protected int[] data = new int[8]; // An array to store the numbers. protected transient int size = 0; // Index of next unused element of array /** Return an element of the array */ public int get(int index) { if (index >= size) throw new ArrayIndexOutOfBoundsException(index); else return data[index]; } /** Add an int to the array, growing the array if necessary */ public void add(int x) { if (data.length == size) resize(data.length * 2); // Grow array if needed. data[size++] = x; // Store the int in it. } /** An internal method to change the allocated size of the array */ protected void resize(int newsize) { int[] newdata = new int[newsize]; // Create a new array System.arraycopy(data, 0, newdata, 0, size); // Copy array elements. data = newdata; // Replace old array } /** * Get rid of unused array elements before serializing the array. This may * reduce the number of array elements to serialize. It also makes data.length == * size, so there is no need to safe the (transient) size field. The * serialization mechanism will automatically call this method when * serializing an object of this class. Note that this must be declared * private. */ private void writeObject(ObjectOutputStream out) throws IOException { if (data.length > size) resize(size); // Compact the array. out.defaultWriteObject(); // Then write it out normally. } /** * Restore the transient size field after deserializing the array. The * serialization mechanism automatically calls this method. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // Read the array normally. size = data.length; // Restore the transient field. } /** * Does this object contain the same values as the object o? We override this * Object method so we can test the class. */ public boolean equals(Object o) { if (!(o instanceof SerialIntList)) return false; SerialIntList that = (SerialIntList) o; if (this.size != that.size) return false; for (int i = 0; i < this.size; i++) if (this.data[i] != that.data[i]) return false; return true; } /** We must override this method when we override equals(). */ public int hashCode() { int code = 1; // non-zero to hash [0] and [] to distinct values for (int i = 0; i < size; i++) code = code * 997 + data[i]; // ignore overflow return code; } /** A main() method to prove that it works */ public static void main(String[] args) throws Exception { SerialIntList list = new SerialIntList(); for (int i = 0; i < 100; i++) list.add((int) (Math.random() * 40000)); SerialIntList copy = (SerialIntList) Serializer.deepclone(list); if (list.equals(copy)) System.out.println("equal copies"); Serializer.store(list, new File("intlist.ser")); } } class Serializer { /** * Serialize the object o (and any Serializable objects it refers to) and * store its serialized state in File f. */ static void store(Serializable o, File f) throws IOException { ObjectOutputStream out = // The class for serialization new ObjectOutputStream(new FileOutputStream(f)); out.writeObject(o); // This method serializes an object graph out.close(); } /** * Deserialize the contents of File f and return the resulting object */ static Object load(File f) throws IOException, ClassNotFoundException { ObjectInputStream in = // The class for de-serialization new ObjectInputStream(new FileInputStream(f)); return in.readObject(); // This method deserializes an object graph } /** * Use object serialization to make a "deep clone" of the object o. This * method serializes o and all objects it refers to, and then deserializes * that graph of objects, which means that everything is copied. This differs * from the clone() method of an object which is usually implemented to * produce a "shallow" clone that copies references to other objects, instead * of copying all referenced objects. */ static Object deepclone(final Serializable o) throws IOException, ClassNotFoundException { // Create a connected pair of "piped" streams. // We'll write bytes to one, and them from the other one. final PipedOutputStream pipeout = new PipedOutputStream(); PipedInputStream pipein = new PipedInputStream(pipeout); // Now define an independent thread to serialize the object and write // its bytes to the PipedOutputStream Thread writer = new Thread() { public void run() { ObjectOutputStream out = null; try { out = new ObjectOutputStream(pipeout); out.writeObject(o); } catch (IOException e) { } finally { try { out.close(); } catch (Exception e) { } } } }; writer.start(); // Make the thread start serializing and writing // Meanwhile, in this thread, read and deserialize from the piped // input stream. The resulting object is a deep clone of the original. ObjectInputStream in = new ObjectInputStream(pipein); return in.readObject(); } /** * This is a simple serializable data structure that we use below for testing * the methods above */ public static class DataStructure implements Serializable { String message; int[] data; DataStructure other; public String toString() { String s = message; for (int i = 0; i < data.length; i++) s += " " + data[i]; if (other != null) s += "\n\t" + other.toString(); return s; } } /** This class defines a main() method for testing */ public static class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { // Create a simple object graph DataStructure ds = new DataStructure(); ds.message = "hello world"; ds.data = new int[] { 1, 2, 3, 4 }; ds.other = new DataStructure(); ds.other.message = "nested structure"; ds.other.data = new int[] { 9, 8, 7 }; // Display the original object graph System.out.println("Original data structure: " + ds); // Output it to a file File f = new File("datastructure.ser"); System.out.println("Storing to a file..."); Serializer.store(ds, f); // Read it back from the file, and display it again ds = (DataStructure) Serializer.load(f); System.out.println("Read from the file: " + ds); // Create a deep clone and display that. After making the copy // modify the original to prove that the clone is "deep". DataStructure ds2 = (DataStructure) Serializer.deepclone(ds); ds.other.message = null; ds.other.data = null; // Change original System.out.println("Deep clone: " + ds2); } } }