アプリ版:「スタンプのみでお礼する」機能のリリースについて

次のコードの実行結果は?

------------------------------------------------------------------------------
01 : import java.io.*;
02 :
03 : public class CodeWalkSeven {
04 :   public static void main(String[] args) {
05 :     Car c = new Car("Nissan", 1500, "blue");
06 :
07 :     System.out.println("before:" + c.make + " " + c.weight);
08 :
09 :     try {
10 :       FileOutputStream fs = new FileOutputStream("Car.ser");
11 :       ObjectOutputStream os = new ObjectOutputStream(fs);
12 :       os.writeObject(c);
13 :       os.close();
14 :     } catch (Exception e) {
15 :       e.printStackTrace();
16 :     }
17 :
18 :     try {
19 :       FileInputStream fis = new FileInputStream("Car.ser");
20 :       ObjectInputStream ois = new ObjectInputStream(fis);
21 :       c = (Car) ois.readObject();
22 :       ois.close();
23 :     } catch (Exception e) {
24 :       e.printStackTrace();
25 :     }
26 :
27 :     System.out.println("after:" + c.make + " " + c.weight);
28 :
29 :   }
30 : }
31 :
32 : class NonLiving {
33 :
34 : }
35 :
36 : class Vehicle extends NonLiving {
37 :     String make = "Lexus";
38 :     String color = "Brown";
39 : }
40 :
41 : class Car extends Vehicle implements Serializable {
42 :
43 :   protected int weight = 1000;
44 :
45 :   Car(String n, int w, String c) {
46 :     color = c;
47 :     make = n;
48 :     weight = w;
49 :   }
50 : }
------------------------------------------------------------------------------
実行結果

before:Nissan 1500
after:Lexus 1500


上記問題の解説でよくわからないとこがありますので、質問しました。

解説では、「書き込む前にCarオブジェクトに"Nissan"をセットしているが、Vehicleのコンストラクタにより初期化され、変数makeには初期値の"Lexus"がセットされる。」となっています。

私はコンストラクタはインスタンス化した際に実行される認識ですが、
この解説の記述ですと、21行目のCarクラスへのキャスト処理時にVehicleのコンストラクタが実行されているように受け取りました。

コンストラクタはキャスト時にも実行されるものなのでしょうか?
もしそうであるならば、Javaの仕様書等に記載がある場合は、そのソースも教えていただけないでしょうか?

解説を読んでも納得いかずもやもやしています。

どなたかご回答お願いします。

A 回答 (2件)

 中々面白い問題を解いていらっしゃいますね。

Javaで直列化を扱う場合、誰もが一度は通る道です。今回のソースですと、10-13行目で対象オブジェクトの直列化(Serialize)、19-22行目で直列化の復元(deserialize)を行っています。

 直列化を行う場合、以下の要件を満たすことが必要です(かなり簡略化して書いてます)。

1. 対象オブジェクトのクラスがjava.io.Serializableインターフェースを実装していること
  (java.io.Serializableインターフェースを実装することにより「直列化可能なクラス」になります)
2. 対象オブジェクトが保持するフィールドのクラスも、java.io.Serializableインターフェースを実装していること
3. 親クラスが直列化不可能なクラスであってもサブクラスが直列化可能なクラスであれば、直列化は可能。但し、親クラスのデフォルト・コンストラクタが外部から呼び出せること。

 今回の場合は、「3」が当てはまりますね。(Vehicleクラスは直列化不可能、その子供であるCarクラスは直列化可能なクラスになります。)で、この「3」の場合なのですが、復元時にちょっと困った(?)動きをします。それは、復元時に親クラスのデフォルト・コンストラクタを呼ぶという動作をするのです。よって、親クラスで保持しているフィールドは親クラスの定義にて初期化されます。ですので、フィールドcolorとmakeは親クラスの定義に紐付く値で、weightは直列化されたCarクラスの値そのままで復元されます。

 質問主様は、「キャスト時にコンストラクタが実行される」と思われているようですが、実際は上記のように直列化から復元する時に行っているのです。ちなみに、Vehicleクラスもjava.io.Serializableインターフェースを実装している場合は、上記処理は行われず直列化時そのままの状態で復元されます。

 この辺りの情報ですが、Java SE APIのjava.io.Serializableインターフェースの項をご参照ください。非常に判りづらい書き方をしていますが、上記のようなことが記載されています。

 以上、ご参考になりましたら。

参考URL:http://java.sun.com/javase/ja/6/docs/ja/api/java …
    • good
    • 0
この回答へのお礼

takepan_toki様

ご丁寧な解説ありがとうございます。

キャスト時にコンストラクタが実行されているのではなく、親クラスがSerializableインターフェースを実装していない場合、デシリアライズの際に親のデフォルトコンストラクタが動いているのですね。

とても参考になりました。

ありがとうございました。

お礼日時:2012/05/03 00:58

http://docs.oracle.com/javase/7/docs/api/java/io …

>To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

>During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!