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:
- 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.
- 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)
- 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?:
- Implemente la interfaz Serializable
- Marque aquellos campos que no son parte de la serialización como ‘trasient’
- Sobreescriba los métodos ‘writeObject’ y ‘readObject’
- !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:}
Pana, excelente. El punto de las colecciones asociadas es muy bueno.
Pana, gracias por el proceso de evangelización de Java que estas haciendo.