1.字节流的概念
在程序的开发中,我们经常会需要处理设备之间的数据传输,而计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的。
而对于字节的输入输出IO流提供了一系列的流,统称为字节流,字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。
在JDK中,提供了两个抽象类InputStream和OutputStream,它们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。为了方便理解,可以把InputStream和OutputStream比作两根“水管”。
在上图中,InputStream被看成一个输入管道,OutputStream被看成一个输出管道,数据通过InputStream从源设备输入到程序,通过OutputStream从程序输出到目标设备,从而实现数据的传输。由此可见,IO流中的输入输出都是相对于程序而言的。
在JDK中,InputStream和 OutputStream提供了一系列与读写数据相关的方法。
方法声明 | 功能描述 |
int read() | 从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数 |
int read(byte[] b) | 从输入流读取若干字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取字节的数目 |
int read(byte[] b,int off,int len) | 从输入流读取若干字节,把它们保存到参数b指定的字节数组中,off指定字节数组开始保存数据的起始下标,len表示读取的字节数目 |
void close() | 关闭此输入流并释放与该流关联的所有系统资源 |
在上面表格中,第一个read()方法是从输入流中逐个读入字节,而第二个和第三个read()方法则将若干字节以字节数组的形式一次性读入,从而提高读数据的效率。
在进行IO流操作时,当前IO流会占用一定的内存,由于系统资源宝贵,因此,在IO操作结束后,应该调用close()方法关闭流,从而释放当前IO流所占的系统资源。
与InputStream对应的是OutputStream。OutputStream是用于写数据的,因此OutputStream提供了一些与写数据有关的方法,OutputStream的常用方法如下表。
方法名称 | 方法描述 |
void write(int b) | 向输出流写入一个字节 |
void write(byte[] b) | 把参数b指定的字节数组的所有字节写到输出流 |
void write(byte[] b,int off,int len) | 将指定byte数组中从偏移量off开始的len个字节写入输出流 |
void flush() | 刷新此输出流并强制写出所有缓冲的输出字节 |
void close() | 关闭此输出流并释放与此流相关的所有系统资源 |
上述表格列举了OutputStream类的五个常用方法。
前三个是重载的write()方法,都是用于向输出流写入字节,其中,第一个方法逐个写入字节,后两个方法是将若干个字节以字节数组的形式一次性写入,从而提高写数据的效率。
flush()方法用来将当前输出流缓冲区(通常是字节数组)中的数据强制写入目标设备,此过程称为刷新。
close()方法是用来关闭流并释放与当前IO流相关的系统资源。
InputStream和OutputStream这两个类虽然提供了一系列和读写数据有关的方法,但是这两个类是抽象类,不能被实例化,因此,针对不同的功能,InputStream和OutputStream提供了不同的子类,这些子类形成了一个体系结构。
InputStream的子类如下图。
OnputStream的子类如下图。
InputStream就是JDk提供的基本输入流。但InputStream并不是一个接口,而是一个抽象类,它是所有输入流的父类,而FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件读取数据是重复的操作,因此需要通过循环语句来实现数据的持续读取。
2. InputStream读文件
接下来通过一个案例来实现字节流对文件数据的读取。在实现案例之前,首先在Java项目的根目录下创建一个文本文件test.txt,在文件中输入内容“itcast”并保存;然后使用字节输入流对象来读取test.txt文本文件。
import java.io.*;
public class Example08 {
public static void main(String[] args) throws Exception {
// 创建一个文件字节输入流
FileInputStream in = new FileInputStream("test.txt");
int b = 0; // 定义一个int类型的变量b,记住每次读取的一个字节
while (true) {
b = in.read(); // 变量b记住读取的一个字节
if (b == -1) { // 如果读取的字节为-1,跳出while循环
break;
}
System.out.println(b); // 否则将b写出
}
in.close();
}
}
上述代码中,第5行代码创建的字节流FileInputStream,第7~13行代码通过read()方法将当前项目中的文件“test.txt”中的数据读取并打印。
从运行结果可以看出,控制台打印的结果分别为105、116、99、97、115和116。由于计算机中的数据都是以字节的形式存在的。在“test.txt”文件中,字符‘i’、‘t’、‘c’、‘a’、‘s’、‘t’各占一个字节,因此,最终结果显示的就是文件“test.txt”中的六个字节所对应的十进制数。
3 OnputStream写文件
OutputStream是JDK提供的最基本的输出流,与InputStream类似的是OutputStream也是抽象类,它是所有输出流的父类。OutputStream是一个抽象类,如果使用此类,则首先必须通过子类实例化对象。FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专门用于把数据写入文件。
接下来通过一个案例来演示如何使用FileOutputStream将数据写入文件。
import java.io.*;
public class Example10
public static void main(String[] args) throws Exception {
// 创建一个文件字节输出流
OutputStream out = new FileOutputStream("example.txt");//绝对路径
String str = "你的名字+学号";
byte[] b = str.getBytes();
for (int i = 0; i < b.length; i++) {
out.write(b[i]);
}
out.close();
}
}
4.代码提升
题目描述:编写一个Java程序,实现将一个文本文件a.txt中的内容逐行读取,并将每行内容逆序写入到另一个文件b.txt中。
要求使用FileInputStream读取源文件内容,
使用FileOutputStream将逆序后的内容写入到目标文件。
a.txt文件内容:我是一个测试内容
1.a.txt在D盘手动创建,包括内容
2.b.txt用代码创建
3.实现逆序内容的保存
import java.io.*;
public class ReverseFileContent {
public static void main(String[] args) {
File sourceFile = new File("D:/a.txt");
File targetFile = new File("b.txt");
try (BufferedReader reader = new BufferedReader(new FileReader(sourceFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(targetFile))) {
String line;
while ((line = reader.readLine()) != null) {
StringBuilder reversedLine = new StringBuilder(line).reverse();
writer.write(reversedLine.toString());
writer.newLine();
}
System.out.println("文件内容逆序写入成功!");
} catch (IOException e) {
System.out.println("文件操作失败:" + e.getMessage());
}
}
}
5.字节缓冲流
IO提供两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream,它们的构造方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能。应用程序、缓冲流和底层字节流之间的关系如下图。
接下来通过一个案例学习BufferedInputStream和BufferedOutputStream 这两个流的用法。首先在Java项目的根目录下创建一个名称为src.txt的文件,并在该文件中随意写入一些内容;然后创建一个类,在类中使用FileOutputStream创建文件des.txt,并使用字节缓冲流对象将文件src.txt中的内容拷贝到文件des.txt中。
import java.io.*;
public class Example14 {
public static void main(String[] args) throws Exception {
// 创建一个带缓冲区的输入流
BufferedInputStream bis = new BufferedInputStream(new
FileInputStream("src.txt"));
// 创建一个带缓冲区的输出流
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("des.txt"));
int len;
while ((len = bis.read()) != -1) {
bos.write(len);
}
bis.close();
bos.close();
}
}
上述代码中,第5~6行代码分别创建了BufferedInputStream和BufferedOutputStream两个缓冲流对象,这两个流内部都定义了一个大小为8192的字节数组,第11~12行代码中调用read()或者write()方法读写数据时,首先将读写的数据存入定义好的字节数组,然后将字节数组的数据一次性读写到文件中