深入分析​​Java​​ I/O 工作机制

egg

微博:​​http://weibo.com/xtfggef​


是Java及众多编程语言很重要的一块,同时很多程序的瓶颈和耗时操作也都在IO这块。所以能够很好的解决IO问题对提高程序性能有很大的帮助!本章我们将要系统的对Java IO做个分析,通过理论加实践,希望读者朋友们能真正彻底的理解并且掌握了它。本章系Java之美[从菜鸟到高手演变]系列之Java IO,通过本章的学习,读者朋友们能基本了解到关于IO的很多知识。日后加以理解、分析、在项目中实践,定能灵活运用!

一、简介

操作面临很多问题,信息量的巨大,网络的环境等等,因为IO不仅仅是对本地文件、目录的操作,有时对二进制流、还有一部分是网络方面的资源,所以多种原因直接造成IO操作无疑是耗时且复杂多变的。Java对IO的支持是个不断的演变过程,经过了很多的优化,直到JDK1.4以后,才趋于稳定,在JDK1.4中,加入了nio类,解决了很多性能问题,虽然我们有足够的理由不去了解关于Java IO以前的情况,但是为了学好现在的类,我们还是打算去研究下,通过掌握类的优化情况来彻底理解IO的机制!Java IO主要主要在java.io包下,分为四大块近80个类:

、基于字节操作的I/O接口:InputStream和OutputStream

、基于字符操作的I/O接口:Writer和Reader

、基于磁盘操作的I/O接口:File

、基于网络操作的I/O接口:Socket(不在java.io包下)

IO性能的无非就是两大因素:数据的格式及存储的方式,前两类主要是数据格式方面的,后两个类是存储方式方面的:本地和网络。所以策划好这两个方面的活动,有助于我们合理使用IO。

二、基于字节的I/O操作(InputStream和OutputStream)

我们先来看看类图:


Java中的IO(面试有时会问java io的问题)_Java

图1


Java中的IO(面试有时会问java io的问题)_System_02

图2

InputStream类,OutputStream留给大家自己去学习。InputStream类是个抽象类,里面核心的方法就是read()、read(byte b[])、read(byte b[], int off, int len),这三个方法是用于读取数据的底层的方法,他们可以用来读取一下这些类型的数据:

A. 字节数组

对象

C. 文件

D. 管道,从一端进入,从另一端输出

E. 流

资源

InputStream子类,因为InputStream是个处于顶层的类,用来处理各种数据源的类都继承了InputStream类,我们来看看这些类:

InputStream使用。

String转换成InputStream,内部实现用的是StringBuffer。

FileInputStream:从文件中读取数据。

:用于从管道中读取数据。

:将多个流对象转化成一个InputStream。

:装饰器类,为其它InputStream类提供功能。

IO操作的读者知道,我们很少单独使用哪个类来实现IO操作,平时都是几个类合起来使用,这其实体现了一种装饰器模式(详见:javascript:void(0))的思想,在后面的分析中我们会详细的分析。从上面的图1中我们可以看出,FilterInputStream虽说是Inputstream的子类,但它依然是BufferedInputStream、DataInputStream、LineNumberInputStream、PushbackInputStream类的父类,这四个类分别提供了最贴近我们程序员使用的方法,如:readInt() 、readInt()、readInt()等等。对于IO操作,不管是磁盘还是网络,最终都是对字节的操作,而我们平时写的程序都是字符形式的,所以在传输的过程中需要进行转换。在字符到字节的转换过程中,我们需要用到一个类:InputStreamReader。

三、基于字符的I/O操作(Writer和Reader)


Java中的IO(面试有时会问java io的问题)_System_03

图3


Java中的IO(面试有时会问java io的问题)_java_04

图4

和Reader操作的目的就是操作字符和不是字节,和InputStream和OutputStream配合增加IO效果。通过InputStreamReader和OutputStreamReader可以进行字节和字符的转换,设计Writer和Reader的目的是国际化,使IO操作支持16位的Unicode。我把它们单独的画出来,因为要是全画的话,太大了放不下,有兴趣的TX可以在rational rose中导入其带的JDK类图看看,很过瘾的!

四、基于磁盘的I/O操作(File)

五、基于网络的I/O操作(Socket)

六、NIO

四-六部分由于时间关系,还没有整理完,后续会补出来!



七、经典IO操作

、缓冲输入文件。



1. import java.io.BufferedReader;  
2. import java.io.FileReader;
3.
4. public class InputStreamTest {
5.
6. public static String read(String filename) throws Exception {
7. new BufferedReader(new FileReader(filename));
8. String s;
9. new StringBuffer();
10. while ((s = br.readLine()) != null) {
11. "\n");
12. }
13. br.close();
14. return sb.toString();
15. }
16.
17. public static void main(String[] args) throws Exception {
18. "src/InputStreamTest.java"));
19. }
20.  }

InputStreamTest.java文件,然后转换成字符串。输出就是将源文件原样输出。

、从内存中读取。



1. import java.io.StringReader;  
2.
3. public class MemoryInput {
4.
5. public static void main(String[] args) throws Exception {
6. new StringReader(
7. "src/MemoryInput.java"));
8. int c;
9. while ((c = in.read()) != -1)
10. char) c);
11. }
12.
13. }

返回的是int类型的数据,所以在输出语句中用char做了强类型转换。该程序将一个一个的输出字符。

、基本的文件输出。



1. import java.io.BufferedReader;  
2. import java.io.BufferedWriter;
3. import java.io.FileWriter;
4. import java.io.PrintWriter;
5. import java.io.StringReader;
6.
7. public class BasicFileOutput {
8.
9. static String file = "basie.out";
10.
11. public static void main(String[] args) throws Exception {
12. new BufferedReader(new StringReader(
13. "src/BasicFileOutput.java")));
14. new PrintWriter(new BufferedWriter(new FileWriter(
15. file)));
16. int lineCount = 1;
17. String s;
18. while ((s = in.readLine()) != null) {
19. ": " + s);
20. }
21. out.close();
22. System.out.println(InputStreamTest.read(file));
23. }
24. }


输出:

1: import java.io.BufferedReader;

2: import java.io.BufferedWriter;

3: import java.io.FileWriter;

、RandomAccessFile

被我们称为”自我独立的类”,因为它独立于我们前面说的IO类,与InputStream和OutputStream没什么关系,除了实现了DataOutput, DataInput两个接口外。所有方法都是重新编写,而且很多都是native方法,我们来看个例子,了解下这个类:

、管道流

八、标准I/O

System.in、System.out等。



1. public class StandardIO {  
2.
3. public static void main(String[] args) throws IOException {
4. new BufferedReader(new InputStreamReader(System.in));
5. String s;
6. while ((s = in.readLine()) != null && s.length() != 0)
7. System.out.println(s);
8. }
9. }


返回的是未经包装的InputStream对象,所以需要进行装饰,经InputStreamReader转换为Reader对象,放入BufferedReader的构造方法中。除此之外,System.out和System.err都是直接的PriintStream对象,可直接使用。我们也可以使用java.util包下的Scanner类来代替上述程序:



1. public class StandardIO {  
2.
3. public static void main(String[] args) throws IOException {
4. new Scanner(System.in);
5. String s;
6. while((s = in.next()) != null && s.length() != 0){
7. System.out.println(s);
8. }
9. }
10. }


九、性能分析及总结

、一个文件读写工具类。



1. import java.io.BufferedReader;  
2. import java.io.File;
3. import java.io.FileReader;
4. import java.io.IOException;
5. import java.io.PrintWriter;
6. import java.util.ArrayList;
7. import java.util.Arrays;
8.
9. /**
10. * 一个非常实用的文件操作类 . 2012-12-19
11. *
12. * @author Bruce Eckel , edited by erqing
13. *
14. */
15. public class TextFile extends ArrayList<String> {
16.
17. private static final long serialVersionUID = -1942855619975438512L;
18.
19. // Read a file as a String
20. public static String read(String filename) {
21. new StringBuilder();
22. try {
23. new BufferedReader(new FileReader(new File(
24. filename).getAbsoluteFile()));
25. String s;
26. try {
27. while ((s = in.readLine()) != null) {
28. sb.append(s);
29. "\n");
30. }
31. finally {
32. in.close();
33. }
34.
35. catch (IOException e) {
36. throw new RuntimeException(e);
37. }
38. return sb.toString();
39. }
40.
41. // Write a single file in one method call
42. public static void write(String fileName, String text) {
43. try {
44. new PrintWriter(
45. new File(fileName).getAbsoluteFile());
46. try {
47. out.print(text);
48. finally {
49. out.close();
50. }
51. catch (IOException e) {
52. throw new RuntimeException(e);
53. }
54. }
55.
56. // Read a file,spilt by any regular expression
57. public TextFile(String fileName, String splitter) {
58. super(Arrays.asList(read(fileName).split(splitter)));
59. if (get(0).equals(""))
60. 0);
61. }
62.
63. // Normally read by lines
64. public TextFile(String fileName) {
65. this(fileName, "\n");
66. }
67.
68. public void write(String fileName) {
69. try {
70. new PrintWriter(
71. new File(fileName).getAbsoluteFile());
72. try {
73. for (String item : this)
74. out.println(item);
75. finally {
76. out.close();
77. }
78.
79. catch (IOException e) {
80. throw new RuntimeException(e);
81. }
82.
83. }
84.
85. // test,I have generated a file named data.d at the root
86. public static void main(String[] args) {
87.
88. /* read() test */
89. "data.d")); // testing is OK!
90.
91. /* write() test */
92. "out.d", "helloworld\negg"); // testing is OK!
93.
94. /* constractor test */
95. new TextFile("data.d"); // testing is OK!
96.
97. }
98.
99. }

、读取二进制文件。


1. import java.io.BufferedInputStream;  
2. import java.io.File;
3. import java.io.FileInputStream;
4. import java.io.IOException;
5.
6. /**
7. * to read the binary file
8. *
9. * @author erqing
10. *
11. */
12. public class BinaryFile {
13.
14. /* the parametre is a file */
15. public static byte[] read(File file) throws IOException {
16. new BufferedInputStream(new FileInputStream(
17. file));
18. try {
19. byte[] data = new byte[bf.available()];
20. bf.read(data);
21. return data;
22. finally {
23. bf.close();
24. }
25. }
26.
27. /* the param is the path of a file */
28. public static byte[] read(String file) throws IOException {
29. return read(new File(file).getAbsoluteFile());
30. }
31. }

本章节未完,因为最近较忙,所以先发出一部分,后续会将缺少的补齐!