<Java> I和O
一、前言
Java I/O这块是学好java必须了解的,以前都是只会用几个简单的类来读读文件啦,写写文件而已,对于新手来说,这个java的I/O实在是太庞大复杂了,而且还涉及到好几种设计模式,尽管如此但是我觉得很有必要多了解一点。
二、为什么要I/O?
I/O是任何一门编程都要遇到的问题,可以说是我们和人机交互的核心。尤其是现在大数据技术的盛行,I/O的问题很容易成为应用程序的性能瓶颈,Java在这方面做的还算不错,尤其是在1.4版本之后引入了NIO,大大提升了I/O性能。
三、什么是I/O?
- 基于字节
- 基于字符
- 基于磁盘
- 基于网络
Java的I/O操作类全在java.io下,有80左右个类,基本可以分成上面四种。前两中是传输方式,后两者是传输内容。就是说如果我们需要拿到另外一个地方的某个东西,那么去执行的人只需要知道两个信息:什么东西和怎么带过来,就够了。下面我们分开来讲:
1.传输方式
我们要知道不管是磁盘还是网络传输,传输过程中最小的单位都是字节,而为什么有字符接口呢,是因为我们的程序中经常操作的都是字符形式,就像我们现实中经常用到的杯子,桌子它们都是原子构成的,但是我们不会去用原子,而用的是杯子。而且我们要注意字符和字节转换时候的编码问题。一般来说,在纯文本都数据传输中,我们才会优先考虑字符流,否则都会用字节流。
try{ StringBuffer str = new StringBuffer(); char[] buf = new char[1024]; FileReader f = new FileReader("file"); while(f.read(buf) > 0){ str.append(buf); } str.toString(); }catch(IOException e){
}
我们一般不会显示的将字符转成字节来存储,如上所示的FileReader就是继承了InputStreamReader类,实际上是读取文件流,然后通过StreamDecoder解码策划那个char,只不过这里的解码字符集是默认的字符集。
2.传输内容
1.磁盘I/O
数据在磁盘中唯一最小描述就是文件,也就是说应用程序只能通过文件来操作磁盘上的数据,文件也是操作系统和磁盘驱动器交互的最小单元。我们在java中经常用到File。其实不一定代表这是一个真实存在的文件对象,当你指定一个路径描述符时,它就返回一个代表这个路径的虚拟对象,这可能是一个真实存在的文件,也可能是文件目录。为什么要这样设计:因为通常我们并不关心则个文件是否真的存在而是对这个文件如何操作。
2.java序列化
java序列化就是将一个对象转化成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的,需要持久化,对象必须继承Serializable接口。反序列化则是相反的过程,将这个字节数据再重新构造成对象,我们知道反序列化需要原始类为模版,才能将这个对象还原(这说明序列化后的数据不如编译后的class文件那么完整)。如果序列化和反序列化都是在java环境下,那么这个功能是比较实用的,但是如果是多语言环境下,很难用其他语言还原出结果,在这种情况下,我们还是应该用通用的数据结构,比如xml、json。
3.网络I/O
首先我们要清楚TCP/IP协议,关于这个我们以后再写一篇来说。最后我们来了解建立连接后如何传输。不过是磁盘还是网络传输,数据在写入OutputSteam或InputSteam读取时都有可能阻塞,一旦发生这种情况,线程就失去cpu使用权,这对性能是不可容忍的。所以需要NIO。
四、两个设计模式
Java的I/O类中用到两个非常重要的设计模式
1.适配器模式
InputSteamReader和OutputSteamWriter分别继承了Reader和Writer接口,但是要创建他们的对象必须在构造函数中传如一个InputSteam和OutputSteam的实例,从而将入的适配到Reader和Writer。显然这里InputSteamReader和OutputSteamWriter就是适配器。
2.装饰者模式
比如说,InputSteam是一个抽象的输入流类,FileInputSteam是它的具体子类并增加好许多其他功能(这只是普通的类继承),而FilterInputSteam就是装饰器角色,它实现了 InputSteam的所有接口,并持有InputSteam的对象实例引用。BufferedInputSteam就是具体的装饰器实现者,负责实现装饰器角色定义的功能(使得InputSteam读取的数据保存内存中,而提高读取的性能),同样的还有LineNumberInputSteam(提高了按行读取的能力)
五、实例演示
//以行为单位从文件读取数据 BufferedReader in = new BufferedReader(new FileReader("F:\\yl0822\\TestIO.java")); StringBuffer s, s2 = new StringBuffer(); while((s = in.readLine()) != null){
s2.append(s);
} in.close(); //控制台的输入 BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter a line:"); System.out.println(stdin.readLine()); //从一个String对象中读取数据 StringReader in2 = new StringReader(s2); int c; while((c = in2.read()) != -1){
System.out.println((char)c);
} in2.close(); //从内存取出格式化输入 try{ DataInputStream in3 = new DataInputStream(new ByteArrayInputStream(s2.getBytes())); while(true){ System.out.println((char)in3.readByte()); } catch(IOException e){ System.out.println("End of stream"); } //输出到文件 try{ BufferedReader in4 = new BufferedReader(new StringReader(s2)); PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("F:\\yl0822\\ TestIO.out"))); int lineCount = 1; while((s = in4.readLine()) != null){ out1.println(lineCount++ + ":" + s); out1.close(); in4.close(); } catch(EOFException ex){ System.out.println("End of stream"); } //数据的存储和恢复 try{ DataOutputStream out2 = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("F:\\yl0822\\Data.txt"))); out2.writeDouble(3.1415926); out2.writeChars("\nThas was pi:writeChars\n"); out2.writeBytes("Thas was pi:writeByte\n"); out2.close(); DataInputStream in5 = new DataInputStream(new BufferedInputStream(new FileInputStream("F:\\yl0822\\Data.txt"))); BufferedReader in5br = new BufferedReader(new InputStreamReader(in5)); System.out.println(in5.readDouble()); System.out.println(in5br.readLine()); System.out.println(in5br.readLine()); } catch(EOFException e){ System.out.println("End of stream"); }
六、关系图
七、忠告
任何和I/O有关的操作都要注意关闭流!!!