W przygotowywaniu się do certyfikatu SCJP jestem już na etapie serializacji, więc dzisiejszy kawałek kodu będzie o serializowaniu obiektów:
01. import java.io.*;
02. class Main {
03. public static void main(String[] args) {
04. Dog d1 = new Dog(10, "Irasiad");
05. Dog d2 = new Dog(20, "Ira");
06. try {
07. FileOutputStream fs = new FileOutputStream("irasiad.scjp");
08. ObjectOutputStream os = new ObjectOutputStream(fs);
09. os.writeObject(d1);
10. os.close();
11. } catch (Exception e) {
12. e.printStackTrace();
13. }
14. try {
15. FileInputStream fis = new FileInputStream("irasiad.scjp");
16. ObjectInputStream ois = new ObjectInputStream(fis);
17. d2 = (Dog) ois.readObject();
18. ois.close();
19. } catch (Exception e) {
20. }
21. System.out.println(d2.name + " " + d2.weight + " " + d2.mammal);
22. }
23. }
24. class Dog extends Animal implements Serializable {
25. String name;
26.
27. Dog(int w, String n) {
28. super(true);
29. weight = w;
30. name = n;
31. }
32. }
33. class Animal {
34. int weight = 42;
35. boolean mammal;
36.
37. Animal(boolean mammal) {
38. this.weight = 100;
39. this.mammal = mammal;
40. }
41. }
No i pytanie: co się wyświetli na ekranie?
A. Irasiad 10 true
B. Ira 20 true
C. Irasiad 42 true
D. Irasiad 42 false
E. Irasiad 100 true
Prawidłowa odpowiedź to B. A dlaczego nie Irasiad? Otóż Dog dziedziczy po Animal, a klasa Animal nie implementuje interfejsu Serializable. Co to oznacza? Oznacza to, że pola należące do klasy Dog są serializowane, a do Animal już nie. Przy deserializacji wywoływany jest bezargumentowy kontruktor klasy rodzica, więc gdyby klasa Animal miała bezargumentowy konstruktor, zostałby on wywołany i pola klasy Animal zostałyby zainicjowane przez konstruktor lub pozostawione z wartościami domyślnymi. Klasa Animal nie ma jednak bezargumentowego konstruktora (nie tworzy się on domyślnie, bo stworzyliśmy własny konstruktor), stąd przy deserializacji zostanie rzucony wyjątek. Gdyby linię 20. zastąpić takim fragmentem kodu:
20. e.printStackTrace();}
na ekranie wyświetliłoby się mniej więcej coś takiego:
java.io.InvalidClassException: Dog; Dog; no valid constructor
at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at Main.main(Main.java:18)
Caused by: java.io.InvalidClassException: Dog; no valid constructor
at java.io.ObjectStreamClass.<init>(Unknown Source)
at java.io.ObjectStreamClass.lookup(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at Main.main(Main.java:10)
Ira 20 true
Stąd zserializowany obiekt d1 nie zostaje zdeserializowany, więc referencja d2 ciągle wskazuje na psa o imieniu Ira :]
A co by było, gdybyśmy linię 36 zastąpili takim fragmentem kodu:
36. Animal(){}
Wtedy prawidłową odpowiedzią byłaby
D. Irasiad 42 false
Pola Irasiada z klasy Dog zostałyby zserializowane, a przy deserializacji pola z klasy Animal przyjęłyby wartość domyślną.



































































































































































































































































































































































































































































