zoukankan      html  css  js  c++  java
  • Java IO流

    Java流类图结构:

    这里写图片描述

    流的概念和作用

    流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

    IO流的分类

    根据处理数据类型的不同分为:字符流和字节流
    根据数据流向不同分为:输入流和输出流

    字符流和字节流

    字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。

    字节流和字符流的区别:

    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
    • 字节流:一次读入或读出是8位二进制。
    • 字符流:一次读入或读出是16位二进制。

    设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

    结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    输入流和输出流

    输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。

    输入字节流 InputStream

    • InputStream 是所有的输入字节流的父类,它是一个抽象类。
    • ByteArrayInputStreamStringBufferInputStreamFileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
    • PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
    • ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

    输出字节流 OutputStream

    • OutputStream 是所有的输出字节流的父类,它是一个抽象类。
    • ByteArrayOutputStreamFileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
    • PipedOutputStream 是向与其它线程共用的管道中写入数据。
    • ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。

    总结:

    输入流:InputStream或者Reader:从文件中读到程序中;
    输出流:OutputStream或者Writer:从程序中输出到文件中;

    节点流

    节点流:直接与数据源相连,读入或读出。
    直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

    这里写图片描述

    常用的节点流

    • 父 类 :InputStreamOutputStreamReaderWriter
    • 文 件 :FileInputStreamFileOutputStreamFileReaderFileWriter 文件进行处理的节点流
    • 数 组 :ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)
    • 字符串 :StringReaderStringWriter 对字符串进行处理的节点流
    • 管 道 :PipedInputStreamPipedOutputStreamPipedReadePipedWriter 对管道进行处理的节点流

    处理流

    处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流

    如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

    这里写图片描述

    常用的处理流

    • 缓冲流:BufferedInputStreanBufferedOutputStreamBufferedReaderBufferedWriter 增加缓冲功能,避免频繁读写硬盘。
    • 转换流:InputStreamReader OutputStreamReader实现字节流和字符流之间的转换。
    • 数据流:DataInputStreamDataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。

    转换流

    InputStreamReader OutputStreamWriterInputStreamOutputStream作为参数,实现从字节流到字符流的转换。

    构造函数

    InputStreamReader(InputStream);        //通过构造函数初始化,使用的是本系统默认的编码表GBK。
    InputStreamWriter(InputStream,String charSet);   //通过该构造函数初始化,可以指定编码表。
    OutputStreamWriter(OutputStream);      //通过该构造函数初始化,使用的是本系统默认的编码表GBK。
    OutputStreamwriter(OutputStream,String charSet);   //通过该构造函数初始化,可以指定编码表。
    

    缓冲流-BufferedInputStream、BufferedOutputStream

    概念:

    有了InputStream为什么还要有BufferedInputStream?

    BufferedInputStream和BufferedOutputStream这两个类分别是FilterInputStream和FilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

    我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

    同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader和BufferedWriter两个类。

    BufferedInputStream和BufferedOutputStream类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。

    总结:

    • BufferedInputStream 是缓冲输入流。它继承于FilterInputStream。
    • BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持mark()标记和reset()重置方法。
    • BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

    注意点:

    那么什么时候flush()才有效呢?
    答案是:当OutputStream是BufferedOutputStream时。

    当写文件需要flush()的效果时,需要
    FileOutputStream fos = new FileOutputStream(“c:a.txt”);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    也就是说,需要将FileOutputStream作为BufferedOutputStream构造函数的参数传入,然后对BufferedOutputStream进行写入操作,才能利用缓冲及flush()。

    查看BufferedOutputStream的源代码,发现所谓的buffer其实就是一个byte[]。
    BufferedOutputStream的每一次write其实是将内容写入byte[],当buffer容量到达上限时,会触发真正的磁盘写入。
    而另一种触发磁盘写入的办法就是调用flush()了。

    BufferedOutputStream在close()时会自动flush

    BufferedOutputStream在不调用close()的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush.

    Code:

    package com.qhong.basic.ioStream;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * Created by qhong on 2019/2/1 16:51
     **/
    public class BufferedInputStreamTest {
    	public static void main(String[] args) throws IOException {
    
    		String filePath = "E:/abc.txt" ;
    		String filePath2 = "E:/def.txt" ;
    		File file = new File( filePath ) ;
    		File file2 = new File( filePath2 ) ;
    		copyFile( file , file2 );
    
    	}
    
    	/**
    	 * 复制文件
    	 * @param oldFile
    	 * @param newFile
    	 */
    	public static void copyFile( File oldFile , File newFile){
    		InputStream inputStream = null ;
    		BufferedInputStream bufferedInputStream = null ;
    
    		OutputStream outputStream = null ;
    		BufferedOutputStream bufferedOutputStream = null ;
    
    		try {
    			inputStream = new FileInputStream( oldFile ) ;
    			bufferedInputStream = new BufferedInputStream( inputStream ) ;
    
    			outputStream = new FileOutputStream( newFile ) ;
    			bufferedOutputStream = new BufferedOutputStream( outputStream ) ;
    
    			byte[] b=new byte[1024];   //代表一次最多读取1KB的内容
    
    			int length = 0 ; //代表实际读取的字节数
    			int i=1;
    			while( (length = bufferedInputStream.read( b ) )!= -1 ){
    				System.out.println("id:"+i+"   length:"+length);
    				//length 代表实际读取的字节数
    				bufferedOutputStream.write(b, 0, length );
    				i++;
    			}
    			//缓冲区的内容写入到文件
    			bufferedOutputStream.flush();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}catch (IOException e) {
    			e.printStackTrace();
    		}finally {
    			if( bufferedOutputStream != null ){
    				try {
    					bufferedOutputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if( bufferedInputStream != null){
    				try {
    					bufferedInputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if( inputStream != null ){
    				try {
    					inputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if ( outputStream != null ) {
    				try {
    					outputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    

    Output:

    id:1   length:1024
    id:2   length:1024
    //....省略
    id:33   length:1024
    id:34   length:1024
    id:35   length:1024
    id:36   length:1024
    id:37   length:521
    

    计算,1024*36+521=37385,正好一致

    如何正确的关闭流

    上面的代码时这样关闭流的

    if( bufferedOutputStream != null ){
    				try {
    					bufferedOutputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if( bufferedInputStream != null){
    				try {
    					bufferedInputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if( inputStream != null ){
    				try {
    					inputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    
    			if ( outputStream != null ) {
    				try {
    					outputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    

    在处理流关闭完成后,我们还需要关闭节点流吗?

    让我们带着问题去看源码:
    - bufferedOutputStream.close();

      /**
         * Closes this input stream and releases any system resources
         * associated with the stream.
         * Once the stream has been closed, further read(), available(), reset(),
         * or skip() invocations will throw an IOException.
         * Closing a previously closed stream has no effect.
         *
         * @exception  IOException  if an I/O error occurs.
         */
        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()
            }
        }
    

    close()方法的作用
    1、关闭输入流,并且释放系统资源
    2、BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。因此,可以只调用外层流的close方法关闭其装饰的内层流。

    那么如果我们想逐个关闭流,我们该怎么做?

    答案是:

    先关闭外层流,再关闭内层流。一般情况下是:先打开的后关闭,后打开的先关闭;

    另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b

    看懂了怎么正确的关闭流之后,那么我们就可以优化上面的代码了,只关闭外层的处理流。

    	if( bufferedOutputStream != null ){
    				try {
    					bufferedOutputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    			if( bufferedInputStream != null){
    				try {
    					bufferedInputStream.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    

    缓冲流-BufferedReader、BufferedWriter

    跟上面的BufferedInputStream,BufferedOutputStream差不多,一个字节,一个字符

    code:

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.Writer;
    
    /**
     * Created by qhong on 2019/2/1 17:24
     **/
    public class BufferedReaderTest {
    
    	public static void main(String[] args) {
    
    		String filePath = "E:/abc.txt" ;
    		String filePath2 = "E:/123.txt" ;
    
    		File file = new File( filePath ) ;
    		File file2 = new File( filePath2 ) ;
    		copyFile( file , file2 );
    	}
    
    	private static void copyFile( File oldFile , File newFile ){
    		Reader reader = null ;
    		BufferedReader bufferedReader = null ;
    
    		Writer writer = null ;
    		BufferedWriter bufferedWriter  = null ;
    		try {
    			reader = new FileReader( oldFile ) ;
    			bufferedReader = new BufferedReader( reader ) ;
    
    			writer = new FileWriter( newFile ) ;
    			bufferedWriter = new BufferedWriter( writer ) ;
    
    			String result = null ; //每次读取一行的内容
    			int i=1;
    			while (  (result = bufferedReader.readLine() ) != null ){
    				System.out.println("num:"+i+"   result:"+result.length());
    				bufferedWriter.write( result );  //把内容写入文件
    				bufferedWriter.newLine();  //换行,result 是一行数据,所以每写一行就要换行
    				i++;
    			}
    
    			bufferedWriter.flush();  //强制把数组内容写入文件
    
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}catch (IOException e) {
    			e.printStackTrace();
    		}finally {
    			try {
    				bufferedWriter.close();  //关闭输出流
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    
    			try {
    				bufferedReader.close();  //关闭输入流
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    Output:

    num:1   result:5
    num:2   result:4
    num:3   result:3
    num:4   result:2
    num:5   result:1
    //....省略 
    num:1023   result:9
    num:1024   result:0
    num:1025   result:2
    num:1026   result:2
    num:1027   result:3
    num:1028   result:16
    num:1029   result:16
    num:1030   result:0
    

    BufferedReader是对文档一行一行读取的

    转换流-InputStreamReader、OutputStreamWriter

    概念:

    • InputStreamReader 是字符流Reader的子类,是字节流通向字符流的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。一次只读一个字符。
    • OutputStreamWriter 是字符流Writer的子类,是字符流通向字节流的桥梁。每次调用 write()方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。一次只写一个字符。

    code

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    
    /**
     * Created by qhong on 2019/2/1 17:51
     **/
    public class InputStreamReaderTest {
    
    	public static void main(String[] args) {
    		String filePath = "E:/abc.txt";
    		String filePath2 = "E:/ghi.txt";
    		File file = new File(filePath);
    		File file2 = new File(filePath2);
    		copyFile(file, file2);
    	}
    
    	private static void copyFile(File oldFile, File newFile) {
    		InputStream inputStream = null;
    		InputStreamReader inputStreamReader = null;
    		OutputStream outputStream = null;
    		OutputStreamWriter outputStreamWriter = null;
    		try {
    			inputStream = new FileInputStream(oldFile); //创建输入流
    			inputStreamReader = new InputStreamReader(inputStream); //创建转换输入流
    			outputStream = new FileOutputStream(newFile); //创建输出流
    			outputStreamWriter = new OutputStreamWriter(outputStream); //创建转换输出流
    			int result = 0;
    			int i=1;
    			while ((result = inputStreamReader.read()) != -1) {  //一次只读一个字符
    				System.out.println("num:"+i +"  result:"+result);
    				outputStreamWriter.write(result); //一次只写一个字符
    				i++;
    			}
    			outputStreamWriter.flush();  //强制把缓冲写入文件
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			if (outputStreamWriter != null) {
    				try {
    					outputStreamWriter.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    			if (inputStreamReader != null) {
    				try {
    					inputStreamReader.close();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    

    output:

    num:1  result:97
    num:2  result:97
    num:3  result:97
    //... 省略
    num:24145  result:26512
    num:24146  result:23454
    num:24147  result:25112
    num:24148  result:13
    num:24149  result:10
    num:24150  result:13
    num:24151  result:10
    

    可以看出该abc.txt为24151个字符,上一个代码里面看它的字节数为37385

    ByteArrayInputStream、ByteArrayOutputStream

    ByteArrayInputStream 可以将字节数组转化为输入流 。
    ByteArrayOutputStream可以捕获内存缓冲区的数据,转换成字节数组。

    ByteArrayInputStream code:

    public class ByteArrayInputStreamTest {
    	public static void main(String[] args) {
    		String mes = "hello,world" ;
    		byte[] b = mes.getBytes() ;
    
    		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( b ) ;
    		int result = -1  ;
            int i=1;
    		while( ( result = byteArrayInputStream.read() ) != -1){
    			System.out.println("num:"+i +"   result:"+(char)result);
    			i++;
    		}
    
    		try {
    			byteArrayInputStream.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    Output:

    num:1   result:h
    num:2   result:e
    num:3   result:l
    num:4   result:l
    num:5   result:o
    num:6   result:,
    num:7   result:w
    num:8   result:o
    num:9   result:r
    num:10   result:l
    num:11   result:d
    

    ByteArrayOutputStream code:

    public class ByteArrayOutputStreamTest {
    
    	public static void main(String[] args) {
    		String mes = "你好,world" ;
    		byte[] b = mes.getBytes() ;
    
    		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream() ;
    		try {
    			byteArrayOutputStream.write( b );
    
    			FileOutputStream fileOutputStream = new FileOutputStream( new File( "E:/1234.txt" ) ) ;
    
    			byteArrayOutputStream.writeTo( fileOutputStream ) ;
    
    			fileOutputStream.flush();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}catch (IOException e) {
    			e.printStackTrace();
    		}finally{
    			try {
    				byteArrayOutputStream.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    参考:

    Java IO流学习总结一:输入输出流

    java基础io流——OutputStream和InputStream的故事(温故知新)

    Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

    Java IO流学习总结四:缓冲流-BufferedReader、BufferedWriter

    Java IO流列表

  • 相关阅读:
    Introduction to XQuery in SQL Server 2005
    [译]Cassandra 架构简述
    冬日绘版实录
    网页实现串口TCP数据通讯的两种方案
    (转)感知哈希算法
    CoreParking
    单线程扫描电脑所有文件与并行计算扫描电脑所有文件所用时间?
    强名称程序集
    一些题(六)
    一些题(五)
  • 原文地址:https://www.cnblogs.com/hongdada/p/10346406.html
Copyright © 2011-2022 走看看