Java Serialization 2013

Материал из SEWiki
Версия от 15:10, 22 мая 2013; Snurk (обсуждение | вклад) (1. Serialization.java (основная часть))

Перейти к: навигация, поиск

Что почитать

1. Serialization.java (основная часть)

import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;



/**
 * Лекция о сериализации в Java.
 */
@SuppressWarnings({"unused"})
public class Serialization {

	/****Введение****/
	
	/**
	 * 
	 * Сериализация -- запись объекта в байтовый поток.
	 * 
	 * Десереализация -- CO.
	 * 
	 * Все в пакете java.io
	 * 
	 * Мотивация:
	 * 	1. dump (ex. JavaBeans persistence)
	 * 	2. RMI
	 */
	class Point {
		double x;
		double y;
	}
	

	
	
	//move to Point1.java and SimplePointTest.java
	
	
	/****Процесс сериализации****/
	
	/**
	 * В сериализации участвуют ВСЕ достижимые объекты.
	 * 
	 * Запись:
	 * 	1. Запись предка (если он сериализуем)
	 * 	2. Запись полей (если они сериализуемы)
	 * 
	 * Циклические зависимости корректно обрабатываются автоматически.
	 * 
	 * Чтение:
	 * 	1. Выделение памяти под объект
	 * 	2. Чтение предка
	 * 	3. Чтение полей
	 */
	
	/**
	 * Все поля должны быть сериализуемы!
	 */
	@SuppressWarnings("serial")
	class Polygon implements Serializable {

		List<Point3> vertices = new ArrayList<>();
		
	}
	
	
	/**
	 * Пока все просто, но впечатление обманчиво!
	 * 
	 * 1. Внутреннее представление становится частью интерфейса
	 * 		Инкапсуляция летит к чертям!
	 * 2. Увеличивает объемы тестирования новых версий
	 * 3. Дополнительный конструктор! 
	 * 		Необходимо обеспечивать все инварианты
	 * 		Некоторые дыры в безопасности
	 * 4. Все потомки становятся сериализуемыми!
	 */
	
	/****Управление сериализацией****/
	
	/**
	 * Serial version UID (stream unique identifier)
	 * 
	 * Применяется для обеспечения совместимости, когда версии класса изменяются
	 * 
	 * По-умолчанию автоматически создается на основе имени, интерфейсов, 
	 * public и protected полей
	 * 
	 * В случае несовпадения -- InvalidClassException
	 * 
	 */
	class Point2 implements Serializable {
		private static final long serialVersionUID = 4889340678034881968L;

		double x;
		double y;
	}


	/**
	 * Модификатор transient.
	 * Исключает поле из процесса сериализации.
	 * Примеры:
	 * 	1. Избыточная информация (предподсчитанные значения)
	 * 	2. Несериализуемые поля
	 * 
	 */
	class Point3 implements Serializable {
		private static final long serialVersionUID = 4889340678034881968L;

		double x;
		double y;
		transient int precomputedHash;
		
		public int hashCode() {
			return precomputedHash;
		}
		
		private int countHash() {
			//some very involved procedure
			return 0;
		}
		
		Point3(double x, double y) {
			this.x = x;
			this.y = y;
			precomputedHash = countHash();
		}
	}
	
	/**
	 * Настраиваемая сериализация 
	 * Методы 
	 * 	readObject(ObjectInputStream in) -- должен прочесть состояние из потока 
	 * 	writeObject(ObjectOutputStream out) -- должен записать состояние в поток
	 * 
	 * Процесс чтения
	 * 	1. Выделение памяти
	 * 	2. Вызов readObject
	 *  
	 */
	class Point4 implements Serializable {
		private static final long serialVersionUID = 4889340678034881968L;

		double x;
		double y;
		
		transient int precomputedHash;
		
		public int hashCode() {
			return precomputedHash;
		}
		
		private int countHash() {
			//some very involved procedure
			return 0;
		}
		
		Point4(double x, double y) {
			this.x = x;
			this.y = y;
			precomputedHash = countHash();
		}
		
		/*
		 * Отвечает только за запись полей ЭТОГО класса, не предка и не потомка!
		 */
		private void writeObject(ObjectOutputStream s) throws IOException {
			/*
			 * Реализует стандартный механизм сериализации.
			 * Может вызываться только из writeObject, иначе NotActiveException
			 */
			s.defaultWriteObject();
		}

		//отвечает только за чтение полей ЭТОГО класса, не предка и не потомка!
		private void readObject(ObjectInputStream s) throws IOException,
				ClassNotFoundException {
			/*
			 * Реализует стандартный механизм десериализации.
			 * Может вызываться только из readObject, иначе NotActiveException
			 */
			s.defaultReadObject();
			//восстановление инвариантов
			precomputedHash = countHash();
		}
	}


	
	
	
	//move to Point5.java
	
	
	
	
	
	
	/**
	 * Сериализация с несериализуемым предком.
	 * 
	 * Если хочется отнаследовать сериализуемый класс от 
	 * несериализуемого предка, то могут быть проблемы...
	 * 
	 * Формальное требование одно -- предок должен иметь 
	 * конструктор по-умолчанию.
	 * 
	 * Но только ли?!
	 */
	class NonSerializablePoint {
		private double x;
		private double y;
		
		protected double getX() {return x;}
		
		protected double getY() {return y;}
		
		protected void init(double x, double y) {
			this.x = x;
			this.y = y;
		}
		
		protected NonSerializablePoint() {}
		
		public NonSerializablePoint(double x, double y) {
			init(x, y);
		}
	}
	
	// Serializable subclass of nonserializable stateful class
	class ColoredPoint extends NonSerializablePoint implements Serializable {
		private int color;

		// Конструктор использует обычный механизм
		public ColoredPoint(double x, double y, int color) {
			super(x, y);
			this.color = color;
		}
		
		private void readObject(ObjectInputStream s) throws IOException,
				ClassNotFoundException {
			s.defaultReadObject();
			// Manually deserialize and initialize superclass state
			double x = s.readDouble();
			double y = s.readDouble();
			init(x, y);
		}
		
		private void writeObject(ObjectOutputStream s) throws IOException {
			s.defaultWriteObject();
			// Manually serialize superclass state
			s.writeDouble(getX());
			s.writeDouble(getY());
		}

		private static final long serialVersionUID = 1856835860954L;
	}

	
	
	/**
	 * Externalizable
	 * 
	 * Сериализация "в ручную"
	 *  
	 * 	void readExternal(ObjectInput in)
	 * 	void writeExternal(ObjectOutput out) 
	 * 
	 * Должен быть конструктор по-умолчанию
	 * 
	 * Чтение: вызов конструктора по-умолчанию, вызов readExternal
	 */
	class Point6 implements Externalizable {
		private double x;
		private double y;

		Point6(double x, double y) {
			this.x = x;
			this.y = y;
		}
		
		@Override
		public void writeExternal(ObjectOutput out) throws IOException {
			out.writeDouble(x);
			out.writeDouble(y);
		}
		@Override
		public void readExternal(ObjectInput in) throws IOException,
				ClassNotFoundException {
			this.x = in.readDouble();
			this.y = in.readDouble();
		}
	}
	
	/**
	 * "Подмена" сериализуемого объекта.
	 * 
	 * Когда вместо самого объекта сериализуется его альтернативное представление
	 * в виде другого объекта некоторого (возможно того же самого) сериализуемого класса. 
	 * 
	 * Object writeReplace() -- какой объект записать вместо данного
	 * Object readResolve() -- какой объект восстановить вместо прочитанного
	 * 
	 * Реализуются у разных классов!!!
	 */
	
}

import java.io.Serializable;

/**
 * В простейшем случае достаточно добавить маркерный интерфейс implements
 * Serializable
 */
@SuppressWarnings({ "serial"})
public class Point1 implements Serializable {
	double x;
	double y;

	Point1(double x, double y) {
		this.x = x;
		this.y = y;
	}

}

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import org.junit.Test;

@SuppressWarnings("javadoc")
public class SimplePointTest {
	void SimpleWrite(OutputStream os) throws /*NotSerializableException,*/ IOException {
		Point1 p = new Point1(10., 10.);
		ObjectOutput out = new ObjectOutputStream(os);
		out.writeObject(p);
		os.flush();
	}

	void SimpleRead(InputStream is) throws IOException, ClassNotFoundException {
		ObjectInput in = new ObjectInputStream(is);
		Point1 p = (Point1) in.readObject();
		System.out.println("x=" + p.x);
		System.out.println("y=" + p.y);
	}

	@Test
	public void SimpleTest() throws FileNotFoundException, IOException,
			ClassNotFoundException {
		try (OutputStream os = new FileOutputStream("point.ser")) {
			SimpleWrite(os);
		}

		try (InputStream is = new FileInputStream("point.ser")) {
			SimpleRead(is);
		}
	}
}

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.Serializable;

/**
 * Что если внутреннее представление класса изменилось?
 * 
 * Ручная сериализация полей
 * 
 * Константа ObjectStreamField[] serialPersistentFields -- сериализуемые поля
 * ObjectOutputStream.PutField putFields() -- поля для записи
 * 
 * writeFields записывает поля
 * 
 * ObjectInputStream.GetField readFields() -- чтение полей
 * 
 */
@SuppressWarnings("unused")
public class Point5 implements Serializable {
	private static final long serialVersionUID = 4889340678034881968L;

	double angle;
	double rad;

	private static final ObjectStreamField[] serialPersistentFields = {
			new ObjectStreamField("x", double.class),
			new ObjectStreamField("y", double.class) };

	private void writeObject(ObjectOutputStream s) throws IOException {
		ObjectOutputStream.PutField fields = s.putFields();
		fields.put("x", Math.cos(angle) * rad);
		fields.put("y", Math.sin(angle) * rad);
		s.writeFields();
	}

	private void readObject(ObjectInputStream s) throws IOException,
			ClassNotFoundException {
		ObjectInputStream.GetField fields = s.readFields();
		double x = (double) fields.get("x", 0.);
		double y = (double) fields.get("y", 0.);
		// count angle and mod here
		// angle = ...
		// rad = ...
	}
}