【Java入門】第20回 リソースとファイル|ファイルの作成/書き込み/読み込み/削除
2024.08.09
Java入門の記事では、Javaの型や演算子、構文といった、Javaのプログラムを作成する上で必要な基本的な内容について説明してきました。当記事ではJava入門の最終回として、実際にコンピューター上のファイルを読み書きするプログラムと、ファイルを扱う際のポイントを紹介します。
目次
リソース
リソースとは
リソースは直訳すると「資源」や「物資」となりますが、Javaのプログラムにおいてリソースとは、簡単に言えば「プログラム外部のデータ(ファイルやデータベース)」自体、およびそれらのリソースへのアクセス(しているインスタンス)のことを指します。
Javaのプログラム上から外部のデータに対して読み込みや書き込みといった操作をする場合は、このリソースを利用して外部のデータを操作する必要があります。
(ファイルの作成、移動、削除などはリソースを利用しなくても可能です)
以下のサンプルでは、ファイル「C:¥java¥files¥sample.txt」の内容を読み込むために、FileReaderクラスのインスタンス「reader」を準備しています。この時、Javaのプログラム上ではこのインスタンス「reader」がリソースと呼ばれます。
// Fileクラス(ファイルの情報)
java.io.File fi = new File("C:¥¥java¥¥files¥¥sample.txt");
// FileReaderクラス(ファイルへのアクセスを行い、中身を読み込むためのインスタンス)
java.io.FileReader reader = new FileReader(fi);
今回は、このプログラム上のリソースの扱い方と、実際のファイル操作のプログラムについて簡単に説明したいと思います。
リソースとclose
Javaでは、リソースのためのクラスは「Closableインターフェイス」または「AutoClosableインターフェイス」を実装しています。これらのインターフェイスを実装したクラスでは「close」メソッドがリソース(との接続)を閉じるためのメソッドして準備されています。
リソースを経由して外部のデータへのアクセスを保持したままになっている場合、他のプロセス(他のソフトなど)から、同じ外部データへのアクセスができなくなってしまったり、Javaのプログラムがメモリを解放できずにメモリリークが発生し、メモリ不足になってしまうなどの問題が発生することがあります。
これを防ぐため、プログラム上では利用が終わったリソースを閉じて、利用していた外部データを開放する必要があり、このためにcloseメソッドを利用することになります。
下記のサンプルでは、ファイル「C:¥java¥files¥sample.txt」を読み込むために利用したリソース「reader」に対して、ファイルを最後まで読み込んだ時点でcloseを呼び出して、ファイルへのアクセス権を開放しています。
// Fileクラス(ファイルの情報)
java.io.File fi = new File("C:¥¥java¥¥files¥¥sample.txt");
// FileReaderクラス(ファイルへのアクセスを行い、中身を読み込むためのインスタンス)
java.io.FileReader reader = new FileReader(fi);
// 読み込んだデータの格納用変数
StringBuilder builder = new StringBuilder();
int read = -1;
// 末尾までファイルデータを読み込んで、StringBuilderに格納
while((read = reader.read()) != -1)
{
builder.append((char) read);
}
// リソースを開放
reader.close();
// 読み込んだファイルの中身を出力
System.out.println(builder.toString());
※確実にcloseするためには、本来はtry-catch-finally文やtry-with-resources文を利用しますが、ここでは簡略化していますのでご注意ください。(詳細は「try-with-resources文の利用」をご参照ください)
ファイル操作
ファイル操作のためのライブラリ
Javaでは、ファイル操作を行うためのライブラリ(プログラム群)がいくつか準備されています。
・java.io パッケージ
java.ioパッケージは、Javaにおける I/O処理のために準備された最初のパッケージです。
I/O処理とは「Input / Output」の略で、ファイルや通信先など、プログラムと外部とのデータの入出力を行う処理を指します。
・java.nio パッケージ
nioは「New I/O」の略で、I/O処理向けの新たなパッケージとして Java1.4 から導入されたパッケージです。java.ioパッケージを補完する内容で準備され、java7 ではNIO.2と呼ばれるアップデートがなされ、java.nio.file のライブラリによってファイル操作がよりシンプルに行えるようになりました。
※現時点(Java21)では、特にリソースを利用するファイル読み書きなどでは、java.nioパッケージを利用するほうが比較的簡単に記述できますので、下記のファイル操作のサンプルコードを確認してみてください。
ファイルパスの記述
プログラム上で、操作するファイルのパス(C:¥java¥files¥sample.txt、など、ファイルの位置を示す文字列)を記述する方法はいくつかあります。
プログラムを動かすOSとファイル名が決まっているのであれば、上記のサンプルのように直接文字列リテラルで書くことも可能ですが、実際には変数などから動的にファイルパスを作成することが多いと思います。
一番簡単なのはPathクラスのインスタンスを利用する方法で、Java11以降であればPath.of メソッドを、Java11より前のバージョンであれば Paths.get メソッドを(PathクラスではなくPathsクラスであることに注意)利用し、ファイルパスの各フォルダ(ディレクトリ)名とファイル名を順に引き渡すことで作成できます。
ファイルパスは、WindowsやLinuxなどのOSによって「¥」(「\」バックスラッシュ)や「/」(スラッシュ)で、各々のフォルダ・ディレクトリの区切りを示します。
ファイルパスの文字列を自分でパスをつないで構築する場合は、Javaでは File クラスのメンバ変数 separator を利用することで、VMを実行している環境に応じた区切り文字を取得することができます。
// OSが決まっている場合は、文字列でそのまま作成してもOK
// 但しwindowsの場合は「¥」はエスケープする(「¥¥」と記述する)必要があるので注意
String pathStr = "C:¥¥java¥¥files¥¥sample.txt";
// Path.of を利用してPathクラスのインスタンスを作成
Path p1 = Path.of("C:", "java", "files", "sample.txt");
String pathStr1 = p1.toString(); // ファイルパス文字列に変換
System.out.println("1:" + pathStr1);
// Paths.get を利用してPathクラスのインスタンスを作成
Path p2 = Paths.get("C:", "java", "files", "sample.txt");
String pathStr2 = p2.toString(); // ファイルパス文字列に変換
System.out.println("2:" + pathStr2);
// StringBuilderを利用して、自力でファイルパス文字列を作成
StringBuilder pathBldr = new StringBuilder();
pathBldr.append("C:")
.append(File.separator).append("java")
.append(File.separator).append("files")
.append(File.separator).append("sample.txt");
String pathStr3 = pathBldr.toString();
System.out.println("3:" + pathStr3);
実行結果:
どの方法でも同じファイルパスの文字列が取得できています。
1:C:¥java¥files¥sample.txt
2:C:¥java¥files¥sample.txt
3:C:¥java¥files¥sample.txt
ファイルの作成
それでは、java.ioパッケージと、java.nioパッケージを使って、新しいファイルの作成を行ってみます。
① java.io パッケージ(java.io.Fileクラス)を利用してファイルを作成
(Path クラスはjava.nioパッケージですが、ファイルパス作成の簡略化のために利用しています)
package lesson20;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
/** java.io.Fileクラスを利用してファイルを作成するクラス */
public class CreateMain {
/** mainメソッド */
public static void main(String[] args) {
// フォルダ(ディレクトリ)のパス
Path fileDir = Path.of("C:","java","files");
File dir = fileDir.toFile();
// フォルダがあるかチェックして、無ければ作成する
if( ! dir.exists()) {
boolean result = dir.mkdirs();
// フォルダ作成結果のチェック
if( ! result) {
System.out.println("フォルダ[" + fileDir.toString() + "]の作成に失敗したため処理を終了します。");
return;
}
}
// 作成するファイルのパス(C:¥java¥files¥sample2.txt)
Path filePath = Path.of(fileDir.toString(), "sample2.txt");
File file = filePath.toFile();
// 既に同じファイルがある場合は処理を終了
if(file.exists()) {
System.out.println("ファイル[" + filePath.toString() + "]が既に存在するため処理を終了します。");
return;
}
// ファイルを作成
try {
file.createNewFile();
} catch (IOException ex) {
// ファイル作成時に例外が発生
System.out.println("ファイルの作成に失敗しました:[" + filePath.toString() + "]");
ex.printStackTrace();
return;
}
System.out.println("ファイル[" + filePath.toString() + "]を作成しました。");
}
}
② java.nioパッケージ(java.nio.file.Filesクラス)を利用してファイルを作成
package lesson20;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/** java.nio.file.Filesクラスを利用してファイルを作成するクラス */
public class CreateMain2 {
/** mainメソッド */
public static void main(String[] args) {
// フォルダ(ディレクトリ)のパス
Path fileDir = Path.of("C:","java","files");
// フォルダがあるかチェックして、無ければ作成する
if( ! Files.exists(fileDir)) {
try {
Files.createDirectory(fileDir);
}catch(IOException ex) {
// フォルダ作成時に例外発生
System.out.println("フォルダ[" + fileDir.toString() + "]の作成に失敗したため処理を終了します。");
ex.printStackTrace();
return;
}
}
// 作成するファイルのパス(C:¥java¥files¥sample2.txt)
Path filePath = Path.of(fileDir.toString(), "sample2.txt");
// 既に同じファイルがある場合は処理を終了
if(Files.exists(filePath)) {
System.out.println("ファイル[" + filePath.toString() + "]が既に存在するため処理を終了します。");
return;
}
// ファイルを作成
try {
Files.createFile(filePath);
} catch (IOException ex) {
// ファイル作成時に例外が発生
System.out.println("ファイルの作成に失敗しました:[" + filePath.toString() + "]");
ex.printStackTrace();
return;
}
System.out.println("ファイル[" + filePath.toString() + "]を作成しました。");
}
}
①②いずれの場合でも、指定したファイルパス(C:¥java¥files¥sample2.txt)に空のテキストファイルが作成されます。
java.ioパッケージとjava.nioパッケージを利用した場合の差はソース上はそれほどありませんが、java.nioパッケージを利用する場合は、フォルダ(ディレクトリ)の作成に失敗した場合に発生した例外をcatchできるため、処理がうまくいかない時の原因の把握はしやすいと言えるでしょう。
ファイルへの書き込み
次に、作成したファイル(C:¥java¥files¥sample2.txt)に、文字列の書き込みを行ってみましょう。
※ここではファイルの存在チェックは省略しますので、ファイルが存在する状態で実行してください。
① java.io パッケージ(java.io.FileWriterクラス)を利用してファイルに書き込み
ファイルへの書き込みには、前述したリソースを利用する必要があります。書き込む方法はいくつかありますが、ここではFileWriterクラスのインスタンスを用いてファイルへの書き込みを行ってみます。
下記のサンプルでは、書き込みの後に利用したリソース(FileWriter)が必ずcloseされるように、finallyブロックの中でcloseの呼び出しを行っています。
package lesson20;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
/** java.io.FileWriterクラスを利用してファイルに書き込みするクラス */
public class WriteMain {
/** mainメソッド */
public static void main(String[] args) {
// 書き込み対象のファイル
Path targetPath = Path.of("C:", "java", "files", "sample2.txt");
File targetFile = targetPath.toFile();
// ファイル書き込み用リソースの変数
FileWriter writer = null;
try {
// リソースの読み込み
writer = new FileWriter(targetFile);
// ファイルへの書き込み文字列を準備
writer.write("これは動的に書き込んだ文字列です。");
writer.write("¥r¥n"); // 改行コード
writer.write("これは2行目の文字列です。");
writer.write("¥r¥n"); // 改行コード
// ここまで文字列をファイルに書き出す
writer.flush();
} catch(IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の書き込み中に例外が発生しました");
ex.printStackTrace();
return;
} finally {
// リソースが取得で来ている場合はcloseを実行
if(writer != null) {
try {
writer.close();
} catch (IOException ex) {
System.out.println("FileWriterのクローズに失敗しました");
ex.printStackTrace();
}
}
}
System.out.println("ファイル[" + targetPath.toString() + "]の書き込みを完了しました");
}
}
② java.nioパッケージ(java.nio.file.Filesクラス)を利用してファイルに書き込み
Filesクラスのwriteメソッドは、文字列のバイト配列を引き渡して実行する他に、行ごとのデータを持つListなどのコレクション(iterableの実装クラス)のインスタンスを引き渡して実行することができます。
package lesson20;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/** java.nio.file.Filesクラスを利用してファイルに書き込みするクラス */
public class WriteMain2 {
/** mainメソッド */
public static void main(String[] args) {
// 書き込み対象のファイル
Path targetPath = Path.of("C:", "java", "files", "sample2.txt");
// 書き込む文字列(行ごとのList)
List<String> rowList = List.of("これは動的に書き込んだ文字列です。", "これは2行目の文字列です。");
// ファイルへの書き込み
try {
Files.write(targetPath, rowList);
} catch (IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の書き込み中に例外が発生しました");
ex.printStackTrace();
return;
}
System.out.println("ファイル[" + targetPath.toString() + "]の書き込みを完了しました");
}
}
※java11以降であれば、String変数に書き込む文字列をまとめてから、writeStringメソッドを利用してファイルに1回で書き込むことも可能です。
①②いずれの場合も、指定したファイルに対して以下の内容が書き込みされます。
Filesクラスのwriteメソッドの引数にコレクションを利用した場合は、自動的に行の末尾に改行コードが追加されることに注意してください。
ファイルの書き込みに関しては、java.nioパッケージ(java.nio.file.Filesクラス)を利用したほうが、リソースの管理をライブラリ側の処理に任せることができるため、簡単に記述できると思います。
但し、内部的にはリソースが利用されていることには注意してください。
ファイルの読み込み
次に、ファイル(C:¥java¥files¥sample2.txt)に書き込んだ内容の読み込みを行ってみましょう。
※ここではファイルの存在チェックは省略しますので、ファイルが存在する状態で実行してください。
① java.io パッケージ(java.io.FileReaderクラス)を利用してファイルを読み込み
ファイルの読み込みでも、前述したリソースを利用する必要があります。読み込みについても、利用できる方法やクラスはいくつかありますが、ここではFileReaderクラスのインスタンスを用いて、ファイルの内容を1文字ずつ読み込んでみます。
下記のサンプルでは、ファイル書き込みの場合と同様に、ファイル読み込みに利用したリソース(FileReader)が必ずcloseされるように、finallyブロックの中でcloseしています。
package lesson20;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
/** java.io.FileReaderクラスを利用してファイルの中身を読み込むクラス */
public class ReadMain {
/** mainメソッド */
public static void main(String[] args) {
// 読み込み対象のファイル
Path targetPath = Path.of("C:", "java", "files", "sample2.txt");
File targetFile = targetPath.toFile();
// 読み込んだデータの格納用変数
StringBuilder builder = new StringBuilder();
// FileReaderクラス(ファイルへのアクセスを行い、中身を読み込むためのインスタンス)
FileReader reader = null;
try {
// リソースを取得
reader = new FileReader(targetFile);
int readbyte = -1;
// 末尾までファイルデータを読み込んで、StringBuilderに格納
while((readbyte = reader.read()) != -1)
{
// byteで引き渡されるので、charに変換してStringBuilderに格納する
builder.append((char)readbyte);
}
} catch(IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の読み込み中に例外が発生しました。");
ex.printStackTrace();
return;
} finally {
// リソースを開放
if(reader != null) {
try {
reader.close();
} catch(IOException ex) {
System.out.println("FileReaderのクローズに失敗しました。");
ex.printStackTrace();
}
}
}
// 読み込んだファイルの中身を出力
System.out.println("ファイル[" + targetPath.toString() + "]を読み込みました。");
System.out.println(builder.toString());
}
}
② java.nioパッケージ(java.nio.file.Filesクラス)を利用してファイルを読み込み
Filesクラスのreadメソッドは、文字列のバイト配列を引き渡して実行する他に、行ごとのデータを持つListなどのコレクション(iterableの実装クラス)のインスタンスを引き渡して実行することができます。
package lesson20;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/** java.nio.file.Filesクラスを利用してファイルの中身を読み込むクラス */
public class ReadMain2 {
/** mainメソッド */
public static void main(String[] args) {
// 読み込み対象のファイル
Path targetPath = Path.of("C:", "java", "files", "sample2.txt");
List<String> rowList = null;
try {
// ファイル内の全行を読み込んで、1行ごとのリストとして取得
rowList = Files.readAllLines(targetPath);
} catch (IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の読み込み中に例外が発生しました");
ex.printStackTrace();
return;
}
// 読み込んだファイルの中身を出力
System.out.println("ファイル[" + targetPath.toString() + "]を読み込みました。");
// 取得した全行のデータを出力
for(String rowdata : rowList) {
System.out.println(rowdata);
}
}
}
※java11以降であれば、readStringメソッドを利用して、String変数にファイル内の全データを読み込むことも可能です。
実行結果:
①②いずれの場合でも、ファイルの中身を読み込むことができます。
ファイル[C:¥java¥files¥sample2.txt]を読み込みました。
これは動的に書き込んだ文字列です。
これは2行目の文字列です。
ファイルの書き込み同様、リソースの管理をライブラリに任せられるため、java.nioパッケージ(java.nio.file.Filesクラス)を利用するほうが簡単に記述できるのがわかると思います。
ファイルの移動
次に、ファイルを違うフォルダ(C:¥java¥files¥dest)に移動させてみます。
※移動するファイルの存在チェックや移動先に同名のファイルが無いかのチェック、および移動先のフォルダの存在チェック&作成などはここでは省きますので、移動先フォルダは事前に作成しておいてください。
① java.io パッケージ(java.io.Fileクラス)を利用してファイルを移動
package lesson20;
import java.io.File;
import java.nio.file.Path;
/** java.io.Fileクラスを利用してファイルを移動するクラス */
public class MoveMain {
/** mainメソッド */
public static void main(String[] args) {
// 移動元のファイルパス
Path orgPath = Path.of("C:", "java", "files", "sample2.txt");
File orgFile = orgPath.toFile();
// 移動先のファイルパス
Path destPath = Path.of("C:", "java", "files", "dest", "sample2.txt");
File destFile = destPath.toFile();
// 出力用の文字列
String moveLogstr = "移動元[" + orgPath.toString() + "] → 移動先[" + destPath.toString() + "]";
// renameToメソッドでファイルを移動
boolean result = orgFile.renameTo(destFile);
// 移動結果のチェック
if(result) {
System.out.println("ファイルの移動に成功しました。" + moveLogstr);
} else {
System.out.println("ファイルの移動に失敗しました。" + moveLogstr);
}
}
}
② java.nioパッケージ(java.nio.file.Filesクラス)を利用してファイルを移動
package lesson20;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/** java.nio.file.Filesクラスを利用してファイルを移動するクラス */
public class MoveMain2 {
/** mainメソッド */
public static void main(String[] args) {
// 移動元のファイルパス
Path orgPath = Path.of("C:", "java", "files", "sample2.txt");
// 移動先のファイルパス
Path destPath = Path.of("C:", "java", "files", "dest", "sample2.txt");
// 出力用の文字列
String moveLogstr = "移動元[" + orgPath.toString() + "] → 移動先[" + destPath.toString() + "]";
// moveメソッドでファイルを移動
try {
Files.move(orgPath, destPath);
} catch(IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイルの移動に失敗しました。" + moveLogstr);
ex.printStackTrace();
return;
}
System.out.println("ファイルの移動に成功しました。" + moveLogstr);
}
}
①②いずれの場合も、ファイルsample2.txtがdestフォルダへ移動します。
java.io.Fileクラスを使う場合は、フォルダをまたいだ移動でも「rename」メソッドで移動します。逆に、java.nio.file.Filesクラスを使う場合は、同じフォルダ内でのリネームでも「move」を使う必要があります。
どちらのクラスでもほぼ同じ処理が可能ですが、java.nio.file.Files.moveの場合は処理の失敗時に例外をcatchできるので、ファイルの作成時同様、処理がうまくいかない時の原因の把握はしやすいでしょう。
ファイルの削除
最後に、ファイル(C:¥java¥files¥dest¥sample2.txt)を削除してみます。
① java.io パッケージ(java.io.Fileクラス)を利用してファイルを削除
※削除するファイルの存在チェックはここでは省きます。
package lesson20;
import java.io.File;
import java.nio.file.Path;
/** java.io.Fileクラスを利用してファイルを削除するクラス */
public class DeleteMain {
/** mainメソッド */
public static void main(String[] args) {
// 削除対象のファイルパス
Path targetPath = Path.of("C:", "java", "files", "dest", "sample2.txt");
File targetFile = targetPath.toFile();
// deleteメソッドでファイルを削除
boolean result = targetFile.delete();
// 削除の実行結果を確認
if(result) {
System.out.println("ファイル[" + targetPath.toString() + "]の削除に成功しました");
} else {
System.out.println("ファイル[" + targetPath.toString() + "]の削除に失敗しました");
}
}
}
② java.nioパッケージ(java.nio.file.Filesクラス)を利用してファイルを削除
Filesクラスには「delete」メソッドと、「deleteIfExists」メソッドが準備されています。
「delete」メソッドはFile.delete同様に指定したパスのファイルをそのまま削除するのに対して、「deleteIfExists」メソッドは、ファイルが存在しない場合は削除を実行せずfalseを返すため、存在チェックを同時に実行できるようになっています。
package lesson20;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/** java.nio.file.Filesクラスを利用してファイルを削除するクラス */
public class DeleteMain2 {
/** mainメソッド */
public static void main(String[] args) {
// 削除対象のファイルパス
Path targetPath = Path.of("C:", "java", "files", "dest", "sample2.txt");
// deleteIfExistsメソッドでファイルを削除
try {
boolean result = Files.deleteIfExists(targetPath);
// 実行結果を確認
if(result) {
System.out.println("ファイル[" + targetPath.toString() + "]の削除に成功しました");
} else {
System.out.println("ファイル[" + targetPath.toString() + "]は存在しないため削除できません");
}
} catch (IOException ex) {
// 例外発生(他のプロセスがファイルを利用中、など)
System.out.println("ファイル[" + targetPath.toString() + "]の削除に失敗しました");
ex.printStackTrace();
}
}
}
try-with-resources文の利用
try-with-resources文
try-with-resources文は、tryブロック内で利用するリソースを宣言することで、try-with-resources文で記述した処理の終了時に自動的にリソースのcloseを呼び出してくれる機能で、Java7以降のバージョンで利用が可能です。
(Java9以降では、tryブロックよりも前で取得したリソースを設定して、自動closeを実行することも可能となりました)
以下のサンプルは、java.ioパッケージを利用したファイル読み込みのソースから、リソースの取得~読み込み~closeまでの部分を抜粋したものです。
FileReader reader = null;
try {
// リソースを取得
reader = new FileReader(targetFile);
int readbyte = -1;
// 末尾までファイルデータを読み込んで、StringBuilderに格納
while((readbyte = reader.read()) != -1)
{
// byteで引き渡されるので、charに変換してStringBuilderに格納する
builder.append((char)readbyte);
}
} catch(IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の読み込み中に例外が発生しました。");
ex.printStackTrace();
return;
} finally {
// リソースを開放
if(reader != null) {
try {
reader.close();
} catch(IOException ex) {
System.out.println("FileReaderのクローズに失敗しました。");
ex.printStackTrace();
}
}
}
同様の処理をtry-with-resources文を使用して記述する場合は、以下のサンプルのようなソースコードになります。try句に続けて記述する括弧の中で、try-with-resources文のtryブロック内で利用するリソースを宣言および取得して利用し、明示的に記述をしなくても自動的にcloseすることができます。
// try句に続けて利用するリソースを宣言・取得
try(FileReader reader = new FileReader(targetFile)){
int readbyte = -1;
// 末尾までファイルデータを読み込んで、StringBuilderに格納
while((readbyte = reader.read()) != -1)
{
// byteで引き渡されるので、charに変換してStringBuilderに格納する
builder.append((char)readbyte);
}
} catch(IOException ex) {
// 例外発生時は処理終了
System.out.println("ファイル[" + targetPath.toString() + "]の読み込み中に例外が発生しました。");
ex.printStackTrace();
return;
}
try-with-resources文を利用した場合、リソースのcloseの実装漏れやタイミング等を意識する必要がなく、try-with-resources文を抜けるタイミングで自動的にcloseが呼び出されるため、基本的に(※)リソースの利用時はtry-with-resources文を利用する方が、シンプルで安全なコードを記述できます。
※ほとんどの場合はtry-with-resources文の利用で問題ないと思われますが、closeの失敗時の処理を明確に切り分けたい場合などは、try-with-resources文を用いない形での記述が必要な場合もありえます。