本文共 16730 字,大约阅读时间需要 55 分钟。
目录
流类似于传输的媒介,Java程序运行在jvm中,jvm需要把数据存到磁盘,或者是jvm将数据通过网络传输传到另一个主机的jvm中,需要先建立流这个通道,流通道连接了传输起点与传输目的地。这就决定了流对象的构造方法中路径是一个主要的属性。
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表(可以设置)。 字节流和字符流的区别:
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。字符流多了一步处理流程,把二进制文件读出来进行字符转换,直接得到字符值,而字节流需要处理后才可以看到可读性结果。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
更详细解释
package com.app;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class A1 { public static void main(String[] args) { A1 a1 = new A1(); //电脑d盘中的abc.txt 文档 String filePath = "D:/abc.txt" ; String reslut = a1.readFile( filePath ) ; System.out.println( reslut ); } /** * 读取指定文件的内容 * @param filePath : 文件的路径 * @return 返回的结果 */ public String readFile( String filePath ){ FileInputStream fis=null; String result = "" ; try { // 根据path路径实例化一个输入流的对象 fis = new FileInputStream( filePath ); //2. 返回这个输入流中可以被读的剩下的bytes字节的估计值; int size = fis.available() ; //3. 根据输入流中的字节数创建byte数组; byte[] array = new byte[size]; //4.把数据读取到数组中; fis.read( array ) ; //5.根据获取到的Byte数组新建一个字符串,然后输出; result = new String(array); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally{ if ( fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } return result ; }}
package com.app;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class A2 { public static void main(String[] args) { A2 a2 = new A2(); //电脑d盘中的abc.txt 文档 String filePath = "D:/abc.txt" ; //要写入的内容 String content = "今天是2017/1/9,天气很好" ; a2.writeFile( filePath , content ) ; } /** * 根据文件路径创建输出流 * @param filePath : 文件的路径 * @param content : 需要写入的内容 */ public void writeFile( String filePath , String content ){ FileOutputStream fos = null ; try { //1、根据文件路径创建输出流 fos = new FileOutputStream( filePath ); //2、把string转换为byte数组; byte[] array = content.getBytes() ; //3、把byte数组输出; fos.write( array ); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally{ if ( fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }}
package com.app;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class A3 { public static void main(String[] args) { A3 a2 = new A3(); //电脑d盘中的cat.png 图片的路径 String filePath1 = "D:/cat.png" ; //电脑e盘中的cat.png 图片的路径 String filePath2 = "E:/cat.png" ; //复制文件 a2.copyFile( filePath1 , filePath2 ); } /** * 文件复制 * @param filePath_old : 需要复制文件的路径 * @param filePath_new : 复制文件存放的路径 */ public void copyFile( String filePath_old , String filePath_new){ FileInputStream fis=null ; FileOutputStream fout = null ; try { // 根据path路径实例化一个输入流的对象 fis = new FileInputStream( filePath_old ); //2. 返回这个输入流中可以被读的剩下的bytes字节的估计值; int size = fis.available() ; //3. 根据输入流中的字节数创建byte数组; byte[] array = new byte[size]; //4.把数据读取到数组中; fis.read( array ) ; //5、根据文件路径创建输出流 fout = new FileOutputStream( filePath_new ) ; //5、把byte数组输出; fout.write( array ); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }finally{ if ( fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if ( fout != null ) { try { fout.close(); } catch (IOException e) { e.printStackTrace(); } } } }}
首先抛出一个问题,有了InputStream
为什么还要有BufferedInputStream
?
BufferedInputStream
和BufferedOutputStream
这两个类分别是FilterInputStream
和FilterOutputStream
的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。
我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream
写完数据后,要调用flush()
方法或close()
方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader
和BufferedWriter
两个类。
现在就可以回答在本文的开头提出的问题:
BufferedInputStream
和BufferedOutputStream
类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。
总结:
BufferedInputStream
是缓冲输入流。它继承于FilterInputStream
。
BufferedInputStream
的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持mark()标记
和reset()重置方法
。
BufferedInputStream
本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream
后,当我们通过read()
读取输入流的数据时,BufferedInputStream
会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。
package com.app;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;public class A3 { public static void main(String[] args) throws IOException { String filePath = "F:/123.png" ; String filePath2 = "F:/abc.png" ; 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 ; //代表实际读取的字节数 while( (length = bufferedInputStream.read( b ) )!= -1 ){ //length 代表实际读取的字节数 bufferedOutputStream.write(b, 0, length ); } //缓冲区的内容写入到文件 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(); } } } }}
那么什么时候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()了。1.BufferedOutputStream
在close()
时会自动flush
BufferedOutputStream
在不调用close()
的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush. 实战演练——复制F盘里面的一个txt文本
package com.app;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;public class A4 { public static void main(String[] args) { String filePath = "F:/123.txt" ; String filePath2 = "F:/abc.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 ; //每次读取一行的内容 while ( (result = bufferedReader.readLine() ) != null ){ bufferedWriter.write( result ); //把内容写入文件 bufferedWriter.newLine(); //换行,result 是一行数据,所以没写一行就要换行 } 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(); } } }}
package com.app;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;public class A5 { public static void main(String[] args) { String filePath = "F:/123.txt" ; String filePath2 = "F:/abc.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 ; while( (result = inputStreamReader.read()) != -1){ //一次只读一个字符 outputStreamWriter.write( result ); //一次只写一个字符 } 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(); } } } }}
练习1 字节流ByteArrayInputStream
的读写过程测试
package com.app;import java.io.ByteArrayInputStream;import java.io.IOException;public class A7 { public static void main(String[] args) { String mes = "hello,world" ; byte[] b = mes.getBytes() ; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( b ) ; int result = -1 ; while( ( result = byteArrayInputStream.read() ) != -1){ System.out.println( (char) result ); } try { byteArrayInputStream.close(); } catch (IOException e) { e.printStackTrace(); } }}
练习2将ByteArrayOutputStream
读出的字节流用FileOutputStream
写入文件
package com.app;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class A6 { 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( "F:/123.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中是怎样工作的,我将使用之前教程中提到的Employee类,假设我们定义了如下的Employee类,该类实现了Serializable 接口。
public class Employee implements java.io.Serializable{ public String name; public String address; public transient int SSN; public int number; public void mailCheck() { System.out.println("Mailing a check to " + name + " " + address); }}
ObjectOutputStream 类用来序列化一个对象,如下的 SerializeDemo 例子实例化了一个 Employee 对象,并将该对象序列化到一个文件中。
该程序执行后,就创建了一个名为 employee.ser 文件。该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。
注意: 当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。
public class SerializeDemo{ public static void main(String [] args) { Employee e = new Employee(); e.name = "Reyan Ali"; e.address = "Phokka Kuan, Ambehta Peer"; e.SSN = 11122333; e.number = 101; try { FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(e); out.close(); fileOut.close(); System.out.printf("Serialized data is saved in /tmp/employee.ser"); }catch(IOException i) { i.printStackTrace(); } }}
下面的 DeserializeDemo 程序实例了反序列化,/tmp/employee.ser 存储了 Employee 对象。
public class DeserializeDemo{ public static void main(String [] args) { Employee e = null; try { FileInputStream fileIn = new FileInputStream("/tmp/employee.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); e = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println("Employee class not found"); c.printStackTrace(); return; } System.out.println("Deserialized Employee..."); System.out.println("Name: " + e.name); System.out.println("Address: " + e.address); System.out.println("SSN: " + e.SSN); System.out.println("Number: " + e.number); }}
以上程序编译运行结果如下所示:
Deserialized Employee...Name: Reyan AliAddress:Phokka Kuan, Ambehta PeerSSN: 0Number:101