javaIO--数据流之IO流与字节流

0、IO流

0.1、IO(Input Output)流的概念

Java中将不同设备之间的数据传输抽象为“流”:Stream
设备指的是:磁盘上的文件,网络连接,另一个主机等等

按流向分:输入流,输出流:都是针对内存来说的

  1. 输入流,只能从其中读取数据
  2. 输出流,只能把数据放入其中

按每次处理的数据单位分:字节流,字符流
1.字节流:每次处理一个字节
2.字符流:每次处理一个字符

0.2、IO流抽象基类

通常流的分类,如果没有特定指出,都是按操作数据单位来说的

字节流:两个方向
InputStream:输入字节流
OutputStream:输出字节流

字节流继承图:

字符流:两个方向
Reader:输入字符流
Writer:输出字符流

字符流继承图:

1、字节流

1.1字节输出流抽象基类:OutputStream

1.1.1 FileOutputStream实现类

构造方法:(输出流会自动创建输出对象)
FileOutputStream(File file)
  通过一个File对象创建一个文件输出流对象
FileOutputStream(String name)
  通过一个字符串构建一个文件输出流对象
FileOutputStream(File file,boolean append)
  通过文件对象创建文件输出流对象,并指定是否追加
FileOutputStream(String name,boolean append)
  通过一个字符串构建一个文件输出流对象,并指定是否追加

note:如果没有实际对象,对对象的操作并不会作用于实际文件

写出数据到流对象:
void write(byte[] bytes)
  一次写一个字节数组的数据
void write(int b)
  一次写一个字节
void write(byte[] bytes,int off,int len)
  一次写一个字节数组的一部分

关闭流对象:
void close()
  关闭此文件输出流并释放与此流有关的所有系统资源

关闭流的两个作用:
1.让流不能再继续使用
2.释放和此流相关的系统资源

1.1.2 FileInputStream实现类

构造方法:(必须要求输入对象事先存在)
FileInputStream(File file)
  通过File对象创建一个文件输入流对象 FileInputStream(String name)
  通过一个字符串创建一个文件输入流对象 String可以是完整路径字符串
int read()
  从输入流中读取一个字节
int read(byte[] b)
  从输入流中读取字节,放到字节数组中,返回字节个数
int read(byte[] b,int off,int len)
  从输入流中读取字节,放到字节数组中,只放到指定位置(不常用)

1.1.3自带关闭资源的try语句块

为了替换finally臃肿的写法,JDK7出现了自带关闭资源的try语句块,可以不用显式的写finally语句。
用法:
将需要关闭的资源定义或者初始化语句放到try后的括号里,程序会自动关闭这些资源,前提是这些类实现了AutoCloseable接口或者Closeable接口
格式:

try(需要自动关闭的资源声明或者初始化){
    //正常逻辑语句
}

案例:字节读取

1.一次读取一个字节
2.用循环改进读取字节
3.简化循环读取字节
4.一次读取一个字节数组
5.加上异常处理读取字节数组
6.改进异常处理读取字节数组
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/*
 * FileInputStream(File name)
 * FileInputStream(String name)
 */
public class FileInputStreamDemo {

    public static void main(String[] args) throws Exception {
        //输入流关联的文件必须事先存在,否则异常,输出流目标文件可以自动创建
        FileInputStream fis = new FileInputStream(new File("a.txt"));
        
//        FileInputStream fis2 = new FileInputStream("a.txt");
        
        //一次读取一个字节:
//        int b = fis.read();
//        System.out.println(b);
        /*
        b = fis.read();
        System.out.println(b);
        b = fis.read();
        System.out.println(b);
        b = fis.read();
        System.out.println(b);
        b = fis.read();
        System.out.println(b);
        */
        //用循环改进
        /*
        int b = 0;
        while(true){
            b = fis.read();
            if(b == -1){
                break;
            }else{
                System.out.println(b);
            }
        }
        */
        //最终代码
//        int b = 0;
//        while((b = fis.read()) != -1){
//            System.out.println(b);
//        }
        
        //一次读取一个字节数组
        /*
        byte[] bys = new byte[1024];
        int len = fis.read(bys);
        System.out.println(len);
        
        //用字节数组构建字符串
        String str = new String(bys, 0, len);
        System.out.println(str);
        */
        
        //循环读取数据
        int len = 0;
        byte[] bys = new byte[1024];
        while((len = fis.read(bys) + test()) != -1){
            System.out.print(new String(bys,0,len));
        }
        
        //释放资源
        fis.close();

    }

    public static int test(){
        System.out.println("方法被调用了");
        return 0;
    }
    
}
字节读取
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
 * 加上异常处理的程序
 * 
 * 1.7后新特性:自带关闭资源的try块
 * 
 */
public class FileInputStreamDemo2 {

    public static void main(String[] args) {
        /*
        //提升作用域
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("a.txt");//此条语句有可能不执行成功
            
            //
            int len = 0;
            byte[] bys = new byte[1024];
            while((len = fis.read(bys)) != -1){
                System.out.print(new String(bys,0,len));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            //用来释放非内存资源
            //释放资源
            //非空判断
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            //如果有多个变量,继续关闭
        }

*/
        
        //自动关闭资源的try块
        try(
                FileInputStream fis = new FileInputStream("a.txt");
                //
                ){
            //正常的逻辑语句
            int len = 0;
            byte[] bys = new byte[1024];
            while((len = fis.read(bys)) != -1){
                System.out.print(new String(bys,0,len));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }

}
异常处理改进

案例:写出数据到流

import java.io.File;
import java.io.FileOutputStream;

/*
 * FileOutputStream(File name)
 * FileOutputStream(File name,boolean append)
 * FileOutputStream(String name)
 * FileOutputStream(String name,boolean append)
 * 
 *    创建对象 --> 使用写出方法写出数据到流 --> 释放资源
 *
 *    写入到文件中的数据.如果使用记事本等程序打开,会经过转码的过程. ( 字节-->字符 )
 */
public class FileOutputStreamDemo1 {

    public static void main(String[] args) throws Exception {
        //
        FileOutputStream fos = new FileOutputStream(new File("a.txt"),true);
        /*
        //
        FileOutputStream fos2 = new FileOutputStream(new File("a.txt"), true);
        
        //
        FileOutputStream fos3 = new FileOutputStream("a.txt");
        
        //
        FileOutputStream fos4 = new FileOutputStream("a.txt",true);
*/
        //一次写一个字节
//        fos.write(98);
        
        //一次写一个字节数组
//        byte[] bys = {97,98,99,100,101,102,103,104};        
//        fos.write(bys);
        
        //一次写一个字节数组的一部分
//        fos.write(bys, 2, 3);
        
        //实现换行
        fos.write("abc
def".getBytes());//使用系统默认的字符集编码成字节数组 :
        
        //释放资源
        fos.close();
        
        //流关闭之后不能再写数据
//        fos.write(97);
        
    }

}
写出数据到流

案例:文件的复制

思路:
1.使用源文件创建文件输入流对象
2.使用目标文件创建文件输出流对象
3.复制数据
4.关闭资源

实现同一个项目路径下的文本文件的复制
实现不同路径下的文本文件的复制
实现图片的复制
实现mp3的复制 

1.1.4 自带缓冲区的字节流

一次读取(或者写入)一个字节数组的数据在效率上提升了很多,字节数组实际上相当于一个缓冲区。
Java提供了自带缓冲区的流,不必自己再定义额外的字节数组充当缓冲区了。

自带缓冲区的字节输入流     vs      自带缓冲区的字节输出流
BufferedInputStream                   BufferedOutputStream

 

 

package java.io;

public class BufferedOutputStream extends FilterOutputStream {

    protected byte buf[];

    protected int count;

    public BufferedOutputStream(OutputStream out) {
        this(out, 8192);
    }

    public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /** Flush the internal buffer */
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }


    public synchronized void write(int b) throws IOException {
        if (count >= buf.length) {
            flushBuffer();
        }
        buf[count++] = (byte)b;
    }


    public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }


    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
}
BufferedOutputStream
package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;


public
class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;

    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

    protected volatile byte buf[];

    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");

    /**
     * The index one greater than the index of the last valid byte in the buffer.
     * 0<count<buf.length
     */
    protected int count;

    /**
     * The current position in the buffer. This is the index of the next character to be read from the buf[].
     * 0<pos<count.  buf[pos] is the next byte to be supplied as input;
     */
    protected int pos;

    /**
     * The value of the pos field at the time the last
     * mark method was called.
     * This value is always in the range -1 through pos.
     * If there is no marked position in  the input
     * stream, this field is -1. If
     * there is a marked position in the input
     * stream,  then buf[markpos]
     * is the first byte to be supplied as input
     * after a reset operation. If
     * markpos is not -1,
     * then all bytes from positions buf[markpos]
     * through  buf[pos-1] must remain
     * in the buffer array (though they may be
     * moved to  another place in the buffer array,
     * with suitable adjustments to the values
     * of count,  pos,
     * and markpos); they may not
     * be discarded unless and until the difference
     * between pos and markpos
     * exceeds marklimit.
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#pos
     */
    protected int markpos = -1;

    /**
     * The maximum read ahead allowed after a call to the
     * mark method before subsequent calls to the
     * reset method fail.
     * Whenever the difference between pos
     * and markpos exceeds marklimit,
     * then the  mark may be dropped by setting
     * markpos to -1.
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#reset()
     */
    protected int marklimit;

    /**
     * Check to make sure that underlying input stream has not been
     * nulled out due to close; if not return it;
     */
    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

    /**
     * Check to make sure that buffer has not been nulled out due to
     * close; if not return it;
     */
    private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

    /**
     * Creates a BufferedInputStream and saves its  argument, the input stream in, for later use. An internal buffer array is created and  stored in buf.
     * @param   in   the underlying input stream.
     */
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    /**
     * Creates a BufferedInputStream with the specified buffer size, and saves its  argument, the input stream in, for later use.  An internal  buffer array of length  size is created and stored in buf.
     * @param   in     the underlying input stream.
     * @param   size   the buffer size.
     * @exception IllegalArgumentException if {@code size <= 0}.
     */
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /**
     * Fills the buffer with more data, taking into account
     * shuffling and other tricks for dealing with marks.
     * Assumes that it is being called by a synchronized method.
     * This method also assumes that all data has already been read in,
     * hence pos > count.
     */
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }


    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }


    private int read1(byte[] b, int off, int len) throws IOException {
        int avail = count - pos;
        if (avail <= 0) {
            /* If the requested length is at least as large as the buffer, and
               if there is no mark/reset activity, do not bother to copy the
               bytes into the local buffer.  In this way buffered streams will
               cascade harmlessly. */
            if (len >= getBufIfOpen().length && markpos < 0) {
                return getInIfOpen().read(b, off, len);
            }
            fill();
            avail = count - pos;
            if (avail <= 0) return -1;
        }
        int cnt = (avail < len) ? avail : len;
        System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
        pos += cnt;
        return cnt;
    }


    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); // Check for closed stream
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)
                return (n == 0) ? nread : n;
            n += nread;
            if (n >= len)
                return n;
            // if not closed but no bytes available, return
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }


    public synchronized long skip(long n) throws IOException {
        getBufIfOpen(); // Check for closed stream
        if (n <= 0) {
            return 0;
        }
        long avail = count - pos;

        if (avail <= 0) {
            // If no mark position set then don't keep in buffer
            if (markpos <0)
                return getInIfOpen().skip(n);

            // Fill in buffer to save bytes for reset
            fill();
            avail = count - pos;
            if (avail <= 0)
                return 0;
        }

        long skipped = (avail < n) ? avail : n;
        pos += skipped;
        return skipped;
    }


    public synchronized int available() throws IOException {
        int n = count - pos;
        int avail = getInIfOpen().available();
        return n > (Integer.MAX_VALUE - avail)
                    ? Integer.MAX_VALUE
                    : n + avail;
    }


    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }


    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }


    public boolean markSupported() {
        return true;
    }


    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }
}
BufferedInputSream

1.1.5测试各种流在复制文件时的效率

FileInputStream
FileOutputStream
基本字节流(节点流):
一次读取一个字节;
一次读取一个字节数组的数据

BufferedInputStream
BufferedOutputStream
缓冲区流(包装流):
一次读取一个字节;
一次读取一个字节数组的数据

原文地址:https://www.cnblogs.com/wqbin/p/11176511.html