InputStream和字符流一样,字节流也有两个抽象类作为其他类的父类,一个是输出字节流InputStream,另外一个就是OutputStream。其他类都是这两个类的拓展!
InputStream 是所有的输入字节流的父类,它是一个抽象类。 ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。 ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。 OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类。 yteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据。 ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。 字节输入流和输出流的对应
图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出Java IO 中的字节流是极其对称的。“存在及合理”我们看看这些字节流中不太对称的几个类吧! LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部 分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行 号,看起来也是可以的。好像更不入流了。 PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。 StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。 SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。 PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!
字节流的概念就先看到这,下面我们先用一个小案例来引出今天的知识点(复制图片文件)
package com.stormma.FileStream; import java.io.*; /*** * 复制 一个图片 * 思路: * 1、字节读取流对象和图片文件进行关联 * 2、用字节写入流创建一个图片文件,用于存储获取到的图片数据 * 3、通过循环读写,完成数据的存储 * 4、关闭资源 * @author StormMaybin * */ public class CopyPic { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub FileInputStream fis = null; FileOutputStream fos = null; try { fos = new FileOutputStream ("F:\\2.jpg"); fis = new FileInputStream ("F:\\1.jpg"); byte [] buf = new byte [1024]; int len = 0; while ((len = fis.read(buf)) != -1) { fos.write(buf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
复制结果:
可以看到我们成功的复制了1.png这个图片资源!
package com.stormma.FileStream; import java.io.*; /*** * 需求: * 自定义字节流的缓冲 * @author StormMaybin * */ public class MyBufferedInputStream { private InputStream in; private byte [] buf = new byte [1024]; private int pos = 0; private int count = 0; public MyBufferedInputStream (InputStream in) { this.in = in; } public int myRead() { //通过in对象读取硬盘中的数据,存储到buf中去 //如果是count=0,那么说明byte数组是空的,所以开始读取数据 if (count == 0) { try { count = in.read(buf); //标记位 pos = 0; } catch (Exception e) { e.printStackTrace(); } //通过下标找到数据 byte b = buf [pos]; //byte数组的个数递减 count --; //移动标记位 pos ++; /*** * 至于这个为什么是返回b&255,原因很简单就是 * 一个字节是8位,比如00000000 00000000 00000000 11111111 * 这个转换成int类型是-1 ,这样会导致复制意外结束 */ return b&255; } //如果byte数组中还有数据继续往出取! else if (count > 0) { byte b = buf [pos]; count --; pos ++; return b&255; } return -1; } //关闭流方法 public void myClose() { try { in.close(); } catch (Exception e) { e.printStackTrace(); } } }
这样,就完成了输入字节流的自定义!
转换流是连接字节流和字符流的桥梁。 可对读取到的字节数据经过指定编码转换成字符。 可对读取到的字符数据经过指定编码转换成字节。
何时使用转换流?
当字节和字符之间有转换动作时。 流操作的数据需要编码或解码时。具体的对象体现:
InputStreamReader:字节到字符的桥梁。 OutputStreamWriter:字符到字节的桥梁
我们来看看java API是怎么解释的
这样说起来很苍白,我们还是来用代码演示一下!
演示代码之前,先看一个小技巧
/** * 流操作的基本规律: * 1、明确源和目的 * --源:输入流:InputStream,Reader * --目的:输出流:OutputStream,Writer * 2、操作的数据是否是纯文本 * --是纯文本:字符流 * --不是纯文本:字节流 * 3、当体系明确之后,然后明确用哪个具体的对象 * 通过设备来区分 * 源设备:内存,磁盘,键盘 * 目的设备:内存,硬盘,控制台 */
现在我们来实现一个功能,把一个文件的内容输出到控制台上!
package com.stormma.FileStream; import java.io.*; /*** 1. 源:文件 2. 目的:控制台 3. 需求:把文件内容输出到控制台上 4. @author StormMaybin 5. 分析: 6. 因为源是文本文件,那么选择使用Reader 7. 目的是控制台,对应的对象是System.in,System.in操作字节流 8. 但是为了操作更见方便,这样用转换流把字节流转换成字符流 9. InputStreamReader isr = new InputStreamReader(System.in); 10. 然后看看是否要提高效率,如皋需要的话,那么就选择使用缓冲区 11. BufferedReader bufr = new BufferedReader(isr); 12. 合并为一句就是BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 13. *********** 14. 这里如果需要编码转换的话 15. 可以用转换流进行编码格式转化 16. 参数(String encoding) */ public class FileStreamDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //创建读取和写入对象 BufferedReader bufr = null; BufferedWriter bufw = null; try { /*** * FileInputStream fis = new FileInputStream ("copy_bufwriter.txt"); * InputStreamReader isr = new InputStreamReader(fis); * BufferdeReader bufr = new BufferedReader(isr); * */ bufr = new BufferedReader(new InputStreamReader(new FileInputStream("copy_bufwriter.txt"))); bufw = new BufferedWriter(new OutputStreamWriter(System.out)); } catch (FileNotFoundException e) { System.out.println("找不到文件了"); } String lines = null; try { while ((lines = bufr.readLine()) != null) { bufw.write(lines); bufw.newLine(); bufw.flush(); } } catch(IOException e) { e.printStackTrace(); } finally { if (bufr != null) { try { bufr.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufw != null) { try { bufw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
现在问题又出来了,这怎么回事,打印结果里面怎么有乱码!其实很简单,源文件copy_bufwriter.txt这个的编码格式是utf-8,而在上面的代码中默认是gbk解码和编码的!编码:字符–>字节,解码:字节–>字符,既然源文件的编码格式是utf-8,那我们应该也用utf-8来编码和解码,不然肯定会产生乱码的!所以我们来修改一下源文件来解决这个问题!
package com.stormma.FileStream; import java.io.*; public class FileStreamDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub BufferedReader bufr = null; BufferedWriter bufw = null; try { /*** * FileInputStream fis = new FileInputStream ("copy_bufwriter.txt"); * InputStreamReader isr = new InputStreamReader(fis); * BufferdeReader bufr = new BufferedReader(isr); * */ bufr = new BufferedReader(new InputStreamReader(new FileInputStream("copy_bufwriter.txt"),"utf-8")); bufw = new BufferedWriter(new OutputStreamWriter(System.out,"utf-8")); } catch (FileNotFoundException e) { System.out.println("找不到文件了"); } catch (UnsupportedEncodingException e) { System.out.println("不支持这种编码格式"); } String lines = null; try { while ((lines = bufr.readLine()) != null) { bufw.write(lines); bufw.newLine(); bufw.flush(); } } catch(IOException e) { e.printStackTrace(); } finally { if (bufr != null) { try { bufr.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufw != null) { try { bufw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
现在上面那一行乱码中文注释解决了!
现在我们看一下LineNumberReader这个类!本来这个类应该是上一篇文章中的,但是由于疏忽,哈哈哈,我们先来看一下java API对LineNumbetReader的介绍吧
大致了解一下,下面我们自己来定义一个MyLineNumberReader类来实现LineNumberReader类的功能。
package com.stormma.FileReaderWriter; import java.io.FileReader; import java.io.IOException; import java.io.Reader; public class MyLineNumberReader { private Reader r; private int lineNumber = 0; /*** * @author StormMaybin * @param r */ public MyLineNumberReader(Reader r) { this.r = r; } public String myReadLine()throws IOException { //标记行号 lineNumber++; StringBuilder sb = new StringBuilder(); int ch = 0; //因为这里的read方法有可能会抛出异常 //所以这个函数申明可抛出异常 while ((ch = r.read()) != -1) { if (ch == '\r') { continue; } else if (ch == '\n') { return sb.toString(); } else { sb.append((char)ch ); } } //避免丢失最后一行 if (sb.length() != 0) { return sb.toString(); } return null; } /*** * myClose方法 * 实现关闭流功能 * @throws IOException */ public void myClose()throws IOException { r.close(); } public int getLineNumber() { return lineNumber; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } } /*** * 演示类 * @author StormMaybin * */ class MyLineNumberReaderDemo { public static void main (String [] args)throws IOException { FileReader fr = new FileReader("copy_bufwriter.txt"); MyLineNumberReader mbufr = new MyLineNumberReader(fr); mbufr.setLineNumber(100); String lines = null; while ((lines = mbufr.myReadLine()) != null) { System.out.println(mbufr.getLineNumber()+lines); } mbufr.myClose(); } }
结果