[Java]基本的なファイル操作のメモ

わかりやすいJava_オブジェクト指向編を読んで勉強中。

File クラス

ファイルの出力を行うクラス

  • FileOutputStream -> OutputStream
    FileString をコンストラクタに受け取り出力ストリームを開く。 抽象クラスの OutputStream を継承したバイト型バイナリデータを出力するためのクラス。 コンストラクタの第2引数(append)が真のときは追記モードで開く。
  • BufferedOutputStream -> FilterOutputStream -> OutputStream
    BufferedOutputStream は効率良くファイル出力を行うために出力内容のバッファリングを行うのWriterのラッパーであり、コンストラクタにWriterを受け取る。第二引数にバッファサイズを指定できる。
  • FileWriter -> OutputStreamWriter -> Writer
    Writer は文字ストリームに書き込むための抽象クラス。 FileWriterJVM のデフォルト文字エンコーディングが使用されるので、これが困る場合は OutputStreamReader で明示的に文字エンコーディングを指定する必要がある。
  • BufferedWriter -> Writer
  • PrintWriter -> Writer

他の関連クラスはOutputStream (Java Platform SE 6)とかWriter (Java Platform SE 6)を見る。

PrintWriter

PrintWriterWriterOutputStreamをラップする高機能なクラスで、たとえばコンストラクタに文字列を受け取る場合は以下の様に生成される。

  public PrintWriter(String fileName) throws FileNotFoundException {
    this
    (new BufferedWriter
        (new OutputStreamWriter
            (new FileOutputStream(fileName))), false);
  }

close, flush, format, printf, print, println, write などのメソッドを持つ。

閉じ忘れ注意

出力・入力クラスは finally 句 で必ず close すること!

ファイルの入力を行うクラス

  • FileReader -> InputStreamReader -> Reader
  • BufferedReader -> Reader
    FileReaderJVMのデフォルト文字エンコーディングが使用されるので、これが困る場合は InputStreamReader で明示的に文字エンコーディングを指定する必要がある。参考リンク : もしも文字化けで困ったら
  • FileInputStream -> InputStream
  • BufferedInputStream -> FilterInputStream -> InputStream

直列化(シリアライズ) と 直列化復元(デシリアライズ)

Serializable マーカーインターフェースを実装したクラスはシリアライズしてファイルに保存することが可能となる。※クラスフィールドは保存できない。

class Book implements Serializable {

  private static final long serialVersionUID = 1L;

  private String title;

  private Date saleDate;

  Book(String title, String saleDate) {
    System.out.println("Book#constructor ");
    this.title = title;
    try {
      this.saleDate = DateFormat.getDateInstance().parse(saleDate);
    } catch (Exception e) {
      this.saleDate = null;
    }
  }

  @Override
  public String toString() {
    return "タイトル:" + title + " - 発売日:" + saleDate;
  }
}

public class Sample {

  public static void main(String[] args) throws Exception {

    Book book1 = new Book("分かりにくいJava", "2012/1/1");
    Book book2 = new Book("インパーフェクトJava", "2022/2/15");
    String info = "何らかの情報";

    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(new FileOutputStream("book.ser"));
      out.writeObject(book1);
      out.writeObject(book2);
      out.writeObject(info);
    } finally {
      if (out != null) {
        out.close();
      }
    }

    ObjectInputStream in = null;
    try {
      in = new ObjectInputStream(new FileInputStream("book.ser"));
      // とりだす順番は書き込んだ順番でなければならない。
       System.out.println(in.readObject());
       System.out.println(in.readObject());
       System.out.println(in.readObject());
       System.out.println(in.readObject()); // 例外が飛ぶ
    } catch(EOFException e) {
      e.printStackTrace();
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

結果

Book#constructor
Book#constructor
タイトル:分かりにくいJava - 発売日:Sun Jan 01 00:00:00 JST 2012
タイトル:インパーフェクトJava - 発売日:Tue Feb 15 00:00:00 JST 2022
何らかの情報
java.io.EOFException
  at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2571)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1315)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
  at jp.fernweh.main.Sample.main(Sample.java:65)

直列化復元(デシリアライズ)時にはコンストラクタは実行されない。

オブジェクトグラフの自動保存

直列化(シリアライズ)しようとしたオブジェクトが別のオブジェクトを持っている場合、その持っているオブジェクトがSerializableインターフェースを実装していれば自動的に保存される。

transient 修飾子

transient 修飾子を付与したフィールドは直列化(シリアライズ)から除外される。コンストラクタや初期化子、初期値による初期化は行われないので注意。デフォルトの初期値が使用される。

class Product implements Serializable {

  private static final long serialVersionUID = 1L;

  public transient String id;
  public transient String name;
  public transient String data = "データ";

  public Product() {
    id = "アイデンティファイア";
  }

  {
    name = "名前";
  }

  @Override
  public String toString() {
    return id + "\n" + name + "\n" + data;
  }
}

public class SerializableSample {

  public static void main(String[] args) throws Exception {
    Product product = new Product();
    product.id = "ダミー";
    product.name = "ダミー";
    product.data = "ダミー";

    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(new FileOutputStream("Product.cer"));
      out.writeObject(product);
    } finally {
      if (out != null) {
        out.close();
      }
    }

    ObjectInputStream in = null;
    try {
      in = new ObjectInputStream(new FileInputStream("Product.cer"));
      System.out.println(in.readObject());
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

結果

null
null
null

継承とシリアライズ(直列化)

親クラスが Serializable を実装していれば、そのサブクラスは直列化可能となる。

親クラスが Serializable を実装しておらずそのサブクラスが実装している場合は、そのサブクラスで定義されたフィールドの直列化はできるが直列化したデータに親クラスのデータは含まれず、復元時に親クラスのデフォルトコンストラクタが実行される。デフォルトコンストラクタが存在しない場合はコンパイルエラーとなる。

class Base {
  public String data1;
  public String data2;

  public Base() {
    System.out.println("Base#constructor");
    data1 = "あいうえお";
  }

  @Override
  public String toString() {
    return "data1: "+ data1 + " - data2: "+ data2;
  }
}

class Sub extends Base implements Serializable {
  private static final long serialVersionUID = 1L;
}

public class SerializableSample {

  public static void main(String[] args) throws Exception {
    Sub sub = new Sub();
    sub.data1 = "かきくけこ";
    sub.data2 = "さしすせそ";

    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(new FileOutputStream("Sub.cer"));
      out.writeObject(sub);
    } finally {
      if (out != null) {
        out.close();
      }
    }

    System.out.println();

    ObjectInputStream in = null;
    try {
      in = new ObjectInputStream(new FileInputStream("Sub.cer"));
      System.out.println(in.readObject());
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

結果

Base#constructor

Base#constructor
data1: あいうえお - data2: null
Share
関連記事