java IO流总结篇(二)

/**
*@author StormMaybin
*Date 2016-07-29
*/

总有人要赢的,那个人为什么不能是我?


继续昨天的IO流总结,今天是第二篇,在上一篇说会分为两篇来总结IO,可是今天看了看,貌似还有一些知识点没复习到,所以会增加一篇,另外以后还会不定时的分享一些其他知识点总结,温故而知新嘛,哈哈,废话不多说了,开始吧!


上一篇是从字符流开始的,这一篇我们来看看字节流,转换流和一些小的知识点,以及字节流的运用(复制MP3文件或者图片)!

基本概念

和字符流一样,字节流也有两个抽象类作为其他类的父类,一个是输出字节流InputStream,另外一个就是OutputStream。其他类都是这两个类的拓展!
这里写图片描述

  • InputStream
    • InputStream 是所有的输入字节流的父类,它是一个抽象类。
    • ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
    • ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
  • OutputStream
    • OutputStream 是所有的输出字节流的父类,它是一个抽象类。
    • yteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据。
    • ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
  • 字节输入流和输出流的对应
    java IO流总结篇(二)_第1张图片
    • 图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出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是怎么解释的

java IO流总结篇(二)_第2张图片

这样说起来很苍白,我们还是来用代码演示一下!


演示代码之前,先看一个小技巧

/**
 * 流操作的基本规律:
 * 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();
                }
            }
        }
    }
}

运行结果:
java IO流总结篇(二)_第3张图片

现在问题又出来了,这怎么回事,打印结果里面怎么有乱码!其实很简单,源文件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();
                }
            }
        }
    }
}

java IO流总结篇(二)_第4张图片

现在上面那一行乱码中文注释解决了!


LineNumberReader

现在我们看一下LineNumberReader这个类!本来这个类应该是上一篇文章中的,但是由于疏忽,哈哈哈,我们先来看一下java API对LineNumbetReader的介绍吧
java IO流总结篇(二)_第5张图片
大致了解一下,下面我们自己来定义一个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();
    }
}

结果
java IO流总结篇(二)_第6张图片

好了,java IO总结篇(二)到这就结束了,期待第三篇,哈哈哈!

你可能感兴趣的:(________JavaSE,java,io流,字符流)