この章では、次の部分を順番に紹介します。
目次
ObjectInputStream オブジェクト入力ストリーム
ObjectOutputStream オブジェクト出力ストリーム
入力ストリームと出力ストリームの概念
Java では、入力と出力の両方が情報の流れとして抽象化されます。
入力ストリーム: プログラム空間の外の他の場所からプログラム空間に情報が流入します。
出力ストリーム: プログラム空間内のデータをプログラム空間外の他の場所に送信します。
事前定義された I/O ストリーム クラス
流れ方向から分ける
入力ストリーム
出力ストリーム
ストリームの分割から
ノードストリーム: 実際にファイルにアクセスし、入出力操作を実行するストリーム
処理フロー:ノードフローをベースに、情報を加工・変換する流れ
ストリームの内容から切り離す
java.io パッケージの最上位階層
文字指向ストリーム: 特に文字データ用
- 通常、ソースまたはターゲットはテキスト ファイルです。
- テキストファイルの内部形式と外部形式間の変換を実装する
- 内部形式: 16 ビット char データ型
- 外部フォーマット:
- UTF (Universal Character Set Transformation Format): 多くの人がこれを「Universal Text Format」と呼んでいます。
- ASCII 文字と非 ASCII 文字 (キリル文字、ギリシャ文字、アジア文字など) が含まれます。
文字指向の抽象ストリーム クラス - リーダーおよびライター
- java.io パッケージ内のすべての文字ストリームの抽象スーパークラス
- Readerは文字入力用のAPIを提供します
- Writerは文字を出力するためのAPIを提供します
- それらのサブクラスは 2 つのカテゴリに分類できます
- ノード フロー: データ ソースからデータを読み取るか、宛先にデータを書き込みます。
- 処理の流れ:データに対して何らかの処理を実行する
- ほとんどのプログラムは、これら 2 つの抽象クラスの一連のサブクラスを使用して、テキスト情報の読み取り/書き込みを行います。
- たとえば、FileReader / FileWriter はテキスト ファイルの読み取り/書き込みに使用されます。
上の図は、いくつかの文字指向のストリームをリストしています。影付きの部分はノード ストリーム、その他は処理ストリームです。
バイト指向ストリーム: 汎用入力および出力用
一部の画像、音声、動画など、ほとんどのデータがテキストデータではない非テキストデータの入出力を処理するために使用されます。
一部のデータがテキストとバイナリの両方で保存できる場合でも、これらのデータをバイナリとして保存すると、多くのスペースが節約され、データをメモリからファイルなどのメモリ以外の場所に送信します。バイナリ形式で保存されるため、時間を節約できます。したがって、保存したいデータが人間による読み取りではなく、他のプログラムによって処理されることを目的としている場合は、データをバイナリで保存して、保存プロセスまたは出力プロセス中に時間を節約できるようにすることがあります。ストレージメディアのスペースを節約します
バイト指向の抽象ストリーム クラス - InputStream および OutputStream
- これはバイト ストリームを処理するための抽象基本クラスであり、プログラムはこれら 2 つのクラスのサブクラスを使用してバイト情報を読み書きします。
- 2つの部分に分かれています
- ノードフロー
- 処理の流れ
標準入出力ストリーム オブジェクト
- System クラスの静的メンバー変数
- 含む
- System.in: 標準入力ストリームを表す InputStream タイプ。デフォルト状態はキーボード入力に対応します。
- System.out: 標準出力ストリームを表す PrintStream タイプ。デフォルト状態は表示出力に対応します。
- System.err: PrintStream 型。標準エラー情報出力ストリームを表し、デフォルト状態は表示出力に対応します。
テキスト ファイルの書き込みと読み取り
テキストファイルの書き込み
テキスト ファイルを作成し、テキスト ファイルに情報を書き込むにはどうすればよいですか?
以下のコードを見てください
package Week13.com.videolearn;
import java.io.FileWriter;
import java.io.IOException;
//创建文件并写入若干行文本
public class FileWriterTester {
public static void main(String[] args) throws IOException {
//main方法中声明抛出IO异常
String fileName = "Hello.txt";
FileWriter writer = new FileWriter(fileName);
writer.write("Hello!\n");
writer.write("This is my first text file,\n");
writer.write("You can see how this is done.\n");
writer.write("输入一行中文也可以\n");
writer.close();
}
}
//每次运行都会删除旧文件生成新文件
//换行在不同平台可能会有不同效果
/*
注意在该代码中:
1.用“\n”作为回车,会产生小黑格
2.没有对异常进行处理,仅仅抛出
3.没有实现追加方式写文件
* */
操作の結果は次のようになります。
コードの修正を続けます
package Week13.com.videolearn;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTester02 {
public static void main(String[] args) {
String fileName = "Hello.txt";
try {//将所有IO操作放入try块中
//true参数表示是追加
FileWriter writer = new FileWriter(fileName, true);
writer.write("Hello!\n");
writer.write("This is my first text file,\n");
writer.write("You can see how this is done.\n");
writer.write("输入一行中文也可以\n");
writer.close();
} catch (IOException e) {
System.out.println("Problem writing" + fileName);
}
}
}
/*
* 说明:
运行此程序,会发现在原文件内容后面又追加了重复的内容,
* 这就是将构造方法的第二个参数设为true的效果。
* 如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,
* 程序将转入catch块中,给出出错信息。
*
*
*
* 如果我们要写入文件中的信息比较多,那么写的效率就是个问题
* 这时候我们可以用BufferedWriter类进行缓冲,能够提高写的效率
*
*
* */
操作の結果は次のようになります。
BufferedWriterクラス
- FileWriter クラスと BufferedWriter クラスはどちらも文字ストリームの出力に使用され、含まれるメソッドはほぼ同じですが、BufferedWriter には改行用の newLine() メソッドが追加されており、改行効果はクロスプラットフォームです。
- システムが異なれば、テキストの折り返し方法も異なります。newLine0 メソッドは、現在のコンピューター上で正しい改行文字を出力できます。
次のコードは、BufferedWriter を使用したファイルの書き込みを示しています。
package Week13.com.videolearn;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterTester {
public static void main(String[] args) throws IOException {
String fileName = "newHello.txt";
BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
out.write("Hello!");
out.newLine();//换行,可以跨平台
out.write("This is another text file using BufferedWriter,");
out.newLine();
out.write("So I can use a common way to start a newline");
out.close();
}
}
操作の結果は次のようになります。
テキストファイルの読み込み
テキストファイルの読み取りに関するクラス
- FileReader クラス
- テキストファイルから文字を読み取る
- Reader抽象クラスのサブクラスInputStreamReaderから継承
- BufferedReader クラス
- テキストファイルを読み取るためのバッファクラス
- これには readLine0 メソッドがあり、改行文字を識別し、入力ストリームの内容を 1 行ずつ読み取ることができます。
- リーダーから継承。
package Week13.com.videolearn;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
//读文本文件并显示
public class BufferedReaderTester {
public static void main(String[] args) {
String fileName = "Hello.txt", line;
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));
line = bufferedReader.readLine(); //读取一行的内容
while(line != null) {
System.out.println(line);
line = bufferedReader.readLine();
}
bufferedReader.close();
} catch (IOException e) {
System.out.println("Problem reading" + fileName);
}
}
}
/*
* 运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容。
* FileReader对象:创建后将打开文件,如果文件不存在,会抛出一个IOException
* BufferedReader类的readLine()方法:从一个面向字符的输入流中读取一行文本。如果其中
* 不再有数据,返回null
Reader类的read()方法:也可用来判别文件结束。该方法返回的一个表示某个字符的int型整数,
* 如果读到文件末尾,返回 -1。据此,可修改本例中的读文件部分:
int c
* while((c = in.read())!= -1)
* System.out.print((char)c);
* close()方法:为了操作系统可以更为有效地利用有限的资源,应该在读取完毕后,调用该方收
*
*
* 思考:如何实现文本文件的复制呢?
* */
操作の結果は次のようになります。
バイナリファイルの書き込みと読み取り
バイナリファイルの書き込み
抽象クラス OutputStream
- 派生クラス FileOutputStream
- 汎用出力 (非文字出力) の場合。
- グループ化されたバイト出力用。
- 派生クラス DataOutputStream
- さまざまな基本的なデータ型を記述するためのメソッドがあります。
- データを別の出力ストリームに書き込みます。
- すべてのコンピュータ プラットフォームで同じデータ形式を使用します。
- このうち、size メソッドは書き込まれたバイト数をカウントするカウンターとして使用できます。
たとえば、int データをバイナリ ファイルに書き込む場合、コードは次のようになります。
package Week13.com.videolearn;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//将三个int型数字255/0/-1写入数据文件data1.dat
public class FileOutputStreamTester {
public static void main(String[] args) {
String filename = "data1.dat";
int value0 = 255, value1 = 0, value2 = -1;
try {
//FileOutputStream是源生字节流,只识别写出去的是一个一个字节
//没办法识别数据类型,DataOutputStream是处理流,可以将字节首先处理成某种
//类型的数据
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(filename));
dataOutputStream.writeInt(value0);
dataOutputStream.writeInt(value1);
dataOutputStream.writeInt(value2);
dataOutputStream.close();
} catch (IOException e) {
System.out.println("Problem writing" + filename);
}
}
}
/*
* 运行结果
运行程序后,生成数据文件datal.dat
用写字板打开没有任何显示
用ultraEdit打开查看其二进制信息,内容为00.00 00 FF 00 0000 00 FF FF FF FF,
* 每个int数字都是32个bit的
说明
FileOutputStream类的构造方法负责打开文件“data1.dat”用于写数据
* FileOutputStream类的对象与DataOutputStream对象连接,写基本类型的数据
*
* 当需要写大量数据时,用缓冲流类可以提高写的效率
*
*
* */
バッファストリームクラスを見てみましょう
BufferedOutputStream クラス
- 使用例:
- DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
- BufferedOutputStream も処理ストリームであり、バッファリングされますが、直接書き込まれません。
ファイルにさまざまなデータを書き込み、バイト数をカウントするコードは次のとおりです。
package Week13.com.videolearn;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//向文件写入多种数据,统计字节数
public class BufferedOutputStreamTester {
public static void main(String[] args) throws IOException {
String fileName = "mixedTypes.dat";
//首先构造输出流对象
//在源生字节输出流对象基础上构造一个缓冲的BufferedOutputStream,提高写的效率
//DataOutputStream可以按照类型去写二进制数据
DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
dataOutputStream.writeInt(0);
System.out.println(dataOutputStream.size() + "bytes have been written");
dataOutputStream.writeDouble(31.2);
System.out.println(dataOutputStream.size() + "bytes have been written");
dataOutputStream.writeBytes("JAVA");
System.out.println(dataOutputStream.size() + "bytes have been written");
dataOutputStream.close();
}
}
操作の結果は次のようになります。
バイナリファイルの読み込み
以前にバイナリでファイルに書き込まれたデータ
バイナリ ファイル内の 3 つの int 数値を読み取って追加します。コードは次のとおりです。
package Week13.com.videolearn;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
//读取二进制文件中的三个int型数字并相加
public class DataInputStreamTester {
public static void main(String[] args) {
String fileName = "data1.dat";
int sum = 0;
try {
//首先构造输入流对象FileInputStream(源生字节的输入流)
//BufferedInputStream缓冲流
//DataInputStream可以按类型读取
DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
//问题1:当初这个文件写的是什么类型的数据要知道,不然就没办法了
//问题2:如果不知道这个文件中总共有多少个整数,怎么判断?
//可以依赖异常,捕获DataInputStream的文件结束异常
sum += dataInputStream.readInt();
sum += dataInputStream.readInt();
sum += dataInputStream.readInt();
System.out.println("The sum is:" + sum);
dataInputStream.close();
} catch (IOException e) {
System.out.println("Problem reading" + fileName);
}
}
}
操作の結果は次のようになります。
ファイルがバイナリファイルかテキストファイルかに関係なく、バイトの読み書きによるファイルのコピーが実装可能
何らかの種類のファイルをコピーする必要がある場合はどうすればよいでしょうか?
コードは以下のように表示されます。
package Week13.com.videolearn;
import java.io.*;
//从命令行输入源文件名和目标文件名,将源文件复制为目标文件
public class CopyBytes {
public static void main(String[] args) {
DataInputStream instr;
DataOutputStream outstr;
if(args.length != 2) {
System.out.println("Please enter file names");
return;
}
try {
instr = new DataInputStream(new BufferedInputStream(new FileInputStream(args[0])));
outstr = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(args[1])));
try {
int data;
while(true) {
data = instr.readUnsignedByte();
outstr.writeByte(data);
}
} catch (EOFException e) {
//遇到文件尾,会抛出文件结束异常,捕获
outstr.close();
instr.close();
return;
}
} catch (FileNotFoundException nfx) {
System.out.println("Problem opening files");
} catch (IOException iox) {
System.out.println("IO Problems");
}
}
}
File クラスの概要
ファイルを読み書きするプログラム内の文字列ファイル名を File オブジェクトに置き換えることができます
注: Java の File クラスは、ファイルの操作とファイル情報の取得にのみ使用されます。ファイル データの読み取りはファイル ストリームによって提供されます。
Fileクラスの役割
- ファイルの作成と削除。
- ファイルの名前を変更します。
- ファイルの読み取りおよび書き込み権限と、ファイルが存在するかどうかを確認します。
- ファイルの最終変更時刻などを設定および照会します。
- ファイル ストリームの構築では、File クラスのオブジェクトをパラメータとして使用できます。
次に見てみましょう: File クラスを結合して以前のバイナリ ファイル コピー プログラムを改善する
File クラスの一般的に使用されるいくつかのメソッド (P194 ページの例 7-6 を参照):
- File オブジェクトがファイルを表すかどうかを判断する isFile0 メソッド
- isDirectory0 メソッドは、File オブジェクトがディレクトリを表すかどうかを判断します。
- exists0 メソッドは、同じ名前のファイルまたはパスが存在するかどうかを判断します。
改良されたファイル コピー プログラム コードは次のとおりです。
package Week13.com.videolearn;
import java.io.*;
//改进的文件复制程序
public class NewCopyBytes {
public static void main(String[] args) {
DataInputStream instr;
DataOutputStream outstr;
if(args.length != 2) {
System.out.println("Please Enter file names!");
return;
}
File inFile = new File(args[0]);
File outFile = new File(args[1]);
//exists方法判断要打开的文件是否存在
if(outFile.exists()) {
System.out.println(args[1] + " already exists");
return;
}
if(! inFile.exists()) {
System.out.println(args[0] + "does not exist");
return;
}
try {
instr = new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
outstr = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
try {
int data;
while(true) {
data = instr.readUnsignedByte();
outstr.writeByte(data);
}
} catch (EOFException e) {
outstr.close();
instr.close();
return;
}
} catch (FileNotFoundException nfx) {
System.out.println("Problem opening files");
} catch (IOException iox) {
System.out.println("IO Problems");
}
}
}
オブジェクトのシリアル化
プログラムメモリ内のオブジェクトはどれくらい存続できるでしょうか?
最も長い寿命は、プログラムが終了し、プログラムが占有していた領域が解放され、メモリ内のオブジェクトが存在しなくなったときです。一部のオブジェクトの情報を永続的に保持する必要がある場合、今度はオブジェクトをシリアル化します。たとえば、オブジェクトを全体としてファイルに書き込み、それを全体として読み取ります。
ObjectInputStream/ObjectOutputStream
オブジェクトの読み書きを実装する
ObjectInputStream オブジェクト入力ストリーム
ObjectOutputStream を通じてオブジェクトをディスク ファイルに書き込む
ObjectOutputStream オブジェクト出力ストリーム
ObjectInputStream を通じてオブジェクトをプログラムに読み込みます。
オブジェクトを保存しない一時型および静的型の変数
オブジェクトをシリアル化するには、そのクラスが Serializable インターフェイスを実装する必要があります。
ObjectOutputStream (処理ストリーム)
ObjectOutputStream は別のストリームから構築する必要があります。
FileOutputStream out = new FileOutputStream("theTime");
ObjectOutputStream s = 新しい ObjectOutputStream(out);
s.writeObject("Today");//ここのパラメータはすでに Serializable インターフェイスを実装しています
s.writeObject(new Date());//ここのパラメータはすでに Serializable インターフェイスを実装しています
s.flush();//バッファを空にする
ObjectInputStream (処理ストリーム)
ObjectInputStream は別のストリームから構築する必要があります。
FileInputStream in = new FileInputStream("theTime");
ObjectInputStream s = 新しい ObjectInputStream(in):
今日の文字列 = (String)s.readObject();
日付 date = (日付)s.readObject0);
定義したオブジェクトをファイル全体から読み取る必要がある場合、クラスは Serializable インターフェイス (実際には空のインターフェイス) を実装する必要があります。
シリアル化可能
- シリアライズ可能なインターフェースの定義
パッケージjava.io;
パブリック インターフェイス シリアル化可能 {
// ここには何もありません!
}
- Serializable インターフェイスを実装するステートメント
public class MyClass は Serializable { を実装します
...
}
- キーワード transient を使用して、オブジェクトの特定のメンバーが自動的にファイルに書き込まれるのを防ぎます。
- 実際、これはフラグであり、このクラスを設計するときは、オブジェクト全体をディスクに書き込むことができるようにすることを目的としています。
宿題
教科書P203ページの例題7~10を練習してください。
従業員オブジェクトを保存します (以前に実行した従業員定義で変更できます)