Echando código: ¿Como hacer serialización en Java a la medida?



La verdad es que nunca me había tocado hacer serialización hasta que me topé con RMI; En ese caso todo se limitaba a implementar la interfaz ‘java.io.Serializable‘ y marcar algunos atributos como ‘trasient‘ los cuales no necesitaba el final y listo, mi clase estaba preparada para ser transportada por la Red (en este caso estaba trabajando en un proyecto de clasificados para automóviles y la forma más fácil y rapida de moverlos sin preocuparme por transacciones y serialización manual era esa).

Sin embargo, leyendo varias fuentes me encontré con varias razones para hacer mi propia serialización:

  1. Si el objeto tiene collecionesy son grandes en número, entonces quizas Java se pueda quedar sin memoria ya que tiene guardar todo el grafo de dependencias.
  2. Si la clase cambia de algún modo , entonces simplemente la des-serialización fallará ya que su serial interno será distinto (además de posibles cambios en la estructura)
  3. Quizas la representación de Java de el objeto serializado no es óptima en tamaño (Java seguro hace un trabajo razonable).

Asi que ¿como se hace?:

  1. Implemente la interfaz Serializable
  2. Marque aquellos campos que no son parte de la serialización como ‘trasient’
  3. Sobreescriba los métodos ‘writeObject’ y ‘readObject’
  4. !Listo!

Es sorprendente como el lenguaje pone a disposición una herramienta tan poderoza como la serialización; En otros lenguajes como C++, hay recurrer a liberías como Roguewave o Fast Objects de Poet para lograr el mismo objetivo (extra $$$$).

Pero bueno, esto está muy hablado; ¿Que tal si usted compila y corre el siguiente código de ejemplo que hice y se convence? (la clase se llama SimpleBean y es realmente simple, sólo con fines ilustrativos)

   1:import java.io.File;

2:import java.io.Serializable;
3:import java.io.ObjectOutputStream;
4:import java.io.ObjectInputStream;
5:import java.io.FileInputStream;
6:import java.io.FileOutputStream;
7:import java.io.IOException;
8:
9:/**
10: * This class shows how to use a custom serialization mechanism.
11: * @author Jose V Nunez Zuleta (josevnz@yahoo.com)
12: * @version 0.1
13: * @see http://java.sun.com/developer/technicalArticles/Programming/serialization/
14: * @see http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/serial-arch.html#wp5251
15: * @see http://www.javapractices.com/Topic45.cjp
16: */
17:public class SimpleBean implements Serializable {
18:
19: /**
20: * Constant, max age.
21: */
22: public static final int MAX_AGE=100;
23:
24: /**
25: * @serial Name of the user
26: */
27:
28: private String name;
29: /**
30: * @serial Last name of the user
31: */
32: private String lastname;
33:
34: /**
35: * @serial Age of the user
36: */
37: private int age;
38:
39: private transient final int maxAge = MAX_AGE;
40:
41: /**
42: * @serial brothers of the user
43: */
44: private String [] brothers;
45:
46: /**
47: * @serial brothers of the user
48: */
49: private long serialVersionUID = 20201973l;
50:
51: /**
52: * @serial The object resources were released early
53: */
54: private boolean isDestroyed;
55:
56: /**
57: * Parametric constructor.
58: * @param name User name
59: * @param lastname User last name
60: * @param age User age
61: * @param brothers Array of brothers (if any)
62: * @since 0.1
63: */
64: public SimpleBean(String name, String lastname, int age, String [] brothers) {
65: if (name == null) {
66: throw new NullPointerException();
67: }
68: this.name = name;
69: if (lastname == null) {
70: throw new NullPointerException();
71: }
72: this.lastname = lastname;
73: if ( (age < 0) || (age > MAX_AGE) ) {
74: throw new IllegalArgumentException();
75: }
76: this.age = age;
77: if (brothers == null) {
78: throw new NullPointerException();
79: }
80: this.brothers = new String[brothers.length];
81: System.arraycopy(brothers, 0, this.brothers, 0, this.brothers.length);
82: }
83:
84: /**
85: * Return a atring representation of the object
86: * @return String The format is maxAge, name, lastname, age, [brother1, brother2, ...]
87: * @since 0.1
88: */
89: public String toString() {
90: if (isDestroyed) {
91: throw new IllegalStateException("Cannot call this method after calling destroy!");
92: }
93: StringBuffer buffer = new StringBuffer();
94: buffer.append(maxAge);
95: buffer.append(", ");
96: buffer.append(name);
97: buffer.append(", ");
98: buffer.append(lastname);
99: buffer.append(", ");
100: buffer.append(String.valueOf(age));
101: buffer.append(", [");
102: for (int i=0; i < brothers.length; i++) {
103: buffer.append(brothers[i]);
104: if (i < brothers.length - 1) {
105: buffer.append(", ");
106: }
107: }
108: buffer.append("]");
109: return buffer.toString();
110: }
111:
112: /**
113: * Performs a deep comparison between objects of the same type
114: * @return boolean If is equal or not
115: * @since 0.1
116: */
117: public boolean equals(Object object) {
118: if (isDestroyed) {
119: throw new IllegalStateException("Cannot call this method after calling destroy!");
120: }
121: if (object instanceof SimpleBean) {
122: SimpleBean bean = (SimpleBean) object;
123: if (! bean.name.equals(name)) {
124: return false;
125: }
126: if (! bean.lastname.equals(lastname)) {
127: return false;
128: }
129: if (! (bean.age == age)) {
130: return false;
131: }
132: if (brothers.length != bean.brothers.length) {
133: return false;
134: }
135: for (int i=0; i < brothers.length; i++) {
136: if (! brothers[i].equals(bean.brothers[i])) {
137: return false;
138: }
139: }
140: return true;
141: } else {
142: return false;
143: }
144: }
145:
146: /**
147: * Custom serialization method for this class.
148: * @param output The object stream were the instance will be written to
149: * @throws IOException if there is an error writing the stream
150: * @serialData
151: * @since 0.1
152: */
153: private void writeObject(ObjectOutputStream output) throws IOException {
154: if (isDestroyed) {
155: throw new IllegalStateException("Cannot call this method after calling destroy!");
156: }
157: output.defaultWriteObject();
158: }
159:
160:
161: /**
162: * Custom de-serialization method for this class.
163: * @param inout The object stream from were the instance will be read
164: * @throws IOException if there is an error writing the stream
165: * @throws ClassNotFoundException
166: * @serialData
167: * @since 0.1
168: */
169: private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
170: if (isDestroyed) {
171: throw new IllegalStateException("Cannot call this method after calling destroy!");
172: }
173: input.defaultReadObject();
174: }
175:
176: /**
177: * Test method for the class
178: * @param args Command line arguments. arg[0] is the name of the serialized class and arg[1]
179: * is the type of operation(read or anything else for writting)
180: * @throws Exception On any fatal error
181: * @since 0.1
182: */
183: public static void main(String [] args) throws Exception {
184: if (! ((args != null) && (args.length == 2)) ) {
185: throw new NullPointerException("Please provide the name of the serialized object and if the option is read or write");
186: }
187: File file = new File(args[0]);
188: ObjectInputStream input = null;
189: ObjectOutputStream output = null;
190: String [] bro = { "pepe", "paco" };
191: SimpleBean instance = new SimpleBean("jose", "nunez", 31, bro);
192: System.out.println("Before serialization :" + instance);
193: try {
194: if (args[1].equals("read")) {
195: if (file.canRead()) {
196: input = new ObjectInputStream(new FileInputStream(file));
197: input.readObject();
198: System.out.println("After serialization :" + instance);
199: } else {
200: throw new IllegalArgumentException("Unable to open '" + file + "'");
201: }
202: } else {
203: output = new ObjectOutputStream(new FileOutputStream(file));
204: output.writeObject(instance);
205: }
206: } catch (Exception exp) {
207: throw exp;
208: } finally {
209: if (input != null) {
210: input.close();
211: }
212: if (output != null) {
213: output.close();
214: }
215: }
216: }
217:
218: /**
219: * Early resource liberation
220: */
221: public void destroy () {
222: if (! isDestroyed) {
223: for (int i=0; i < brothers.length; i++) {
224: brothers[i] = null;
225: }
226: isDestroyed = true;
227: }
228: }
229:
230: /**
231: * Last chance for resource liberation
232: */
233: protected void finalize() throws Throwable {
234: destroy();
235: super.finalize();
236: }
237:
238:}

2 thoughts on “Echando código: ¿Como hacer serialización en Java a la medida?

Comments are closed.