本文编写于 802 天前,最后修改于 802 天前,其中某些信息可能已经过时。
一. IO流
定义
- IO流用来处理设备之间的数据传输
- Java对数据的操作是通过流的方式
- Java用于操作流的类都在IO包中
- 流按流向分为两种:输入流,输出流。I/O
流按操作类型分为两种:
- 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类
字节流的抽象父类:
- InputStream
- OutputStream
字符流的抽象父类:
- Reader
- Writer
- IO流图解

使用步骤
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
二. 字节流之FileInputStream
定义
- 以字节的形式来读取文件内容
- InputStream抽象类的子类, 已经重写了read()方法
构造方法
- FileInputStream(String name) : 根据字符串地址来连接到一个本地文件
- FileInputStream(File file) : 根据一个File类对象连接到一个本地文件
常用方法
- read() : 从此输入流中读取一个数据字节
- read(byte[] b) : 从此输入流中将最多b.length个字节的数据读入一个byte数组中
- read(byte[] b,int off,int len) : 从此输入流中读取最多 len个字节的数据读入到一个byte[] 数组中
- close() : 关闭此输入流并释放与此流有关的所有系统资源
演示
public static void main(String[] args) throws Exception { //创建输入流连接 FileInputStream inputStream = new FileInputStream("d:\\xidada.jpg"); //定义接收数据的变量 int i = 0; //从输入流中读取一个字节(如果返回-1,说明已经到了文件的末尾) while ((i = inputStream.read())!=-1) { System.out.println(i); } //关闭流 inputStream.close(); }
思考题
- read()方法读取的是一个字节,为什么返回是int,而不是byte ?
- 因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111,那么11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型
三. 字节流之FileOuputStream
定义
- 以字节的形式将数据写入到文件中
- OutputStream抽象类的子类, 已经重写了write()方法
构造方法
- FileOuputStream(String name) : 根据字符串地址来连接到一个本地文件
- FileOuputStream(File file) : 根据一个File类对象连接到一个本地文件
- FileOuputStream(File file,boolean append) : 在文件末尾继续写入
常用方法
- write(int b) : 将指定字节写入此文件输出流
- write(byte[] b) : 将b.length个字节从指定byte数组写入此文件输出流中
- write(byte[] b,int off, int len) : 将指定byte数组中从偏移量off开始的len个字节写入此文件输出量
- close() : 关闭输出流并释放与此流有关的所有系统资源
演示
public static void main(String[] args) throws Exception { FileOutputStream fileOutputStream = new FileOutputStream("d:\\ddd.txt"); fileOutputStream.write(97); fileOutputStream.close(); }
尾部追加
- FileOutputStream为我们提供了尾部添加的形式, 我们利用FileOuputStream(File file,boolean append)构造方法创建的对象,就可以将流中的内容追加到文件的尾部
- 在将多个文件合并成一个的时候可以使用
演示
public static void main(String[] args) throws Exception { FileOutputStream fos = new FileOutputStream("d:\\合成文件.txt",true); FileInputStream fis1 = new FileInputStream("d:\\碎片1.txt"); FileInputStream fis2 = new FileInputStream("d:\\碎片2.txt"); FileInputStream fis3 = new FileInputStream("d:\\碎片3.txt"); int i ; while ((i=fis1.read())!=-1) { fos.write(i); } while ((i=fis2.read())!=-1) { fos.write(i); } while ((i=fis3.read())!=-1) { fos.write(i); } fis1.close(); fis2.close(); fis3.close(); fos.close(); }
四. 读写练习
复制图片
- 需求: 将一张图片复制到其他地方
- 解析: 使用FileInputStream读取 , 使用FileOutputStream写入
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\xidada.jpg"); FileOutputStream fos = new FileOutputStream("d:\\副本.jpg"); int i = 0; while ((i=fis.read())!=-1) { fos.write(i); } fis.close(); fos.close(); }
复制歌曲
- 需求: 将一首歌曲复制到其他地方
- 解析: 使用FileInputStream读取 , 使用FileOutputStream写入
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); FileOutputStream fos = new FileOutputStream("d:\\音乐副本.mp3"); int i = 0; while ((i=fis.read())!=-1) { fos.write(i); } fis.close(); fos.close(); }
- 分析: 速度太慢
五. 小数组读写
定义
- 每次读取一个字节的速度实在是太慢了,实际开发中根本无法使用
- 所以,java为我们提供了一次获取多个字节的方法, read(byte[] b), 允许一次获取一个数组长度的字节
available()方法
- 这个方法可以帮我们获取资源中剩余的长度
所有我们可以创建一个和资源文件大小相等长度小数组来装载数据
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); FileOutputStream fos = new FileOutputStream("d:\\音乐副本.mp3"); //创建一个和资源文件大小相同的字节数组 byte[] bs = new byte[fis.available()]; fis.read(bs); fos.write(bs); fis.close(); fos.close(); }
- 如果资源文件过大的话,很容易造成内存溢出
- 获取长度的速度极慢, 不可取
标准写法
- 定义的小数组的长度不宜过长也不宜过短
- 多长容易造成内存溢出, 过短效率低下
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); FileOutputStream fos = new FileOutputStream("d:\\音乐副本.mp3"); //创建一个和资源文件大小相同的字节数组 byte[] bs = new byte[1024*8]; int len = 0; while((len=fis.read(bs))!= -1){ //最后一次读取的时候可能并不能装满整个数组 fos.write(bs, 0, len); } fis.close(); fos.close(); }
测试题
- 需求: 从键盘录入文字, 保存到本地文件中, 直到录入quit时就退出
- 解析: 字符串要转换成字节数组
六. 图片加密
- 对图片的每一个字节进行加密
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\图片副本.jpg"); FileOutputStream fos = new FileOutputStream("d:\\图片副本2.jpg"); int i = 0; while((i=fis.read())!= -1){ //对每一个字节进行加密 fos.write(i^123); } fis.close(); fos.close(); }
七. 字节输入缓冲流(BufferedInputStream)
定义
- 我们都知道单个字节的读取是效率非常低下的, 所以我们使用了小数组的方法进行流的读取和写入
- java中已经帮我们实现了, 这就是缓冲区的概念, 也是一次性读取一个数组,然后将这个数组放入内存中供我们使用
- 缓冲流只是一个壳,实际还是字节流在工作
构造方法
BufferedInputStream(InputStream in)
- 创建一个BufferedInputStream 并保存其参数, 即输入in ,一遍将来使用
BuffferedInputStream(InputStream in , int size)
- 创建一个具有指定缓冲区大小的BufferedInputStream并保存其参数, 即输入流in,以便将来使用
常用方法
- read() : 一次读取一个字节
- read(byte[] b, int off, int len) : 从此字节输入流中给定偏移量处开始各字节读取到指定的byte数组中
- close() : 关闭并释放资源,实际关闭的是内部真正工作的字节流
注意事项
- BufferedInputStream内部建立一个缓冲区域,在读取文件的时候, 一次性读取大量的字节, 缓冲到缓冲区当中, 然后再反给我们, 看着没有变化, 区别在于一个直接操作硬盘, 一个是操作内存, 效率不可相提并论
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\图片副本.jpg"); BufferedInputStream bis = new BufferedInputStream(fis); int i ; while((i=bis.read())!=-1){ System.out.println(i); } //关闭缓冲流就会关闭字节流 bis.close(); }
八. 字节输出缓冲流(BufferedOutputStream)
定义
- 单个字节的写入是效率及其低下的, 所以我们使用了小数组的形式进行写入, 一次写入一个数组的数据
- java中给我们提供了输出缓冲区流,将我们写的字节先存放到一个小数组中,等数组满了之后再写入到本地去
- 在流关闭之前, 会有一次写入动作, 这样就避免了最后一次读取时, 缓冲区没有满不能写入的问题
构造方法
BufferedOutputStream(OutputStram out)
- 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
BufferedOutputStream(OutputStream out, int size)
- 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
常用方法
- write(int b) : 将指定的字节写入此缓冲流的输出流
- write(byte[] b, int off, int len) : 将指定byte数组中从偏移量off开始的len个字节吸入此缓冲流的输入流
- flush() : 刷新此缓冲流的输出流
- close() : 关闭流,并释放资源
演示
public static void main(String[] args) throws Exception { FileOutputStream fos = new FileOutputStream("d:\\ddd.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); for (int i = 0; i < 10000; i++) { bos.write(97); } bos.close(); }
注意事项
- 缓冲流就一个壳, 他不能实际和本地文件建立连接, 本质还是通过InputStream和OutputStream来工作
九. 使用字节缓冲流来实现文件的复制
演示
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream("d:\\音乐副本.mp3"); BufferedOutputStream bos = new BufferedOutputStream(fos); int i ; while((i=bis.read())!= -1){ bos.write(i); } bis.close(); bos.close(); }
测试题
- 需求: 将一个文件夹复制到另一个地方
演示
public static void copy(String srcname,String target) throws Exception{ File srcFile = new File(srcname); File targetFile = new File(new File(target),srcFile.getName()); targetFile.mkdir(); File[] files = srcFile.listFiles(); if(files!=null){ for (File file : files) { if (file.isFile()) { FileInputStream fis = new FileInputStream(file); //targetFile+文件名 File file2 = new File(targetFile,file.getName()); FileOutputStream fos = new FileOutputStream(file2); byte[] bs = new byte[1024*8]; int len ; while((len=fis.read(bs))!=-1){ fos.write(bs,0,len); } fis.close(); fos.close(); }else{ copy(file.getAbsolutePath(),targetFile.getAbsolutePath()); } } } }
小数组和缓冲流的效率问题
- 定义小数组如果是8192个字节大小和Buffered比较的话,小数组块
- 定义小数组会略胜一筹,因为读和写操作的是同一个数组
- 而Buffered操作的是两个数组
十. 流的异常处理问题
jdk1.6及以前的版本处理方式
public static void main(String[] args) { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); fos = new FileOutputStream("d:\\音乐副本.mp3"); int i ; while((i=fis.read())!= -1){ fos.write(i); } } catch (Exception e) { }finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
jdk1.7的处理方式
- 在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉
public static void main(String[] args) { try( FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3"); FileOutputStream fos = new FileOutputStream("d:\\音乐副本.mp3"); ){ int i ; while((i=fis.read())!= -1){ fos.write(i); } } catch (IOException e) { e.printStackTrace(); } }