Java基础之文件字节流
字节流
文本、图片、视频等文件存储时,底层都是以二进制形式保存,即字节存储,所以数据传输时也可以采用字节流。在操作流时,无论使用什么样的流对象,底层数据的传输始终为二进制数据。
java.io.InputStream
是所有字节输入流的抽象父类
java.io.OutputStream
是所有字节输出流的抽象父类:
在代码中,使用流的基本步骤:
- 选择合适的IO类,实例化对象
- 调用IO流对象中的方法,去完成相应操作
- 操作完成后,关闭流(释放资源)
InputStream,OutputStream
有很多子类,我们从最简单的一个子类开始。
1)文件输入流
文件字节输入流
FileInputStream
,用于从文件中读取字节数据。
源码参考:
package java.io;public class FileInputStream extends InputStream {//略...//通过File对象来创建一个 FileInputStreampublic 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数组,返回成功读取字节数目,// 如果到文件末尾,则返回-1byte[] 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); //aos.write(98); //bos.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();}
}
注意事项:
- 逐个字节拷贝效率太低,推荐使用小数组拷贝
- 关闭资源的顺序应该和打开资源顺序相反:先打开的后关闭,后打开的先关闭