字节流
文本、图片、视频等文件存储时,底层都是以二进制形式保存,即字节存储,所以数据传输时也可以采用字节流。在操作流时,无论使用什么样的流对象,底层数据的传输始终为二进制数据。
java.io.InputStream
是所有字节输入流的抽象父类
java.io.OutputStream
是所有字节输出流的抽象父类:
在代码中,使用流的基本步骤:
- 选择合适的IO类,实例化对象
- 调用IO流对象中的方法,去完成相应操作
- 操作完成后,关闭流(释放资源)
InputStream,OutputStream
有很多子类,我们从最简单的一个子类开始。
1)文件输入流
文件字节输入流
FileInputStream
,用于从文件中读取字节数据。
源码参考:
package java.io;
public class FileInputStream extends InputStream {
//略...
//通过File对象来创建一个 FileInputStream
public FileInputStream(File file) throws FileNotFoundException;
//通过文件路径名(字符串)实例化FileInputStream对象
public FileInputStream(String name) throws FileNotFoundException;
//逐个字节读取,返回值为读取的单个字节
public int read() throws IOException;
//小数组读取,将结果存入数组,返回值为读取的字节个数
public int read(byte b[]) throws IOException;
//小数组读取,存入数组指定位置,返回值为读取的字节个数
public int read(byte b[], int off, int len) throws IOException;
//省略...
}
案例展示:
使用3种read方法,读取文件D:\\test\\a.txt
内容,掌握read的用法。a.txt
文件内容如下:
按下图编写测试代码:
注意:IO操作每个方法都可能产生异常,具体如上图。
关于异常的处理,我们之前学过两种处理方式:
- 当前方法中声明继续抛出,借助
throws
实现 - 自行捕获处理,借助
try-catch
块实现
我们暂时采用第一种方式来处理异常:
package com.briup.chap11.test;
public class Test024_Read {
public static void main(String[] args) throws IOException {
//1.创建流对象【IO流对象 跟 文件进行关联】
InputStream is = new FileInputStream("D:\\test\\a.txt");
System.out.println("is: " + is);
//2.读取文件内容
// 1次读1个字节返回,如果到文件末尾,则返回-1
// int read();
int r = is.read();
System.out.println("第1个字节:" + r);
r = is.read();
System.out.println("第2个字节:" + r);
r = is.read();
System.out.println("第3个字节:" + r);
r = is.read();
System.out.println("第4个字节:" + r); //-1
//3.关闭流对象,释放资源
is.close();
}
}
//输出结果:
is: java.io.FileInputStream@7852e922
第1个字节:97
第2个字节:98
第3个字节:99
第4个字节:-1
**注意:**创建FileInputStream对象时,必须传入一个有效文件路径,否则系统自动抛出FileNotFoundException
异常。
read方法测试:int read(byte[] arr);
public static void main(String[] args) throws IOException {
//1.创建流对象【IO流对象 跟 文件进行关联】
InputStream is = new FileInputStream("D:\\test\\a.txt");
System.out.println("is: " + is);
//2.读取文件内容
// int read(byte[] arr);
// 读多个字节,放入arr数组,返回成功读取字节数目,
// 如果到文件末尾,则返回-1
byte[] arr = new byte[10];
int len = is.read(arr);
System.out.println("成功读取字节数目:" + len);
//遍历数组有效内容
for(int i = 0; i < len; i++)
System.out.println(arr[i]);
System.out.println("------------");
//再次读取
len = is.read(arr);
System.out.println("第二次读取: " + len); // -1
//3.关闭流对象,释放资源
is.close();
}
//输出结果:
is: java.io.FileInputStream@7852e922
成功读取字节数目:3
97
98
99
------------
第二次读取: -1
read方法测试:int read(byte[] arr,int off,int len);
public static void main(String[] args) throws Exception {
//1.创建流对象
InputStream is = new FileInputStream("D:\\test\\a.txt");
System.out.println("is: " + is);
//2.读取
// 读取5个字节往arr中 往后偏移3个位置 放入
// 如果读取成功,则返回实际读取长度
// 如果返回-1,则表示读取到文件末尾
byte[] arr = new byte[10];
int len = is.read(arr,3,5); //arr[ , , , a, b, c, ...]
System.out.println("成功读取: " + len);
//遍历数组所有内容
for(int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
//3.关闭资源
is.close();
}
//输出结果:
is: java.io.FileInputStream@7852e922
成功读取: 3
0 0 0 97 98 99 0 0 0 0
2)文件输出流
文件字节输出流,FileOutputStream
,用于写入字节数据到文件中。
FileOutputStream
源码:
package java.io;
public class FileOutputStream extends OutputStream
//创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(File file) throws FileNotFoundException;
//创建文件输出流以指定的名称写入文件
public FileOutputStream(String name) throws FileNotFoundException;
//创建流对象,可配置是否追加
public FileOutputStream(File file, boolean append) throws FileNotFoundException;
//写操作方法
public void write(int b) throws IOException;
public void write(byte b[]) throws IOException;
public void write(byte b[], int off, int len) throws IOException;
//省略...
}
注意事项:
-
创建输出流对象时传入的文件路径如果不存在,系统会自动创建该文件
-
如果该文件路径存在,系统默认会清空这个文件中的数据
案例实现:
提前创建好目录src/dir
,然后使用文件输出流write
字节到src/dir/a.txt
中。
package com.briup.chap11.test;
public class Test024_Write {
public static void main(String[] args) throws Exception {
//1.关联流对象和文件
// 实例化输出流时,目标文件a.txt不存在不会抛异常,系统会自动创建
// 但src/dir目录必须存在,系统不会自动创建多级目录
OutputStream os = new FileOutputStream("src/dir/a.txt");
System.out.println("os: " + os);
//2.逐个字节写数据
os.write(97); //a
os.write(98); //b
os.write(99); //c
//3.关闭资源
os.close();
}
public static void main02(String[] args) throws Exception {
//1.关联流对象和文件
OutputStream os = new FileOutputStream("src/dir/a.txt");
System.out.println("os: " + os);
//2.写字节数组
String str = "abcd";
byte[] arr = str.getBytes();
//将arr所有元素全部写入文件
//写入 会 覆盖 文件原有内容
os.write(arr);
//3.关闭资源
os.close();
}
public static void main03(String[] args) throws Exception {
//1.关联流对象和文件
OutputStream os = new FileOutputStream("src/dir/a.txt");
System.out.println("os: " + os);
//2.写字节数组中的一部分数据 '1''2''3''4''5'
byte[] arr = {49,50,51,52,53,54,55};
//os.write(arr,0,arr.length);
//从arr[2]开始,获取arr数组的3个字节,即[51,52,53],然后写入a.txt
//写入 会 覆盖 文件原有内容
os.write(arr,2,3);
// 写出一个换行, 换行符号转成数组写出
os.write("\r\n".getBytes());
//3.关闭资源
os.close();
}
}
注意:不同操作系统中回车、换行符是不同的
回车和换行:
-
回车符:回到一行的开头(return)
-
换行符:下一行(newline)
各系统中的换行:
-
Windows系统里,每行结尾是
回车+换行
,即\r\n
; -
Unix系统里,每行结尾只有
换行
,即\n
; -
Mac系统里,每行结尾是
回车
,即\r
。从 Mac OS X开始与Linux统一。
综合案例:
拷贝src/dir/a.txt
内容到src/dir/b.txt
中,a.txt内容如下:
hello world
1、确认过眼神,我遇上的人。我策马出征,马蹄声如泪奔。青石板上的月光照进这山城。
我一路的跟,你轮回声,我对你用情极深。 --方文山 《醉赤壁》
2、无关风月 我题序等你回 悬笔一绝 那岸边浪千叠 --方文山 《兰亭序》
3、那画面太美,我不敢看 --方文山 《布拉格广场》
4、你说 想哭就弹琴 想起你就写信情绪来了就不用太安静 --方文山 《想你就写信》
代码实现:
package com.briup.chap11.test;
public class Test024_Copy {
public static void main(String[] args) throws Exception {
//1.关联文件和流对象
InputStream is = new FileInputStream("src/dir/a.txt");
OutputStream os = new FileOutputStream("src/dir/b.txt");
//2.拷贝
//2.1 逐个字节拷贝
// int r;
// while((r = is.read()) != -1) {
// os.write(r);
// }
//2.2 小数组拷贝,使用最多
byte[] arr = new byte[8];
int len;
while((len = is.read(arr)) != -1) {
//注意事项:读取多少个字节 就写出多少个字节
os.write(arr,0,len);
}
System.out.println("拷贝完成");
//3.关闭资源
//注意:先关闭后打开的,后关闭先打开的
os.close();
is.close();
}
}
注意事项:
- 逐个字节拷贝效率太低,推荐使用小数组拷贝
- 关闭资源的顺序应该和打开资源顺序相反:先打开的后关闭,后打开的先关闭