字节流
概述
计算机中任何数据(文本、图片、视频、音乐等)都是以二进制(字节)形式存储的。JDK中所有字节输入流继承自抽象类InputStream,所有字节输出流继承自抽象类OutputStream。它们是字节流的顶级父类。
字节流体系结构
InputStream和OutStream这两个虽然提供了一系列和读写有关的方法,但是这两个类是抽象类,不能被实例化。因此,针对不同功能,InputStream和OutputStream提供不同的子类,
形成的体系结构如下:


FileOutputStream
操作文件的字节输出流,专门用于把数据写入文件。
通过FileOutputStream将数据输出到文件内,案例代码如下:
//创建FileOutputStream对象
FileOutputStream fs = new FileOutputStream("d:\\临时文件夹\\测试\\1.txt");
//使用write(int b)将数据写出
fs.write(65);
fs.close();
System.out.println("执行完毕");
运行结果展示:

上述代码展示的是一次写出一个字节的情况,下面我们更改一段代码,将一次写出一组字节:
//创建FileOutputStream对象
FileOutputStream fs = new FileOutputStream("d:\\临时文件夹\\测试\\1.txt");
byte[] bytes = {65,66,67,68};
//使用write(bytes[] b)将数据写出
fs.write(bytes);
fs.close();
System.out.println("执行完毕");
运行结果展示:

字符流在每次输入前都会清空,如果不想清空,可以在创建FileOutputStream对象的构造方法后传入true,表示追加。
进入追加模式后,不会清空之前已有的内容,而是再原先的数据后继续输出。
我们也可以把字符串转为bytes[]数组,传入write(byte[] b)方法中输出,代码如下:
//创建FileOutputStream对象
FileOutputStream fs = new FileOutputStream("d:\\临时文件夹\\测试\\1.txt");
//使用getBytes()方法将String类型数据转为byte类型数组
byte[] bytes = "你好".getBytes();
//使用write(bytes[] b)将数据写出
fs.write(bytes);
fs.close();
System.out.println("执行完毕");
运行结果展示:

也可以通过write(byte[] b,int off,int len)对输出字节的开始索引和长度设置,代码如下:
//创建FileOutputStream对象
FileOutputStream fs = new FileOutputStream("d:\\临时文件夹\\测试\\1.txt");
//使用getBytes()方法将String类型数据转为byte类型数组
byte[] bytes = "A".getBytes();
//使用write(bytes[] b)将数据写出
fs.write(bytes,0,1);
fs.close();
System.out.println("执行完毕");
运行结果展示:

FileInputStream
FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于文件中读取数据是重复操作,因此需要通过循环语句实现数据的持续读取。
下面通过一个案例简单认识一下FileInputStream:
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
int i = fis.read();
System.out.println((char) i);
int i2 = fis.read();
System.out.println((char) i2);
fis.close();
运行结果如下:
A
B
通过上述案例,我们可以看出read()方法每此可以读取一个字节。
我们可以设想,
如果使用循环,可以持续读取数据
接下来我们使用循环读取,代码如下:
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
while (true) {
int i = fis.read();
System.out.println(i);
}
运行结果如下:
65
66
67
-1
-1
-1
-1
-1
-1
当文件中数据读取完成后,继续向后读取每次会返回-1。
在源代码中加入条件判断即可。当返回-1时退出循环。代码如下:
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
while (true) {
int i = fis.read();
if (i == -1){
break;
}
System.out.println(i);
}
fis.close();
}
运行结果如下:
65
66
67
下面我们来看一下read(byte[] b)方法的使用注意事项,代码如下:
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
//定义长度为10的byte数组
byte[] bytes = new byte[10];
//read(bytes[] b)读取字节保存在b中,返回读取的字节数
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.close();
运行结果如下:
ABCDEFGHIJ
KLMNOPQRST
UVWXYZQRST
之所以出现第3行结果的情况,
因为读取剩下7个字节的内容,覆盖原先前7个字节内容,最后3给字节没有覆盖。
为了解决这样的情况,
可以使用构造方法String(byte[] b,off,len),从off下标开始,len个长度的数组b转为String类型数据。
下面代码来看更改后的结果:
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
//定义长度为10的byte数组
byte[] bytes = new byte[10];
//read(bytes[] b)读取字节保存在b中,返回读取的字节数
int len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
fis.close();
运行结果如下:
ABCDEFGHIJ
KLMNOPQRST
UVWXYZ
定义变量len保存每次读取的长度,通过String构造方法截取字节数组中len个字节,
这样就可以避免覆盖重复发生。
我们在最后一行添加如下代码,注意此时文件中的数据已经全部读完:
len = fis.read(bytes);
System.out.println(len);
控制台打印输出:
-1
也就是说,当读取完所有字节后,返回的读取个数为-1。我们可以利用这个特点将它作为退出循环的条件。
代码如下:
FileInputStream fis = new FileInputStream("d:\\临时文件夹\\测试\\1.txt");
//定义长度为10的byte数组
byte[] bytes = new byte[10];
int len = 0;
while (true){
len = fis.read(bytes);
if (len == -1) {
break;
}
System.out.println(new String(bytes,0,len));
}
fis.close();
运行结果如下:
ABCDEFGHIJ
KLMNOPQRST
UVWXYZ
浙公网安备 33010602011771号