IO流
一、IO流原理及流的分类
IO流的原理
- I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
- Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
- java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
流的分类:
- 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流
| 抽象基类 | 字节流 | 字符流 |
|---|---|---|
| 输入流 | InputStream | Reader |
| 输出流 | OutputStream | Writer |
- Java的IO流共涉及40多个类,实际上非常规则,都是从以上4个抽象基类派生的。
- 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

IO 流体系

二、 节点流(或文件流)
字符流:
FileReader读入数据的基本操作
读取文件的步骤:
1、实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");
2、FileReader流的实例化
FileReader fileReader = new FileReader(file);
3、读入数据的操作
char[] ch = new char[1024];
fileReader.read(ch);
4、关闭资源
fileReader.close();
FileReader读取数据操作:
方式一:
@Test public void FileReaderTest() throws IOException { //1、实例化File类的对象,指明要操作的文件 File file = new File("hello.txt"); //2、提供具体的流 FileReader fr = new FileReader(file); //3、数据的读入 //redd():返回读入的一个字符。如果到达文件末尾。范湖-1 //方式一 int date = fr.read(); while (date != -1) { System.out.print((char)date); date = fr.read(); } //4、流的关闭操作 fr.close(); }
方式二:语法上针对方式一的修改
@Test public void FileReaderTest(){ FileReader fr = null; //1、实例化File类的对象,指明要操作的文件 File file = new File("hello.txt"); //2、提供具体的流 try { fr = new FileReader(file); //3、数据的读入 //redd():返回读入的一个字符。如果到达文件末尾。范湖-1 //方式一 int date; while ((date = fr.read())!= -1) { System.out.print((char)date); } } catch (IOException e) { e.printStackTrace(); } finally { //4、流的关闭操作 if (fr!=null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
FileRead中使用read(char[] cbuf)读入数据:
@Test public void test2(){ FileReader fr = null; File file = new File("hello.txt"); try { fr = new FileReader(file); char[] cbuf = new char[5]; int len; while ((len = fr.read(cbuf))!=-1){ //方式一: //错误的写法 // for (int i = 0;i < cbuf.length;i++){ // System.out.print(cbuf[i]); // } //正确的写法 // for (int i = 0;i < len;i++){ // System.out.print(cbuf[i]); // } //方式二: //错误的写法,对应着方式一的错误写法 // String str = new String(cbuf); // System.out.print(str); String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
FileWriter写出数据的操作
写入文件的步骤
1、实例化File类的对象,指明写出到的文件
File file = new File("hi.txt");
2、提供FileWriter的对象,用于数据的写出
FileWriter fw = new FileWriter(file);
3、写出的操作
fw.write("醉里挑灯看剑\n");
fw.write("梦回吹角连营\n");
fw.write("八百里分麾下炙\n");
fw.write("五十弦翻塞外声\n");
fw.write("沙场秋点兵\n");
4、流资源的关闭
fw.close();
写出数据操作
@Test public void test(){ FileWriter fw = null; try { //1、提供File类的对象,指明写出到的文件 File file = new File("hi.txt"); //2、提供FileWriter流的对象,用于数据的写出 fw = new FileWriter(file); //3、写出的操作 fw.write("醉里挑灯看剑\n"); fw.write("梦回吹角连营\n"); fw.write("八百里分麾下炙\n"); fw.write("五十弦翻塞外声\n"); fw.write("沙场秋点兵\n"); } catch (IOException e) { e.printStackTrace(); } finally { try { //4、关闭流 if (fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
说明:
1、输出操作,对应的File可以不存在的。并不会报异常
2、File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
File对应的硬盘中的文件如果存在:
如果流使用的构造器是: FileWriter(file,false) / FileWriter(file):对原有
如果流使用的构造器是: FileWriter(file, true):不会对原有文件覆盖,而是在原有文件基础上追加内容
使用FileReader和FileWriter实现文本文件的复制
@Test public void test(){ FileReader fr = null; FileWriter fw = null; try { //1、创建File类的对象,指明读入和写出的文件 File file1 = new File("hello.txt"); File file2 = new File("hi.txt"); //2、创建输入流和输出流的对象 fr = new FileReader(file1); fw = new FileWriter(file2); //3、数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的祝福的个数 while ((len = fr.read(cbuf)) != -1){ //每次写入len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (fr!=null) fr.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fw!=null) fw.close(); } catch (IOException e) { e.printStackTrace(); } } }
字节流:
使用FileInputStream不能读取文本文件的测试
@Test public void test(){ FileReader fr = null; FileWriter fw = null; try { //1、创建File类的对象,指明读入和写出的文件 File file1 = new File("JXBZ-139.jpg"); File file2 = new File("JXBZ-140.jpg"); //2、创建输入流和输出流的对象 fr = new FileReader(file1); fw = new FileWriter(file2); //3、数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的祝福的个数 while ((len = fr.read(cbuf)) != -1){ //每次写入len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (fr!=null) fr.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fw!=null) fw.close(); } catch (IOException e) { e.printStackTrace(); } } }
原图:

效果:

使用FileInputStream和FileOutputStream读写非文本文件
@Test public void testFileInputStream(){ FileInputStream fis = null; try { File file = new File("hello.txt"); fis = new FileInputStream(file); byte[] buffer = new byte[5]; int len; while ((len = fis.read(buffer))!=-1){ System.out.print(new String(buffer,0,len)); } } catch (IOException e) { e.printStackTrace(); } finally { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
结果:
原因:UTF-8中一个中文占三个字节的位置
结论:
1、对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
2、对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理
使用FileInputStream和FileOutputStream读写非文本文件
@Test public void testFileInputOutputStream(){ FileInputStream fis = null; FileOutputStream fos = null; try { //1、实例化File类指定要读取和写入的文件 File srcFile = new File("JXBZ-139.jpg"); File destFile = new File("JXBZ-140.jpg"); //2、创建FileInputStream流和FileOutputStream流 fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //3、对数据进行读写 byte[] bytes = new byte[1024]; int len; while ((len = fis.read(bytes))!=-1){ fos.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4、关闭流 try { if (fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fos != null) fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
结果:
缓冲流
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
- BufferedInputStream和BufferedOutputStream
- BufferedReader和BufferedWriter
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
flush()方法的使用:手动将buffer中内容写入文件
如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。

缓冲流(字节型)实现非文本文件的复制
@Test public void test(){ File file1; File file2; FileInputStream fis; FileOutputStream fos; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1、造文件 file1 = new File("D:\\Test\\最终效果.mov"); file2 = new File("声之形.mov"); //2、造流 //2.1造节点流 fis = new FileInputStream(file1); fos = new FileOutputStream(file2); //2.2造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3、复制的细节,读取,写入 byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1){ bos.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4、资源关闭 //要求:先关闭外层的流,再关闭内层的流 try { if (bis!=null) bis.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bos!=null) bos.close(); } catch (IOException e) { e.printStackTrace(); } } }
缓冲流与字节流读写速度的对比:
缓冲流复制文件:
@Test public void test(){ long start = System.currentTimeMillis(); File file1; File file2; FileInputStream fis; FileOutputStream fos; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1、造文件 file1 = new File("D:\\Test\\最终效果.mov"); file2 = new File("声之形.mov"); //2、造流 //2.1造节点流 fis = new FileInputStream(file1); fos = new FileOutputStream(file2); //2.2造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3、复制的细节,读取,写入 byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1){ bos.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4、资源关闭 //要求:先关闭外层的流,再关闭内层的流 try { if (bis!=null) bis.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bos!=null) bos.close(); } catch (IOException e) { e.printStackTrace(); } } long end = System.currentTimeMillis(); System.out.println("花费时间为"+(end-start)+"毫秒"); }
节点流复制文件:
@Test public void test1(){ long start = System.currentTimeMillis(); File file1; File file2; FileInputStream fis = null; FileOutputStream fos = null; try { //1、造文件 file1 = new File("D:\\Test\\最终效果.mov"); file2 = new File("声之形.mov"); //2、造流 //2.1造节点流 fis = new FileInputStream(file1); fos = new FileOutputStream(file2); //3、复制的细节,读取,写入 byte[] bytes = new byte[1024]; int len; while ((len = fis.read(bytes)) != -1){ fos.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4、资源关闭 //要求:先关闭外层的流,再关闭内层的流 try { if (fis!=null) fis.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fos!=null) fos.close(); } catch (IOException e) { e.printStackTrace(); } } long end = System.currentTimeMillis(); System.out.println("花费时间为"+(end-start)+"毫秒"); }
缓冲流的复制用时:

字节流复制用时:

缓冲流(字符流)实现文本文件的复制
@Test public void test(){ BufferedReader br = null; BufferedWriter bw = null; try { //创建文件对应的流 br = new BufferedReader(new FileReader(new File("hello.txt"))); bw = new BufferedWriter(new FileWriter(new File("hi.txt"))); //读写操作 //方式一: //每次读入至多指定数组的大小 // char[] cbuf = new char[1024]; // int len; // while ((len = br.read(cbuf)) != -1){ // bw.write(cbuf,0,len); // } //方式二: //使用String //每次读入一行 String data; while ((data = br.readLine())!=null){ //方法一: // bw.write(data+"\n");//data中不包含换行符 //方法二: bw.write(data); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (br != null) br.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bw != null) bw.close(); } catch (IOException e) { e.printStackTrace(); } } }
转换流
- 转换流提供了在字节流和字符流之间的转换
- Java API提供了两个转换流:
- InputStreamReader:将InputStream转换为Reader
- 实现将字节的输入流按指定字符集转换为字符的输入流。
- 需要和InputStream“套接”。
- 构造器
- public InputStreamReader(InputStreamin)
- public InputSreamReader(InputStreamin,StringcharsetName)
- 如:Reader isr= new InputStreamReader(System.in,”gbk”);
- OutputStreamWriter:将Writer转换为OutputStream
- 实现将字符的输出流按指定字符集转换为字节的输出流。
- 需要和OutputStream“套接”。
- 构造器
- public OutputStreamWriter(OutputStreamout)
- public OutputSreamWriter(OutputStreamout,StringcharsetName)
- Java API提供了两个转换流:
- 字节流中的数据都是字符时,转成字符流操作更高效。
- 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
转换流



浙公网安备 33010602011771号