字节流
字数: 0 字 时长: 0 分钟
我们必须明确一点:一切文件(文本、视频、图片)的数据都是以二进制形式存储的,传输时也是如此。
所以,字节流可以传输任意类型的文件数据。
1. 字节输出流(OutputStream)
java.io.OutputStream 是字节输出流的超类(父类),它定义了一些字节输出流共有的方法:
close():关闭此输出流,并释放与此流相关联的系统资源。flush():刷新此输出流,并强制缓冲区中的字节写入目的地。write(byte[] b):将b.length个字节从指定的字节数组写入此输出流。write(byte[] b, int off, int len):从指定字节数组的off偏移位置开始,写入len个字节。
2. FileOutputStream 类
OutputStream 有很多子类,我们从最简单的一个子类 FileOutputStream 开始。
看名字就知道,它是文件输出流,用于将数据写入文件。
2.1 FileOutputStream 的构造方法
2.1.1 使用文件名创建 FileOutputStream
String fileName = "example.txt";
FileOutputStream fos =
new FileOutputStream(fileName);以上代码使用文件名 "example.txt" 创建一个 FileOutputStream 对象,并将数据写入该文件。
如果文件不存在,则创建一个新文件;如果文件已经存在,则覆盖原有文件。
2.1.2 使用 File 对象创建 FileOutputStream
File file =
new File("example.txt");
FileOutputStream fos =
new FileOutputStream(file);FileOutputStream 的使用示例:
FileOutputStream fos = null;
try {
fos =
new FileOutputStream(
"example.txt"
);
fos.write(
"kerwim".getBytes()
);
} catch (
IOException e
) {
e.printStackTrace();
} finally {
if (
fos != null
) {
try {
fos.close();
} catch (
IOException e
) {
e.printStackTrace();
}
}
}以上代码创建了一个 FileOutputStream 对象,将字符串 "kerwim" 写入到 example.txt 文件中,并在最后关闭输出流。
2.2 FileOutputStream 写入字节数据
使用 FileOutputStream 写入字节数据主要通过 write() 方法:
write(int b)
write(byte[] b)
write(byte[] b, int off, int len)其中:
write(byte[] b, int off, int len)表示:
从
off索引开始,写入len个字节。
2.2.1 写入单个字节
使用:
write(int b)每次可以写入一个字节。
代码如下:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt"
);
// 写出数据
fos.write(97);
fos.write(98);
fos.write(99);
// 关闭资源
fos.close();字符 a 的 ASCII 值为 97,字符 b 的 ASCII 值为 98,字符 c 的 ASCII 值为 99。
所以上面的代码也可以写成:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt"
);
// 写出数据
fos.write('a');
fos.write('b');
fos.write('c');
// 关闭资源
fos.close();当使用:
write(int b)写出一个字节时,参数 b 表示要写出的字节的整数值。
由于一个字节只有 8 位,因此最终真正写入的是参数 b 的低 8 位。
例如:
-1
↓
低 8 位
↓
255
256
↓
低 8 位
↓
0在 Java 中,整型数据在内存中以二进制补码形式表示。
当一个 int 类型的数据传递给:
write(int b)时,只会写入该整数的低 8 位,高 24 位会被忽略。
例如:
0x12345678对应的二进制可以表示为:
0001 0010 0011 0100 0101 0110 0111 1000调用:
write(0x12345678);时,真正写入的是低 8 位:
0111 1000高 24 位:
0001 0010 0011 0100 0101 0110会被忽略。
0111 1000 对应的十进制数是:
120而 ASCII 码中:
120 → x因此:
write(120);
write('x');
write(0x12345678);最终写出的结果其实都是字符:
x我们来验证一下:
FileOutputStream fos5 = null;
try {
fos5 =
new FileOutputStream(
"example.txt"
);
fos5.write(120);
fos5.write('x');
fos5.write(0x12345678);
} catch (
IOException e
) {
e.printStackTrace();
} finally {
if (
fos5 != null
) {
try {
fos5.close();
} catch (
IOException e
) {
e.printStackTrace();
}
}
}来看一下结果:

果然是 3 个 x。
2.2.2 写入字节数组
使用:
write(byte[] b)代码示例:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt"
);
// 字符串转换为字节数组
byte[] b =
"kerwim is good".getBytes();
// 写入字节数组数据
fos.write(b);
// 关闭资源
fos.close();2.2.3 写入指定长度的字节数组
使用:
write(byte[] b, int off, int len)代码示例:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt"
);
// 字符串转换为字节数组
byte[] b =
"abcde".getBytes();
// 从索引 2 开始写入 2 个字节
// 索引 2 是 c,两个字节就是 cd
fos.write(
b,
2,
2
);
// 关闭资源
fos.close();2.3 FileOutputStream 实现数据追加、换行
在上面的代码示例中,每次运行程序都会创建新的输出流对象,于是文件中的数据也会被清空。
如果想保留目标文件中的数据,并继续追加新数据,该怎么办?
以及如何实现换行?
其实很简单。
FileOutputStream 还提供了另外两个构造方法。
2.3.1 使用文件名和追加标志创建
String fileName =
"example.txt";
boolean append =
true;
FileOutputStream fos =
new FileOutputStream(
fileName,
append
);以上代码使用文件名 "example.txt" 和追加标志创建一个 FileOutputStream 对象,并将数据追加到文件末尾。
如果文件不存在,则创建新文件;如果文件已经存在,则在文件末尾追加数据。
2.3.2 使用 File 对象和追加标志创建
File file =
new File(
"example.txt"
);
boolean append =
true;
FileOutputStream fos =
new FileOutputStream(
file,
append
);这两个构造方法的第二个参数都是 boolean 类型:
true
↓
追加数据
false
↓
不追加,清空原有数据实现数据追加:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt",
true
);
// 字符串转换为字节数组
byte[] b =
"abcde".getBytes();
// 写入数据
fos.write(b);
// 关闭资源
fos.close();多次运行代码,就会发现数据在不断追加。
2.3.3 换行
在 Windows 系统中,换行符通常是:
\r\n具体代码如下:
// 使用文件名称创建流对象
FileOutputStream fos =
new FileOutputStream(
"fos.txt",
true
);
// 字符串转换为字节数组
byte[] b =
"abcde".getBytes();
// 写入数据
fos.write(b);
// 关闭资源
fos.close();在 macOS 系统中,换行符通常是:
\n具体代码如下:
String filename =
"example.txt";
FileOutputStream fos =
new FileOutputStream(
filename,
true
);
String content =
"kerwim is good\n";
fos.write(
content.getBytes()
);
fos.close();回车符 \r 和换行符 \n 是计算机中常见的控制字符,用于表示一行的结束或换行操作。
在不同的操作系统和编程语言中,它们的使用方式可能有所不同。
Windows 系统中通常使用:
\r\n表示一行结束。
在文本文件中,每行末尾通常由一个回车符和一个换行符组成。
这是因为早期的打印机和终端设备需要回车符和换行符的组合,来完成一行的结束和换行操作。
在 Windows 中,文本编辑器和命令行终端等工具都支持使用:
\r\n表示一行结束。
而在 macOS 和 Linux 系统中,通常只使用:
\n表示一行结束。
在文本文件中,每行末尾通常只有一个换行符。
这是因为早期 Unix 系统中的终端设备只需要换行符,就可以完成一行结束和跨行操作。
在 macOS 和 Linux 中,文本编辑器和终端等工具都支持使用:
\n表示一行结束。
在编程语言中,也经常使用回车符和换行符进行字符串操作。
例如,在 Java 中:
\r表示回车符:
\n表示换行符。
通过输入输出流进行文件读写时,也需要注意回车符、换行符和操作系统之间的差异。
3. 字节输入流(InputStream)
java.io.InputStream 是字节输入流的超类(父类),它定义了一些字节输入流共有的方法:
close():关闭此输入流,并释放与此流相关的系统资源。int read():从输入流读取数据的下一个字节。read(byte[] b):从输入流中读取数据到字节数组中,返回值表示本次读取到的字节数量;如果读取到文件末尾,则返回-1。
4. FileInputStream 类
InputStream 有很多子类,我们从最简单的一个子类 FileInputStream 开始。
看名字就知道,它是文件输入流,用于从文件中读取数据。
4.1 FileInputStream 的构造方法
4.1.1 FileInputStream(String name)
FileInputStream(String name)创建一个 FileInputStream 对象,并打开指定名称的文件进行读取。
文件名由 name 参数指定。
如果文件不存在,会抛出:
FileNotFoundException4.1.2 FileInputStream(File file)
FileInputStream(File file)创建一个 FileInputStream 对象,并打开指定 File 对象所表示的文件进行读取。
代码示例如下:
// 创建一个 FileInputStream 对象
FileInputStream fis =
new FileInputStream(
"test.txt"
);
// 读取文件内容
int data;
while (
(data = fis.read()) != -1
) {
System.out.print(
(char) data
);
}
// 关闭输入流
fis.close();4.2 FileInputStream 读取字节数据
4.2.1 读取单个字节
read() 方法会读取一个字节,并返回它的整数表示。
如果已经到达文件末尾,则返回:
-1如果读取过程中发生错误,则会抛出:
IOException代码示例如下:
// 创建一个 FileInputStream 对象
FileInputStream fis =
new FileInputStream(
"test.txt"
);
// 读取文件内容
int data;
while (
(data = fis.read()) != -1
) {
System.out.print(
(char) data
);
}
// 关闭输入流
fis.close();4.2.2 使用字节数组读取
read(byte[] b) 方法会从输入流中最多读取 b.length 个字节,并将读取到的数据保存到字节数组 b 中。
代码示例如下:
// 创建一个 FileInputStream 对象
FileInputStream fis =
new FileInputStream(
"test.txt"
);
// 读取文件内容到缓冲区
byte[] buffer =
new byte[1024];
int count;
while (
(count = fis.read(buffer)) != -1
) {
System.out.println(
new String(
buffer,
0,
count
)
);
}
// 关闭输入流
fis.close();4.3 字节流 FileInputStream 复制图片
原理很简单:
图片文件
↓
FileInputStream
↓
读取字节数据
↓
FileOutputStream
↓
写入新的图片文件代码示例如下:
// 创建 FileInputStream 对象,读取原始图片文件
FileInputStream fis =
new FileInputStream(
"original.jpg"
);
// 创建 FileOutputStream 对象,写入复制后的图片文件
FileOutputStream fos =
new FileOutputStream(
"copy.jpg"
);
// 创建缓冲区数组,用于存储读取的数据
byte[] buffer =
new byte[1024];
int count;
// 读取原始图片文件
// 并将数据写入复制后的图片文件
while (
(count = fis.read(buffer)) != -1
) {
fos.write(
buffer,
0,
count
);
}
// 关闭输入流和输出流
fis.close();
fos.close();上面的代码创建了一个 FileInputStream 对象来读取原始图片文件,并创建一个 FileOutputStream 对象,将数据写入复制后的图片文件。
然后使用 while 循环不断读取原始图片文件中的字节,并将读取到的数据写入新的图片文件。
最后关闭输入流和输出流,释放资源。
5. 小结
InputStream 是字节输入流的抽象类,它定义了读取字节数据的方法,例如:
read()
read(byte[] b)
read(byte[] b, int off, int len)OutputStream 是字节输出流的抽象类,它定义了写入字节数据的方法,例如:
write(int b)
write(byte[] b)
write(byte[] b, int off, int len)这两个抽象类是整个字节流体系的基础。
字节流
│
├── InputStream
│ ↓
│ 字节输入流
│
└── OutputStream
↓
字节输出流FileInputStream 是从文件中读取字节数据的流,它继承自 InputStream。
FileOutputStream 是将字节数据写入文件的流,它继承自 OutputStream。
这两个类都是字节流中最常用的实现类之一。