黑马程序员_Java基础:IO流总结
IO流在是java中非常重要,也是应用非常频繁的一种技术。初学者要是能把IO技术的学透,java基础也就能更加牢靠。本文是根据以前学习IO的过程中的一些总结,再通过查找资料完善出来的,应该算是比较适合初学者解读的。
一、概念
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输。所以,为了方便更直观的进行数据操作,就根据数据传输特性将流抽象为各种类。
一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。
流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。
我们可以把形象的比喻成水流 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出。
二、流的分类
1.根据从流本身的特点来说,可以分为结点流(node stream)和过滤流(filters)。
(1)结点流直接从指定的位置(如磁盘文件或内存区域)读或写。(如FileOutputStream和FileInputstream)
(2)过滤流输入流往往是以其它输入流作为它的输入源,经过过滤或处理后再以新的输入流的形式提供给用户,过滤流输出流的原理也类似。(如ObjectOutputStream和ObjectInputStream)
2.根据流操作对象的类型是字节还是字符可分为两大类: 字节流和字符流。
(1)字节流,顾名思义,是操作字节的流,而字节也是最基本的存储单位,所以字节流是最基本的流。它对应有字节输入流和字节输出流(Inputstream和OutputStream)。
(2)字符流,是操作字符的流。我们应该知道,字符是从字节根据编码表编码而来的,而编码表不是唯一的,这就造成了同样的字节数据,如果根据的编码表不一样,那么最终得到的字符也就不一样。所以为了对字符进行高效操作,就有了字符流。总的来说,字符流本质上是基于字节流读取时,查询了指定的码表。字符流也对应有字符输入流和字符输出流(Reader和Writer)。
字节流和字符流的区别可以归纳一下:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件。
三、一般使用原则
java类库中,还包含很多流对象,我们可以看一下他们的派生关系,如下图示意:

那么,这么多的流对象,使用中如何选择?
我们先把流对象按特点划分一下:
1.按数据来源和目的:
(1)是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
(2)是byte[]:ByteArrayInputStream, ByteArrayOutputStream
(3)是Char[]: CharArrayReader, CharArrayWriter
(4)是String: StringBufferInputStream, StringReader, StringWriter
(5)网络数据流:InputStream, OutputStream, Reader, Writer
2.按是否格式化输出:
要格式化输出:PrintStream, PrintWriter
3.按是否要缓冲:
要缓冲:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter
4.按数据格式:
(1)二进制格式(只要不能确定是纯文本的): InputStream, OutputStream及其所有带Stream结束的子类
(2)纯文本格式(含纯英文与汉字或其他编码方式);Reader, Writer及其所有带Reader, Writer的子类
5.按输入输出:
(1)输入:Reader, InputStream类型的子类
(2)输出:Writer, OutputStream类型的子类
6.特殊需要:
(1)从Stream到Reader,Writer的转换类:InputStreamReader, OutputStreamWriter
(2)对象输入输出:ObjectInputStream, ObjectOutputStream
(3)进程间通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
(4)合并输入:SequenceInputStream
(5)更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
基于以上各个流的特点,我们在使用IO流时,可按一下步骤判断选择:
(1)考虑最原始的数据格式是什么:是否为文本?
(2)是输入还是输出?
(3)是否需要转换流:InputStreamReader, OutputStreamWriter?
(4)数据来源和目的是什么:文件?内存?网络?
(5)是否要缓冲:bufferedReader等 ?
(6)是否要格式化输出:print?
四、应用
1.例子一(字节流复制图片):
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 /* 6 要求:复制一张图片。 7 8 需求分析: 9 1,原始的数据格式是什么:是否为文本? 10 原始数据是图片,所以用字节流。 11 2,是输入还是输出? 12 需要复制,所以需要输入和输出。 13 3,是否需要转换流? 14 因为操作过程中只涉及字节,所以不需要转换流。 15 4,数据来源和目的是什么:文件?内存?网络? 16 数据源是硬盘,写入目的地也是硬盘。所以使用FileInputStream和FileOutputStream。 17 5,是否要缓冲? 18 不需要使用缓冲。 19 6,是否要格式化输出? 20 不需要格式化输出。 21 22 思路: 23 1,用字节读取流对象和图片关联。 24 2,用字节写入流对象创建一个图片文件,用于存储获取到的图片数据。 25 3,通过循环读写,完成数据存储。 26 4,关闭资源。 27 */ 28 public class CopyPicTest { 29 30 public static void main(String[] args) { 31 FileOutputStream fos = null; 32 FileInputStream fis = null; 33 34 try 35 { 36 // 定义读取和写入目的文件。如果文件在多层路径下,可以先定义File。 37 fos = new FileOutputStream("d:\\Miku_Toxic_Copy.jpg"); 38 fis = new FileInputStream("c:\\Miku_Toxic.jpg"); 39 40 byte[] buf = new byte[1024*4]; //先把字节写入到字节数组中。 41 int len = 0; 42 43 while ((len=fis.read(buf))!=-1) 44 { 45 fos.write(buf,0,len); //再把字节数组中的元素写入目的文件。 46 } 47 } 48 catch (IOException e) 49 { 50 throw new RuntimeException("复制文件失败"); 51 } 52 finally 53 { 54 try 55 { 56 if(fis!=null) 57 fis.close(); 58 } 59 catch (IOException e1) 60 { 61 throw new RuntimeException("读取关闭失败"); 62 } 63 64 try 65 { 66 if(fos!=null) 67 fos.close(); // 流使用完必须要关闭,释放系统资源。在finally块中关闭就是为了保证执行关闭动作。 68 } 69 catch (IOException e1) 70 { 71 throw new RuntimeException("写入关闭失败"); 72 } 73 } 74 } 75 }
需要注意的是,任何流最终都必须要关闭,释放系统资源。所以可以把关闭资源的动作,放在fially块中来保证执行。
2.例子二(字符流复制文本文件):(为了方便阅读异常做抛处理)
1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.FileReader; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 /* 7 要求:复制文本文件。 8 9 需求分析: 10 1,原始的数据格式是什么:是否为文本? 11 原始数据是文本文件,所以用字符流。 12 2,是输入还是输出? 13 需要复制,所以需要输入和输出。 14 3,是否需要转换流? 15 因为操作过程中只涉及字符,所以不需要转换流。 16 4,数据来源和目的是什么:文件?内存?网络? 17 数据源是硬盘,写入目的地也是硬盘。所以使用FileReader和FileWriter。 18 5,是否要缓冲? 19 有和没有缓冲的情况都有。 20 6,是否要格式化输出? 21 不需要格式化输出。 22 */ 23 public class CopyFileTest { 24 25 public static void main(String[] args) throws IOException{ 26 readAndWriteFile_1(); 27 readAndWriteFile_2(); 28 readAndWriteFile_3(); 29 } 30 31 public static void readAndWriteFile_1()throws IOException 32 { 33 FileReader fr = new FileReader("c:\\fr.txt"); 34 FileWriter fw = new FileWriter("d:\\fw1.txt"); 35 36 int ch = 0; 37 while ((ch=fr.read())!=-1) { //读一个字符,写一个字符。 38 fw.write(ch); 39 fw.flush(); //字符流写入时,需要刷新。 40 } 41 // 流使用完,必须要关闭。 42 fr.close(); 43 fw.close(); 44 } 45 46 public static void readAndWriteFile_2() throws IOException 47 { 48 FileReader fr = new FileReader("fos.txt"); 49 FileWriter fw = new FileWriter("d:\\fos2.txt"); 50 51 char[] cha = new char[1024]; //先把字符写入到字符数组中。 52 int len = 0; 53 while ((len=fr.read(cha))!=-1) { 54 fw.write(cha,0,len); //再把字符数组中的元素写入目的文件。 55 fw.flush(); //字符流写入时,需要刷新。 56 } 57 // 流使用完,必须要关闭。 58 fr.close(); 59 fw.close(); 60 } 61 62 public static void readAndWriteFile_3() throws IOException 63 { 64 FileReader fr= new FileReader("fos.txt"); 65 FileWriter fw = new FileWriter("d:\\fos3.txt"); 66 // 使用缓冲区。 67 BufferedReader bufr = new BufferedReader(fr); 68 BufferedWriter bufw = new BufferedWriter(fw); 69 70 String len = null; 71 while ((len=bufr.readLine())!=null) { //每次把文本中的一行读进缓冲区(不含换行符)。 72 bufw.write(len); //再把一行数据写入目的文件(不含换行符)。 73 bufw.newLine(); //换行方法。 74 bufw.flush(); //字符流需要刷新。 75 } 76 // 流使用完,必须要关闭。 77 bufr.close(); 78 bufw.close(); 79 } 80 }
前面有提过,字符流本身实际有使用缓冲技术,所以写入的时候需要手动刷新,让缓冲区的数据(内存)刷新到目的地。
3.例子三(转换流,将键盘录入数据保存为文本文件)
1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.FileWriter; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 /* 7 要求:录入键盘数据,保存到文本文件。 8 9 需求分析: 10 1,原始的数据格式是什么:是否为文本? 11 源数据是字节流。目的数据是字符流。 12 2,是输入还是输出? 13 需要读取和写入,所以需要输入和输出。 14 3,是否需要转换流? 15 因为操作过程中有字节和字符,需要转换流。 16 4,数据来源和目的是什么:文件?内存?网络? 17 数据源是键盘录入,写入目的地是硬盘。所以使用InputStreamReader和FileWriter。 18 5,是否要缓冲? 19 需要。使用BufferedReadr和BufferedWriter。 20 6,是否要格式化输出? 21 不需要格式化输出。 22 */ 23 public class TransStream { 24 25 public static void main(String[] args) { 26 System.out.println("请输入要保存到文件中的内容(输入over结束):"); 27 // System.in录入键盘数据得到的是字节流,如要用字符流的方式读取,必须要先将字节流转成字符流。 28 InputStreamReader isr = new InputStreamReader(System.in); 29 // 使用缓冲区读写更加效率。 30 BufferedReader bufr = new BufferedReader(isr); 31 BufferedWriter bufw = null; 32 try { 33 // 也可以通过OutputStreamWriter来指定编码表。 34 // bufw = new BufferedWriter(new OutputStreamWriter("c:\\systemin.txt","UTF-8")); 35 bufw = new BufferedWriter(new FileWriter("c:\\systemin.txt")); 36 String str = null; 37 while ((str=bufr.readLine())!=null) { 38 if ("over".equals(str)) 39 System.exit(0); //如果键盘录入over,则结束。 40 bufw.write(str); 41 bufw.newLine(); 42 bufw.flush(); 43 } 44 } catch (IOException e) { 45 throw new RuntimeException("找不到目标文件或写入数据失败"); 46 } finally { 47 try { 48 if (bufw!=null) { 49 bufr.close(); 50 bufw.close(); 51 } 52 } catch (IOException e) { 53 throw new RuntimeException("关闭资源失败"); 54 } 55 } 56 } 57 }
从以上例子可以看出,转换流是字符流和字节流之间的桥梁,我们实际也可以利用转换流把文本文件中的内容打印到控制台上。也就是说可以通过转换流让字符流和字节流相互转换。
另外要注意的是,将字节转换成字符的时,想按照指定的码表来转换,也可以通过OutputStreamWriter(OutputStream out,CharsetEncoder enc)的方式指定,其中enc即代表码表的名字,如"UTF-8","ISO 8859-1"等。
4.例子四(利用打印流把键盘录入数据保存到文件):
1 import java.io.BufferedReader; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 import java.io.InputStreamReader; 5 import java.io.PrintWriter; 6 /* 7 要求:录入键盘数据,保存到文本文件。 8 9 需求分析: 10 1,原始的数据格式是什么:是否为文本? 11 源数据是字节流。目的数据是字符流。 12 2,是输入还是输出? 13 需要读取和写入,所以需要输入和输出。 14 3,是否需要转换流? 15 因为操作过程中有字节和字符,需要转换流。 16 4,数据来源和目的是什么:文件?内存?网络? 17 数据源是键盘录入,写入目的地是硬盘。所以使用InputStreamReader和FileWriter。 18 5,是否要缓冲? 19 需要。使用BufferedReadr。 20 6,是否要格式化输出? 21 需要格式化输出。使用PrintWriter。 22 */ 23 public class PrintStream { 24 25 public static void main(String[] args) throws IOException{ 26 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 27 // PrintWriter可以设置刷新模式,如果刷新模式为true,那么PrintWriter部分方法输出是会自动刷新。 28 PrintWriter out = new PrintWriter(new FileWriter("c:\\a.txt"),true); 29 String line = null; 30 while ((line=bufr.readLine())!=null) 31 { 32 // 如果键盘输入over,则结束程序。 33 if("over".equals(line)) 34 break; 35 // 因为PrintWriter设置有刷新模式,不需再手动刷新。 36 out.println(line.toUpperCase()); 37 } 38 bufr.close(); 39 out.close(); 40 } 41 }
看完例子,在看看打印流的特点:
打印流:
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:
PrintStream
构造函数可以接收的参数类型:
(1)file对象。File
(2)字符串路径。String
(3)字节输出流。OutputStream
字符打印流:
PrintWriter
构造函数可以接收的参数类型:
(1)file对象。File
(2)字符串路径。String
(3)字节输出流。OutputStream
(4)字符输出流。Writer
可以看出,字符打印流相对来说适用性更强。
5.例子五(LineNumberReader):
1 import java.io.FileReader; 2 import java.io.IOException; 3 import java.io.LineNumberReader; 4 5 public class LineNumberReaderDemo { 6 public static void main(String[] args)throws IOException { 7 FileReader fr = new FileReader("PersonDemo.java"); 8 9 LineNumberReader lnr = new LineNumberReader(fr); 10 11 String line = null; 12 lnr.setLineNumber(100); 13 while((line=lnr.readLine())!=null) 14 System.out.println(lnr.getLineNumber()+":"+line); 15 lnr.close(); 16 } 17 }
从以上例子可以看出,LineNumberReader属于过滤流,而它实际的功能也是在字符写入流的基础上,加上获取行号(getLineNumber()), 设置行号(setLineNumber())的功能。LineNumberOutputStream同理,这里不再做阐述。
6.例子六(数据流):
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 7 public class DataStreamDemo 8 { 9 public static void main(String[] args) throws IOException{ 10 writeData(); 11 readData(); 12 } 13 14 public static void readData()throws IOException{ 15 DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); 16 17 int num = dis.readInt(); 18 boolean b = dis.readBoolean(); 19 double d = dis.readDouble(); 20 21 System.out.println("num="+num); 22 System.out.println("b="+b); 23 System.out.println("d="+d); 24 25 dis.close(); 26 } 27 public static void writeData()throws IOException{ 28 DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); 29 30 dos.writeInt(234); 31 dos.writeBoolean(true); 32 dos.writeDouble(9887.543); 33 34 dos.close(); 35 } 36 }
以上运行结果是:
234
true
9887.543
从例子可以看出数据流的特点:
DataInputStream和DataOutputStream,经常用来处理基本数据类型的数据,好处就是可以按类型来处理数据。
7.例子七(数组流):
1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 4 public class ByteArrayStream { 5 public static void main(String[] args) { 6 //数据源。 7 ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes()); 8 //数据目的,数组输出流输出的数据目的地是内存。 9 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 10 11 int by = 0; 12 while((by=bis.read())!=-1) 13 bos.write(by); 14 15 System.out.println(bos.size()); 16 System.out.println(bos.toString()); 17 18 // 最后内存中流的数据可以通过writeTo方法输出到其他节点流中,将数据保存到目的地。 19 // bos.writeTo(new FileOutputStream("a.txt")); 20 } 21 }
运行结果为:
7
ABCDEFD
数组流是用于操作字节数组的流对象,它是使用流的思想来操作数据的体现。
ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地(在内存中)。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
8.例子八(RandomAccessFile):
1 import java.io.IOException; 2 import java.io.RandomAccessFile; 3 4 public class RandomAccessFileDemo { 5 public static void main(String[] args) throws IOException{ 6 writeFile(); 7 writeFile_2(); 8 readFile(); 9 } 10 11 public static void readFile()throws IOException{ 12 RandomAccessFile raf = new RandomAccessFile("ran.txt","r"); 13 //调整对象中指针,以8为单位是因为两个中文字4个字节加上int数据的4个字节。 14 //raf.seek(8*1); 15 16 //跳过指定的字节数 17 raf.skipBytes(8); 18 19 byte[] buf = new byte[4]; 20 raf.read(buf); 21 String name = new String(buf); 22 int age = raf.readInt(); 23 24 System.out.println("name="+name); //结果为周七。 25 System.out.println("age="+age); 26 raf.close(); 27 } 28 29 // 也能修改指定指针上的数据。 30 public static void writeFile_2()throws IOException{ 31 RandomAccessFile raf = new RandomAccessFile("ran.txt","rw"); 32 raf.seek(8*0); 33 raf.write("周七".getBytes()); 34 raf.writeInt(103); 35 raf.close(); 36 } 37 38 public static void writeFile()throws IOException{ 39 RandomAccessFile raf = new RandomAccessFile("ran.txt","rw"); 40 raf.write("李四".getBytes()); 41 raf.writeInt(97); 42 raf.write("王五".getBytes()); 43 raf.writeInt(99); 44 raf.close(); 45 } 46 }
运行结果为:
周七
103
从前面的派生图可以看出,RandomAccessFile不算是IO体系中子类,而是直接继承自Object。但是它是IO包中成员,因为它具备读和写功能。
实际上RandomAccessFile内部封装了一个数组,而且通过指针对数组的元素进行操作。
从例子可以看出通过getFilePointer获取指针位置,同时通过seek改变指针的位置。
至于RandomAccessFile能够同时实现读和写的功能的原因,其实是是其在内部封装了字节输入流和输出流。
通过查询API中构造函数可以看出,该类只能操作文件,而且操作文件还有模式:只读r,,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
9.例子九(管道流):
1 import java.io.IOException; 2 import java.io.PipedInputStream; 3 import java.io.PipedOutputStream; 4 5 class Read implements Runnable { 6 private PipedInputStream in; 7 Read(PipedInputStream in){ 8 this.in = in; 9 } 10 public void run() { 11 try { 12 byte[] buf = new byte[1024]; 13 14 System.out.println("读取前。。没有数据阻塞"); 15 int len = in.read(buf); 16 System.out.println("读到数据。。阻塞结束"); 17 18 String s= new String(buf,0,len); 19 System.out.println(s); 20 in.close(); 21 } 22 catch (IOException e) { 23 throw new RuntimeException("管道读取流失败"); 24 } 25 } 26 } 27 28 class Write implements Runnable { 29 private PipedOutputStream out; 30 Write(PipedOutputStream out) { 31 this.out = out; 32 } 33 public void run() { 34 try { 35 System.out.println("开始写入数据,等待6秒后。"); 36 Thread.sleep(6000); 37 out.write("piped lai la".getBytes()); 38 out.close(); 39 } 40 catch (Exception e) { 41 throw new RuntimeException("管道输出流失败"); 42 } 43 } 44 } 45 46 public class PipedStreamDemo { 47 public static void main(String[] args) throws IOException { 48 PipedInputStream in = new PipedInputStream(); 49 PipedOutputStream out = new PipedOutputStream(); 50 // 管道输入流连接到管道输出流。 51 in.connect(out); 52 53 Read r = new Read(in); 54 Write w = new Write(out); 55 new Thread(r).start(); 56 new Thread(w).start(); 57 } 58 }
运行结果为:
读取前。。没有数据阻塞
开始写入数据,等待6秒后。
读到数据。。阻塞结束
piped lai la
看完例子,再来看看管道流的特点:
PipedInputStream类与PipedOutputStream类用于在应用程序中创建管道通信,多用于多线程中。
一个PipedInputStream实例对象必须和一个PipedOutputStream实例对象进行连接而产生一个通信管道。PipedOutputStream可以向管道中写入数据,PipedIntputStream可以读取PipedOutputStream向管道中写入的数据。
这两个类主要用来完成线程之间的通信。一个线程的PipedInputStream对象能够从另外一个线程的PipedOutputStream对象中读取数据。
所以,我们使用管道流时,必须要同时构造管道输入流和管道输出流。
10.例子十(合并流):
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 import java.io.SequenceInputStream; 5 import java.util.ArrayList; 6 import java.util.Enumeration; 7 import java.util.Iterator; 8 9 public class MergeFile { 10 public static void main(String[] args) throws IOException { 11 merge(); 12 } 13 14 public static void merge() throws IOException { 15 // 创建集合,泛型为文件字节输入流。 16 ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); 17 18 for(int x=1; x<=3; x++) 19 // 将输入流添加进集合中。 20 al.add(new FileInputStream("c:\\splitfiles\\"+x+".part")); 21 22 final Iterator<FileInputStream> it = al.iterator(); 23 24 // 因为前面为了更加让程序效率使用了ArrayList,所以这里只能通过匿名内部类自定义Enumeration,实现目的。 25 // 如果前面使用的是Vetor集合,则不需再定义这一步。 26 Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){ 27 public boolean hasMoreElements() { 28 return it.hasNext(); 29 } 30 public FileInputStream nextElement() { 31 return it.next(); 32 } 33 }; 34 35 SequenceInputStream sis = new SequenceInputStream(en); 36 37 FileOutputStream fos = new FileOutputStream("c:\\splitfiles\\0.bmp"); 38 byte[] buf = new byte[1024]; 39 int len = 0; 40 while((len=sis.read(buf))!=-1) 41 fos.write(buf,0,len); 42 43 fos.close(); 44 sis.close(); 45 } 46 }
合并流,SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。总而言之,合并流就是用来合并多个字节输出流变成一个字节输出流。
11.例子十一(分割文件):
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 5 public class SplitFile { 6 public static void main(String[] args) throws IOException { 7 splitFile(); 8 } 9 10 public static void splitFile()throws IOException { 11 FileInputStream fis = new FileInputStream("c:\\1.bmp"); 12 FileOutputStream fos = null; 13 14 byte[] buf = new byte[1024*1024]; 15 int len = 0; 16 int count = 1; 17 while((len=fis.read(buf))!=-1) { 18 fos = new FileOutputStream("c:\\splitfiles\\"+(count++)+".part"); 19 fos.write(buf,0,len); 20 fos.close(); 21 } 22 fis.close(); 23 } 24 }
可以看出,java中并没有可以直接分割流的对象。分割流的方法很多,以上例子主要是通过计数器,和作为缓冲区的数组来控制分割流,然后输出到不同的文件上。
这十一个例子囊括了IO流中最常用的部分,剩余其他的流的使用思想也是相通的,这里就不再阐述了。相信只要认真的去了解学习IO流,以后它就会成为你的好帮手。
浙公网安备 33010602011771号