Java 提供了许多实现文件输入 / 输出的类。 这些类可以分为文本 I/O 类
和二进制 I/O 类
在Java中如何处理文本I/O
使用
Scanner类
读取文本数据,使用PrintWriter类
写文本数据
为了将文本写入一个名为temp.txt
的文件中,可以使用PrintWriter
类创建一个对象:
PrintWriter output = new PrintWriter("temp.txt");
可以调用该对象的print
方法向文件写入一个字符串
output.print("Java 101");
之后关闭文件
output.close();
从文件temp.txt
中读入数据:
Scanner input = new Scanner(new File("temp.txt"));
System.out.println(input.nextLine());
文本I/O 与 二进制I/O
二进制 I / O 不涉及编码和解码 , 因此比文本 I / O 更加高效
为了保持一致性,使用扩展名.txt
来命名文本文件,使用.dat
来命名二进制文件
二进制I/O类
抽象类
InputStream
是读取二进制数据的根类 , 抽象类OutputStream
是写入二进制数据的根类
**二进制 I / O 类 中 的 所 有 方 法 都 声 明 为 抛 出java.io.IOException
或 java.io. IOException
的子类 **
FileInputStream
和 FileOutputStream
FileInputStream
: 从文件读取字节
FileOutputStream
:向文件写入字节
- 构造一个
FileInputStream
对象
如果试图为一个不存在的文件创建 FilelnputStream
对象 , 将会发生java.io.FileNotFoundException
异常
- 构造一个
FileOutputStream
对象
-
如果这个文件不存在 , 就会创建一个新文件 。 如果这个文件已经存在 , 前两个构造方法将会删除文件的当前内容 。 为了既保留文件现有的内容又可以给文件追加新数据 , 将最后两个构造方法中的参数 append 设置为 true 。
-
几乎所有的
I/O
类中的方法都会抛出异常java.io.IOException
使用二进制 I / O 将从 1 到 10 的 10 个字节值写入一个名为 temp . dat 的文件 , 再把它们从文件中读出来
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileStream {
public static void main(String[] args) throws IOException {
try( // 利用try-with-resources来声明和创建输入流
FileOutputStream output = new FileOutputStream("temp.dat");
){
for (int i = 1; i <= 10; i++)
output.write(i); // 也可以写成 output.write((byte)i)
}
try( // 利用try-with-resources来声明和创建输出流
FileInputStream input = new FileInputStream("temp.dat");
){
int value;
while((value = input.read()) != -1) // -1意味着文件结束
System.out.print(value + " ");
}
}
}
1 2 3 4 5 6 7 8 9 10
java.io.InputStream
和java.io.OutputStream
实现了AutoClosable
接口,该接口中定义了close()
方法,用于关闭资源。任何AutoClosable
类型的对象可以用于try-with-resources
语法中实现自动关闭。
FileInputStream
类的实例可以作为参数去构造一个Scanner
对象,而FileOutputStream
类的实例可以作为参数构造一个PrinterWriter
对象
new PrintWriter(new FileOutputStream("temp.txt", true));
FilterInputStream
和FilterOutputStream
DataInputStream
和DataOutputStream
DataInputStream
从数据流读取字节,并且将他们转换为合适的基本类型值或字符串
DataOutputStream
将基本类型的值或字符串转换为字节,并且将字节输出到数据流
二进制I/O中的字符与字符串
— 个统一码由两个字节构成。
writeChar(char c)
方法将字符c的统一码写入输出流writeChars(String s)
方法将字符串s中所有字符的统一码写到输出流中writeBytes(String s)
方法将字符串s中每个字符统一码的低字节写到输出流,统一码的高字节被丢弃,该方法适用于由ASCII
码字符构成的字符串(因为ASCII
码仅仅存储统一码的低字节)- 如果一个字符串包含非
ASCII
码的字符,必须使用writeChars
方法实现写入这个字符串
- 如果一个字符串包含非
writeUTF(String s)
方法将两个字节的长度信息写入输出流,后面紧跟的是字符串s中每个字符的改进版UTF-8
的形式readUTF()
方法读取一个使用writeUTF
方法写入的字符串UTF-8
格式具有存储每个ASCII
码就节省一个字节的优势
writeUTF("ABCDEF")
写入文件的8个字节(00 06 41 42 43 44 45 46)
,头两个字节存储的是字符串中的字符个数
创建DataInputStream
类和DataOutputStream
类
根据上面的UML图,通过下面构造方法来创建DataInputStream
类和DataOutputStream
类
public DataInputStream(InputStream instream);
public DataOutputStream(OutputStream outStream);
DataInputStream input = new DataInputStream(new FileInputStream("in.dat")); // 为文件in.dat创建一个输入流
DataOutputStream output = new DataOutputStream(new FileOutputStream("out.dat")); // 为文件out.dat创建一个输出流
例子:将学生的名字和分数写入名为temp.dat
的文件中
import java.io.*;
public class TestDataStream {
public static void main (String[] args) throws IOException {
try (
DataOutputStream output =
new DataOutputStream(new FileOutputStream("temps.dat"));
){
output.writeUTF("yzy");
output.writeDouble(89.0);
output.writeUTF("wjy");
output.writeDouble(90.2);
}
try(
DataInputStream input =
new DataInputStream(new FileInputStream("temps.dat"));
){
for (int i = 1; i <= 2; i++)
System.out.println(input.readUTF() + " " + input.readDouble());
}
}
}
yzy 89.0
wjy 90.2
检测文件的末尾
如果到达InputStream
的末尾之后还继续从中读取数据 , 就会发生 EOFException
异常 。
这个异常可以用来检査是否已经到达文件末尾
import java.io.*;
public class TestEOF {
public static void main(String[] args) throws IOException {
try {
try (
DataOutputStream output =
new DataOutputStream(new FileOutputStream("testEOF.dat"));
) {
output.writeDouble(1.1);
output.writeDouble(2.2);
output.writeDouble(3.3);
}
try(
DataInputStream input =
new DataInputStream(new FileInputStream("testEOF.dat"));
){
while(true){
System.out.println(input.readDouble());
}
}
}
catch (EOFException ex){
System.out.println("All data were read");
}
catch (IOException ex){
ex.printStackTrace();
}
}
}
1.1
2.2
3.3
All data were read
BufferedInputStream
和BufferedOutputStream
这两个类可以通过减少磁盘读写次数来提高输入和输出速度
这两个类没有包含新的方法,所有方法继承自InputStream
类和OutputStream
类
如果没有指定缓冲区大小 , 默认的大小是 512 个字节
DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("temp.dat")));
DataInputStream input = new DataInputStream(
new BufferedInputStream(new FileInputStream("temp.dat")));
实例学习:复制文件
import java.io.*;
public class Copy {
public static void main(String[] args) throws IOException {
if (args.length != 2){
System.out.println(" Usage : java Copy sourceFile targetfile");
System.exit(1);
}
File sourceFile = new File(args[0]);
if (!sourceFile.exists()){
System.out.println("sourceFile does not exist");
System.exit(2);
}
File targetFile = new File(args[1]);
if (targetFile.exists()){
System.out.println("targetFile already exist");
System.exit(3);
}
try(
BufferedInputStream input = new BufferedInputStream(
new FileInputStream(sourceFile));
BufferedOutputStream output = new BufferedOutputStream(
new FileOutputStream(targetFile));
){
int r, numberOfByteCopied = 0;
while ((r = input.read()) != -1){
output.write((byte)r);
numberOfByteCopied++;
}
System.out.println("number of copied : " + numberOfByteCopied);
}
}
}
D:JAVA_IDEAJavaProjectsChapter05src>java Copy testCopy.txt target.txt
number of copied : 23
对象I/O
ObjectInputStream
类和ObjectOutputStream
类可以用于读/写可序列化的对象,也可以实现基本数据类型与字符串的输人和输出,包含了DataInputStream
类和DataOutputStream
类的所有功能
public ObjectInputStream(InputStream in) // 创建一个对象输入流
public ObjectOutputStream(OutputStream out) // 创建一个对象输出流
- 姓名 、 分数和当前日期写入名为
object.dat
的文件
import java.io.*;
public class TestObjectOutputStream{
public static void main(String[] args) throws IOException{
try(
ObjectOutputStream output =
new ObjectOutputStream(new FileOutputStream("object.dat"));
){
output.writeUTF("John");
output.writeDouble(33.3);
outout.writeObject(new java.util.Date()); // 将对象写入文件
}
}
}
可以添加缓冲区:
ObjectOutputStream output = new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream("object.dat")));
- 读回这些对象:按照写入时的类型和顺序。为了得到所需的类型 , 必须使用 Java 安全的类型转换
import java.io.*;
public class TestObjectOUtputStream{
public static void main(String[] args)
throws ClassNotFoundException, IOException{
try(
ObjectInputStream input =
new ObjectInputStream(new FileInputStream("object.dat"));
){
String name = input.readUTF();
double score = input.readDouble();
java.util.Date date = (java.util.Date)(input.readObject());
// readObject()方法可能会拋出异常java.lang.ClassNotFoundException
// readObject()读入的是Object对象,所以应将他转换为Date类型
System.out.println(name + " " + score + " " + date);
}
}
}
Serializable
接口
序列化数组
如果数组中的所有元素都是可序列化的 , 这个数组就是可序列化的 。 一个完整的数组可以用 writeObject
方法存入文件 , 随后用 readObject
方法恢复
import java.io.*;
public class TestObjectStreamWithArray {
public static void main(String[] args) throws IOException, ClassNotFoundException {
int[] num = {1, 2, 3, 4, 5};
String[] str = {"shang", "shan", "da", "lao", "hu"};
try (
ObjectOutputStream output =
new ObjectOutputStream(new FileOutputStream("testOb.dat", true));
){
output.writeObject(num);
output.writeObject(str);
}
try (
ObjectInputStream input =
new ObjectInputStream(new FileInputStream("testOb.dat"));
){
int[] number = (int[])(input.readObject());
String[] strings = (String[])(input.readObject());
for (int i = 0; i < number.length; i++){
System.out.print(number[i] + " ");
}
System.out.println();
for (int i = 0; i < strings.length; i++){
System.out.print(strings[i] + " ");
}
}
}
}
1 2 3 4 5
shang shan da lao hu
随机访问文件
Java 提供了
RandomAccessFile
类 , 允许从文件的任何位置进行数据的读写
-
上面学习的都是只读的和只写的,这称为顺序流, 使用顺序流打开的文件称为**顺序访问文件 **。 顺序访问文件的内容不能更新 。
-
Java 提供了
RandomAccessFile
类 , 允许在文件的任意位置上进行读写 。 使用RandomAccessFile 类
打开的文件称为随机访问文件
RandomAccessFile raf = new RandomAccessFile("test.dat", "rw"); // rw为可读写的
- 如果文件
test.dat
已经存在 , 则创建raf
以便访问这个文件 - 如果
test.dat
不存在,则创建一个名为test.dat
的新文件,再创建raf
来访问这个新文件
随机访问文件是由字节序列组成的。一个称为文件指针
的特殊标记定位这些字节中的某个字节的位置
import java.io.*;
public class TestRandomAccessFile {
public static void main(String[] args) throws IOException {
try(
RandomAccessFile inout = new RandomAccessFile("inout.dat", "rw");
){
inout.setLength(0); // 清空文件
for (int i = 0; i < 200; i++){ // 写入200个数字
inout.writeInt(i);
}
System.out.println("目前文件长度为:" + inout.length());
inout.seek(0); // 将文件指针移动到头位置
System.out.println("第一个数:" + inout.readInt());
inout.seek(9 * 4); // 将文件指针移到第10个数位置
System.out.println("第10个数:" + inout.readInt());
inout.seek(inout.length()); // 将文件指针移动末尾
inout.writeInt(999);
System.out.println("现在文件长度为:" + inout.length());
}
}
}
目前文件长度为:800
第一个数:0
第10个数:9
现在文件长度为:804
Write by Gqq