对于java的文件读写是咱们必须使用的一项基本技能,所以了解其中的原理,字节流和字符流的本质有着重要的意义。html
1 方式: 字节流 Byte 和 字符流 Char 2 方向: 输入 Input 和 输出 Output ; 读 Reader 和 写 Writer 3 源: 字符串 String, 数组 Array, 对象 Object, 文件 File, 通道 Channel, 管道 Pipe, 过滤器 Filter,控制台 Console, 网络 Network Link ;
一切可产生/接收数据的 Data 4 性能: 带缓冲和不带缓冲的 5 行为: Readable, Appendable, Closable, Flushable, Serializable/Externalizable
方式、方向、源、行为四个维度能够组合出各类特性的IO流。
JavaIO框架采用装饰器模式,能够在现有IO流的基础上添加新的IO特性,生成更强功能的IO流,体现了读写能力的可扩展性。 好比要构造一个带缓冲的文本文件读取流:
File -> FileInputStream -> InputStreamReader -> FileReader -> BufferedFileReader,
其中文本:字符流, 文件:源, 读:输入方向, 带缓冲的:性能。 读写耗时的字符输入输出流可使用缓冲来提高性能,好比文件读写、网络流读写等。
这是因为每一个待读写的字符都要通过读写字节、字节/字符转换两个操做。
缓冲其实是将大量单个的读写操做转换为一个大的单个的批量读写操做。
1 字节输入流:InputStream, BufferedInputStream, FileInputStream, ByteArrayInputStream, PipedInputStream, FilterInputStream, DataInputStream, ObjectInputStream; 2 字节输出流: OuputStream, BufferedOuputStream, FileOuputStream, ByteArrayOuputStream, PipedOuputStream, FilterOuputStream, DataOuputStream, ObjectOuputStream, PrintStream;
1 字符输入流: Reader, BufferedReader, FileReader, CharArrayReader, PipedReader, FilterReader, StringReader, LineNumberReader; 2 字符输出流: Writer, BufferedWriter, FileWriter, CharArrayWriter, PipedWriter, FilterWriter, StringWriter, PrintWriter;
1 InputStreamReader:输入字节流转化为输入字符流 2 OutStreamWriter: 输出字符流转化为输出字节流
多个类实现同一个接口,而且这些实现类均持有一个该接口的引用,经过构造器传入,从而能够在这些实现类的基础上任意动态地组合叠加,构造出所需特性的实现来。装饰器模式能够实现数学公式/逻辑公式运算函数。java
在IO流的实现类中,一般有一个 Char[],Byte[], CharBuffer, StringBuffer, ByteBuffer的成员数组或成员对象。将成员数组/对象里的数据写到方法里给定的参数数组或返回值,即为读实现;将方法里给定的参数数组写到成员数组或成员对象里,即为写实现。读写数据本质上就是数据在不一样源之间的拷贝实现。
IO流一般是会实现读写单个字节/字符、读写字节数组/字符数组、跳过若干字节/字符的功能。成员 (next,pos,mark,count) 用于标识数据读写位置;mark 与 reset 方法组合可实现重复读写。linux
1 要实现一个自定义的 Reader, 可参考 StringReader 实现;StringReader 能够将字符串做为字符流来读取,内部成员为String;
而 StringWriter 内部成员为线程安全的 StringBuffer。二者均要考虑并发读写的问题,使用一个 Object 锁进行互斥访问。 2 FileReader 是文本文件读取流,经过继承 InputStreamReader, 转化 FileInputStream 而得。
因文件是对磁盘字节数据的抽象,所以要得到人类可读的文本,必然存在从字节流到字符流的转换。 3 InputStreamReader 使用 StreamDecoder 将字节流转换为指定字符编码的字符流,后续读操做直接委托 StreamDecoder 读取;
StreamDecoder 是一个字节解码器, 是 Reader 实现类。
OutputStreamWriter 使用 StreamEncoder 将字符流转换为指定字符编码的字节流,
后续写操做直接委托 StreamEncoder 写入底层 OutputStream;StreamEncoder 是一个 Writer 实现类。 4 FilterReader, FilterWriter, FilterInputStream, FilterOutputStream: IO流的抽象类,分别持有一个[Reader,Writer,InputStream, OutputStream],
自定义 IO 流能够经过继承 FilterXXX 来实现。CommonIO库里 ProxyReader, ProxyWriter 分别提供了 Reader, Writer 的一个示例实现,
java.io.DataInputStream 提供了 FilterInputStream 的一个示例实现,java.io.PrintStream 提供了 FilterOutputStream 的一个示例实现。 5 BufferedReader: 提供了缓冲读及读行的功能。
BufferedReader 主要成员为 [Reader in, char[defaultsize=8192] cb, int nextChar, int nChars, int markedChar];
nextChar 指示将要读取的字符位置, nChars 指示能够读取字符的终止位置,
markedChar 表示被标记的将要读取字符的位置;
BufferedReader 从字符流 in 中读取字符预存入缓冲字符数组 cb 中,而后在须要的时候从 cb 中读取字符。
BufferedReader 的实现由 fill 和 read, readLine 共同完成。
当缓冲字符数组的字符数不足于填充要读取的参数数组时,就会使用 fill 方法从输入流 in 中读取数据再进行填充;6 PipedReader: 管道输入流,必须从 PipedWriter 中读取。
底层实现有两个线程、两个位置索引以及一个字符数组。
两个线程分别是读线程 readSide 和 写线程 writeSide, 两个位置索引分别用于指示字符数组中将要写的位置和将要读的位置。
字符数组是一个循环队列,默认大小为 1024 chars 。初始化时必须将 PipedReader 的输入链接至 PipedWriter 。
读实现方法中,便是将底层字符数组拷贝到方法中的参数数组。PipedWriter 的实现相对简单,其输出链接至 PipedReader;
PipedWriter 的写就是 PipedReader 的读,所以 PipedWriter 的方法实现委托给 PipedReader 引用的方法。
1 文件相关的类: File, RandomAccessFile, FileDescriptor, FileChannel, FilePermission, FilePermissionCollection, FileFilter, FileSystem, UnixFileSystem ; 2 文件 File: 惟一性路径标识【目录路径/文件名[.文件扩展名]】、文件元信息【描述符、类型、大小、建立时间、最近访问时间、节点信息】、文件访问权限【读/写/执行组合】等;
File 的操做会先使用 SecurityManager 检查访问权限,而后经过 FileSystem 判断和操做。 3 构造能够实际读写的文件输入输出流须要使用 FileInputStream, FileOutputStream, FileReader, FileWriter, BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream 来包装 File ,
好比 new BufferedReader(new FileReader(new File(filename))) 。 4 FileChannel: 操做文件内容的通道;FileChannel 是线程安全的,使用 java.nio 引入的高性能机制来实现。 5 文件描述符 FileDescriptor : 用于操做打开的文件、网络socket、字节流的惟一标识句柄;
其成员是一个整型数 fd 和一个原子计数器 AtomicInteger useCount ;
标准输入流 System.in.fd = 0 , 标准输出流 System.out.fd = 1 , 标准错误流 System.err.fd = 2 6 文件访问权限 FilePermission: 提供了【文件可进行的操做 actions 与位掩码表示 mask 】之间的转化方法。
文件可进行的操做 actions 在类 SecurityConstants 的常量中定义;
mask 是一个整型数,表示文件的执行(0x01)/写(0x02)/读(0x04)/删(0x08)/读连接(0x10)/无(0x00) 等权限组合。 7 FileSystem:所使用的操做系统平台的文件系统。
指明该文件系统所使用的【文件分隔符、路径分隔符、路径标准化、路径解析、文件访问安全控制、文件元信息访问、文件操做等】。
文件分割符是指路径名中分割目录的符号,linux = '/', windows = '\' ;
路径分割符是环境变量中分割多个路径的符号,linux = ':' , windows = ';' ;
UnixFileSystem 提供了 FileSystem 的一个实现示例。
存在 BA_EXISTS = 0x01;
普通文件 BA_REGULAR = 0x02;
目录 BA_DIRECTORY = 0x04;
隐藏 BA_HIDDEN = 0x08;
可读 ACCESS_READ = 0x04;
可写 ACCESS_WRITE = 0x02;
可执行 ACCESS_EXECUTE = 0x01;
判断文件属性及访问权限采用与或非位运算实现, 优势是能够灵活组合。
序列化是实现内存数据持久化的机制。能够将 Java 对象数据存储到文件中,或者将文件中的数据恢复成Java对象。Java RPC, Dubbo,ORM 等都依赖于序列化机制。 序列化相关的类及方法: Serializable/Externalizable, ObjectInputStream, ObjectOutputStream, readObject, writeObject 。 使用 ObjectInputStream.readObject, ObjectOutputStream.writeObject 方法进行对象序列化:
JDK提供的标准对象序列化方式;对象必须实现 Serializable 接口;
适用于任何 Java 对象图的序列化,通用的序列化方案;输出为可读性差的二进制文件格式,很难为人所读懂,没法进行编辑;
对某些依赖于底层实现的组件,存在版本兼容和可移植性问题;只有基于 Java 的应用程序能够访问序列化数据; 使用 XMLDecoder.readObject, XMLEncoder.writeObject 方法进行对象序列化:
对象是普通的 javaBean,无需实现 Serializable 接口;输出为可读性良好的XML文件,容易编辑和二次处理;
其它平台亦可访问序列化数据;能够修改数据成员的内部结构的实现,只要保留原有bean属性及setter/getter签名,就不影响序列化和反序列化;
能够解决版本兼容的问题,提供长期持久化的能力;每一个须要持久化的数据成员都必须有一个 Java bean 属性。 实现 Externalizable 接口来进行对象序列化。
需在对象中实现 readObject, writeObject 方法,提供更高的序列化灵活性,由开发者自行决定实例化哪些字段、何种顺序、输出格式等细节。
java.nio 为 javaIO 体系引入了更好的性能改善,主要是 Buffer, Channel 和 file 相关的支撑类。
因为 java.io 类已经引用了 nio 里的类,所以直接使用 java.io 里的类就能够得到 nio 带来的好处了。
首先咱们看一个例子:windows
字节流读写测试,能够发现若是预先规定了Byte对象的大小,就会产生乱码:数组
1 package com.io.test; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.OutputStream; 10 11 public class ByteTest { 12 13 public static void main(String[] args) { 14 15 // 第一种方式读文件,由于方法throws了异常,因此在这要捕获异常 16 try { 17 ByteTest.readFromFileByByte(); 18 } catch (FileNotFoundException e) { 19 e.printStackTrace(); 20 System.out.println("找不到文件啊"); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 System.out.println("读不成功啊!"); 24 } 25 26 System.out.println("==========================="); 27 28 // 第二种方式读文件 29 try { 30 ByteTest.readFromFileByteTwo(); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 System.out.println("仍是读不成功啊!"); 34 } 35 System.out.println("==========================="); 36 ByteTest.WriteToFile(); 37 } 38 39 /** 40 * 第一种方法读文件 经过字节流读取文件中的数据 41 * 42 * @throws IOException 43 */ 44 public static void readFromFileByByte() throws IOException { 45 File file = new File("abc.txt"); 46 // 若是文件不存在则建立文件 47 if (!file.exists()) { 48 file.createNewFile(); 49 } 50 InputStream inputStream = new FileInputStream(file); 51 // 这里定义了数组的长度是1024个字节,若是文件超出这字节,就会溢出,结果就是读不到1024字节之后的东西 52 byte[] bs = new byte[2048]; 53 // 这里len得到的是文件中内容的长度 54 int len = inputStream.read(bs); 55 inputStream.close(); 56 System.out.println(len+new String(bs)); 57 } 58 59 /** 60 * 第二种方法读文件 经过字节流读取文件中的数据 61 * 62 * @throws IOException 63 */ 64 public static void readFromFileByteTwo() throws IOException { 65 // 注意这里的不一样,File.separator是分隔符,这里指明绝对路径,即D盘根目录下的abc.txt文件 66 File file = new File("d:" + File.separator + "abc.txt"); 67 // 若是文件不存在则建立文件 68 if (!file.exists()) { 69 file.createNewFile(); 70 } 71 InputStream inputStream = new FileInputStream(file); 72 // 这里也有不一样,能够根据文件的大小来声明byte数组的大小,确保能把文件读完 73 byte[] bs = new byte[(int) file.length()]; 74 // read()方法每次只能读一个byte的内容 75 inputStream.read(bs); 76 inputStream.close(); 77 System.out.println(new String(bs)); 78 } 79 80 public static void WriteToFile() { 81 File file = new File("D:" + File.separator + "write.txt"); 82 OutputStream outputStream = null; 83 if (!file.exists()) { 84 try { 85 // 若是文件找不到,就new一个 86 file.createNewFile(); 87 } catch (IOException e) { 88 e.printStackTrace(); 89 } 90 } 91 try { 92 // 定义输出流,写入文件的流 93 outputStream = new FileOutputStream(file); 94 } catch (FileNotFoundException e) { 95 e.printStackTrace(); 96 } 97 // 定义将要写入文件的数据 98 String string = "Hell Java, Hello World, 你好,世界!"; 99 // 把string转换成byte型的,并存放在数组中 100 byte[] bs = string.getBytes(); 101 try { 102 // 写入bs中的数据到file中 103 outputStream.write(bs); 104 outputStream.close(); 105 } catch (IOException e) { 106 e.printStackTrace(); 107 } 108 109 // =================到此,文件的写入已经完成了! 110 // 若是想在文件后面追加内容的话,用下面的方法 111 OutputStream outToFileEnd = null; 112 try { 113 outToFileEnd = new FileOutputStream(file, true); 114 String string2 = "Here I come!!"; 115 byte[] bs2 = string2.getBytes(); 116 outToFileEnd.write(bs2); 117 outToFileEnd.close(); 118 } catch (FileNotFoundException e) { 119 e.printStackTrace(); 120 } catch (IOException ex) { 121 ex.printStackTrace(); 122 } 123 } 124 }
字符流的读写,一样的对于Byte对象的大小设定很是重要,同时写入的时候并无识别出换行符:缓存
1 package com.io.test; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 import java.io.Reader; 11 import java.io.Writer; 12 13 public class CharTest { 14 15 public static void main(String[] args) throws IOException { 16 17 File file1 = new File("D:" + File.separator + "test1.txt"); 18 File file2 = new File("D:" + File.separator + "test2.txt"); 19 Writer writer = new FileWriter(file1); 20 Writer writer1 = new FileWriter(file2, true); 21 String string = "今天是教师节!"; 22 writer.write(string); 23 String string2 = "祝愿全部的老师教师节快乐!"; 24 writer1.write(string); 25 writer1.write(string2); 26 // 在这必定要记得关闭流 27 writer.close(); 28 writer1.close(); 29 30 ReadFromFile(); 31 ReadFromFile2(); 32 } 33 34 public static void ReadFromFile() throws IOException { 35 File file = new File("d:" + File.separator + "test1.txt"); 36 Reader reader = new FileReader(file); 37 char[] cs = new char[1024]; 38 // 上面定义了一个大小为1024的char型数组,若是文件内容过大,程序就会报错,而不是只读到1024的大小 39 reader.read(cs, 0, (int) file.length()); 40 System.out.println(cs); 41 reader.close(); 42 } 43 44 public static void ReadFromFile2() throws IOException { 45 try { 46 // 声明一个可变长的stringBuffer对象 47 StringBuffer sb = new StringBuffer(""); 48 49 Reader reader = new FileReader("d:" + File.separator + "test1.txt"); 50 // 这里咱们用到了字符操做的BufferedReader类 51 BufferedReader bufferedReader = new BufferedReader(reader); 52 String string = null; 53 // 按行读取,结束的判断是是否为null,按字节或者字符读取时结束的标志是-1 54 while ((string = bufferedReader.readLine()) != null) { 55 // 这里咱们用到了StringBuffer的append方法,这个比string的“+”要高效 56 sb.append(string + "/n"); 57 System.out.println(string); 58 } 59 // 注意这两个关闭的顺序 60 bufferedReader.close(); 61 reader.close(); 62 63 /* 64 * 完整写入文件 65 */ 66 Writer writer = new FileWriter("d:" + File.separator + "test3.txt"); 67 BufferedWriter bw = new BufferedWriter(writer); 68 // 注意这里调用了toString方法 69 bw.write(sb.toString()); 70 // 注意这两个关闭的顺序 71 bw.close(); 72 writer.close(); 73 74 } catch (FileNotFoundException e) { 75 e.printStackTrace(); 76 } catch (IOException e) { 77 e.printStackTrace(); 78 } 79 } 80 81 }
在整个io包中,惟一表示与文件自己有关的类就是File类。使用File类能够进行建立或删除文件以及文件夹等经常使用操做。安全
要想使用File类,则首先要观察File类的构造方法,此类的经常使用构造方法以下所示:网络
1 public File(String pathname): 2 实例化File类的时候,必须设置好路径。直接根据路径找到文件。
File类中的主要方法和常量:多线程
一、public static final String pathSeparator 常量 表示路径的分隔符(windows是:";") 二、public static final String separator 常量 表示路径的分隔符(windows是:"\") 三、public File(String pathname) 构造方法 建立File类对象,传入完整路径 四、public boolean createNewFile() throws IOException 普通方法 建立新文件 五、public boolean delete() 普通方法 删除文件 六、public boolean exists() 普通方法 判断文件是否存在 七、public boolean isDirectory() 判断给定的路径是不是一个目录 八、public long length() 普通方法 返回文件的大小 九、public String[] list() 普通方法 列出指定目录的所有内容,只是列出了名称 十、public File[] listFiles() 普通方法 列出指定目录的所有内容,会列出路径 十一、public boolean mkdir() 普通方法 建立一个目录 十二、public boolean removeTo(File dest)为已有的文件重命名
该对象并非流体系中的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),经过内部的指针来操做字符数组中的数据。该对象特色:并发
1、 该对象只能操做文件,因此构造函数接收两种类型的参数:a.字符串文件路径;b.File对象。
二、 该对象既能够对文件进行读操做,也能进行写操做,在进行对象实例化时可指定操做模式(r,rw)
注意:该对象在实例化时,若是要操做的文件不存在,会自动建立;若是文件存在,写数据未指定位置,会从头开始写,即覆盖原有的内容。能够用于多线程下载或多个线程同时写数据到文件。
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为相似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操做从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。若是随机访问文件以读取/写入模式建立,则输出操做也可用;输出操做从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾以后的输出操做致使该数组扩展。该文件指针(实现数组随机读写)能够经过 getFilePointer 方法读取,并经过 seek 方法设置。一般,若是此类中的全部读取例程在读取所需数量的字节以前已到达文件末尾,则抛出 EOFException(是一种 IOException)。若是因为某些缘由没法读取任何字节,而不是在读取所需数量的字节以前已到达文件末尾,则抛出 IOException,而不是 EOFException。须要特别指出的是,若是流已被关闭,则可能抛出 IOException。
RandomAccessFile类包含了一个记录指针,用以标识当前读写处的位置,当程序新建立一个RandomAccessFile对象时,该对象的文件记录指针位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会向后移动n个字节。除此以外,RandomAccessFile能够自由的移动记录指针,便可以向前移动,也能够向后移动。RandomAccessFile包含了如下两个方法来操做文件的记录指针.
1 long getFilePointer(); 返回文件记录指针的当前位置 2 void seek(long pos); 将文件记录指针定位到pos位置
RandomAccessFile便可以读文件,也能够写,因此它即包含了彻底相似于InputStream的3个read()方法,其用法和InputStream的3个read()方法彻底同样;也包含了彻底相似于OutputStream的3个write()方法,其用法和OutputStream的3个Writer()方法彻底同样。除此以外,RandomAccessFile还包含了一系类的readXXX()和writeXXX()方法来完成输入和输出。
RandomAccessFile有两个构造器,其实这两个构造器基本相同,只是指定文件的形式不一样而已,一个使用String参数来指定文件名,一个使用File参数来指定文件自己。除此以外,建立RandomAccessFile对象还须要指定一个mode参数。该参数指定RandomAccessFile的访问模式,有如下4个值:
1 “r” 以只读方式来打开指定文件夹。若是试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。 2 “rw” 以读,写方式打开指定文件。若是该文件尚不存在,则试图建立该文件。 3 “rws” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容或元数据的每一个更新都同步写入到底层设备。 4 “rwd” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容每一个更新都同步写入到底层设备。
Java中I/O操做主要是指使用Java进行输入,输出操做,Java全部的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。
数据流是一串接二连三的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另外一端看到的是一股接二连三的水流。数据写入程序能够是一段、一段地向数据流管道中写入数据,这些数据段会按前后顺序造成一个长的数据流。对数据读取程序来讲,看不到数据流在写入时的分段状况,每次能够读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据(不能随机读取)。无论写入时是将数据分屡次写入,仍是做为一个总体一次写入,读取时的效果都是彻底同样的。流序列中的数据既能够是未经加工的原始二进制数据,也能够是经必定编码处理后符合某种格式规定的特定数据。所以Java中的流分为两种: 1) 字节流:数据流中最小的数据单元是字节 2) 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。
简而言之:数据流是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
当程序须要读取数据的时候,就会创建一个通向数据源的链接,这个数据源能够是文件,内存,或是网络链接。相似的,当程序须要写入数据的时候,就会创建一个通向目的地的链接。
Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。
Java I/O主要包括以下3层次:
流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等
非流式部分——如:File类、RandomAccessFile类和FileDescriptor等类
其余——文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操做系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。
I/O流:java.io包里有4个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流。其余各类各样的流都是由这4个派生出来的。
1 按来源/去向分类: 2 File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter 3 byte[]:ByteArrayInputStream, ByteArrayOutputStream 4 Char[]: CharArrayReader, CharArrayWriter 5 String: StringBufferInputStream, StringReader, StringWriter 6 7 网络数据流:InputStream, OutputStream, Reader, Writer 8 InputStream:InputStream 为字节输入流,它自己为一个抽象类,必须依靠其子类实现各类功能,此抽象类是表示字节输入流的全部类的超类。 继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit); 9 InputStream是输入字节数据用的类,因此InputStream类提供了3种重载的read方法,Inputstream类中的经常使用方法: 10 public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工做结束。 11 public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法其实是调用下一个方法实现的 12 public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。 13 public int available( ):返回输入流中能够读取的字节数。注意:若输入阻塞,当前线程将被挂起,若是InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用。 14 public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取。 15 public int close( ) :使用完后,必须对咱们打开的流进行关闭。 16 17 来看看几种不一样的InputStream: 18 FileInputStream把一个文件做为InputStream,实现对文件的读取操做 19 ByteArrayInputStream:把内存中的一个缓冲区做为InputStream使用 20 StringBufferInputStream:把一个String对象做为InputStream 21 PipedInputStream:实现了pipe的概念,主要在线程中使用 22 SequenceInputStream:把多个InputStream合并为一个InputStream 23 24 OutputStream 25 OutputStream提供了3个write方法来作数据的输出,这个是和InputStream是相对应的。 26 public void write(byte b[ ]):将参数b中的字节写到输出流。 27 public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。 28 public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。 29 public void flush( ) : 将数据缓冲区中数据所有输出,并清空缓冲区。 30 public void close( ) : 关闭输出流并释放与流相关的系统资源。 31 32 几种不一样的OutputStream: 33 ByteArrayOutputStream:把信息存入内存中的一个缓冲区中 34 FileOutputStream:把信息存入文件中 35 PipedOutputStream:实现了pipe的概念,主要在线程中使用 36 SequenceOutputStream:把多个OutStream合并为一个OutStream 37 Reader和InputStream相似;Writer和OutputStream相似。 38 有两个须要注意的: 39 InputStreamReader : 从输入流读取字节,在将它们转换成字符。 40 BufferedReader :接受Reader对象做为参数,并对其添加字符缓冲器,使用readline()方法能够读取一行。 41 42 如何选择I/O流 43 肯定是输入仍是输出 44 输入:输入流 InputStream,Reader 45 输出:输出流 OutputStream,Writer 46 47 明确操做的数据对象是不是纯文本 48 是:字符流 Reader,Writer 49 否:字节流 InputStream,OutputStream 50 51 明确具体的设备。 52 文件: 53 读:FileInputStream,, FileReader, 54 写:FileOutputStream,FileWriter 55 数组: 56 byte[ ]:ByteArrayInputStream, ByteArrayOutputStream 57 char[ ]:CharArrayReader, CharArrayWriter 58 59 String: 60 StringBufferInputStream(已过期,由于其只能用于String的每一个字符都是8位的字符串), StringReader, StringWriter 61 Socket流 62 键盘:用System.in(是一个InputStream对象)读取,用System.out(是一个OutputStream对象)打印 63 是否须要转换流 64 是,就使用转换流,从Stream转化为Reader、Writer:InputStreamReader,OutputStreamWriter 65 是否须要缓冲提升效率 66 是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWriter
是否须要格式化输出
下面再看一个例子:
1 package com.io.test; 2 3 import java.io.BufferedReader; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 import java.io.InputStreamReader; 11 import java.util.Scanner; 12 13 public class IOTest { 14 15 public static void main(String[] args) throws Exception{ 16 System.out.println("将标准输入(键盘输入)显示到标准输出(显示器),支持字符:"); 17 inputAndOutput(); 18 19 System.out.println("将IOTest.java的内容打印到显示器1:"); 20 inputFileAndShow1(); 21 22 System.out.println("将IOTest.java的内容打印到显示器2:"); 23 inputFileAndShow2(); 24 25 System.out.println("将文件A的内容拷贝到文件B:"); 26 copyAToB(); 27 28 System.out.println("将标准输入的内容写入文件:"); 29 systemToFile(); 30 } 31 32 33 //将标准输入(键盘输入)显示到标准输出(显示器),支持字符。 34 public static void inputAndOutput(){ 35 char ch; 36 BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); //将字节流转为字符流,带缓冲 37 try { 38 while ((ch = (char) in.read()) != -1 && ch != 'q'){ 39 System.out.print(ch); 40 } 41 } catch (IOException e) { 42 e.printStackTrace(); 43 } 44 } 45 46 47 //将IOTest.java的内容打印到显示器 48 public static void inputFileAndShow1() throws IOException{ 49 BufferedReader in = new BufferedReader(new FileReader("IOTest.java")); 50 String s; 51 try { 52 while ((s = in.readLine()) != null){ 53 System.out.println(s); 54 } 55 in.close(); 56 } catch (IOException e) { 57 e.printStackTrace(); 58 } 59 } 60 public static void inputFileAndShow2() throws FileNotFoundException{ 61 FileReader in = new FileReader("IOTest.java"); 62 int b; 63 try { 64 while ((b = in.read()) != -1){ 65 System.out.print((char)b); 66 } 67 in.close(); 68 } catch (IOException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 74 //将文件A的内容拷贝到文件B 75 public static void copyAToB() throws IOException{ 76 FileInputStream in = new FileInputStream("IOTest.java"); 77 FileOutputStream out = new FileOutputStream("copy.txt"); 78 int b; 79 while ((b = in.read()) != -1){ 80 out.write(b); 81 } 82 out.flush(); 83 in.close(); 84 out.close(); 85 } 86 87 88 //将标准输入的内容写入文件 89 public static void systemToFile() throws IOException{ 90 Scanner in = new Scanner(System.in); 91 FileWriter out = new FileWriter("systemIn.log"); 92 String s; 93 while (!(s = in.nextLine()).equals("Q")){ 94 out.write(s + "\n"); 95 } 96 out.flush(); 97 out.close(); 98 in.close(); 99 } 100 101 }
对于文件的操做,本质上就是对内存数据的读写而且解析和表示的问题,必不可少的会牵扯到管道,缓存等机制,在java的IO之中使用了装饰器模式很是的优美和简洁,值得深刻思考。
参考文献: http://www.javashuo.com/article/p-hroebtqt-bo.html
http://www.cnblogs.com/lovesqcc/p/5201929.html
https://blog.csdn.net/Named_BaoYong/article/details/74626789