大话 Java IO(一)

相信好多人都对java的io类感到抓狂,一个简单的输入输出都要创建n多个对象,完全搞不懂为什么要一层套一层。
如果你也有上面的困惑,那么不妨和我一起究一究为什么Java IO长这样。

我猜Java IO的创建过程是这样的:
先参考一下其它已有编程语言都是怎么创建IO类的。IO输入/输出源各种各样,有键盘输入,有文件输入,有网络输入,有内存输入等等。为了屏蔽这些不同,编程语言都引入了“流"的概念,说白了所有这些IO操作就是字节流流来流去。
这些流又可以分为两大类,输入流和输出流。

于是乎Java 1.0 便也照猫画虎提供了两大类IO类库, InputStream, OutputStream.
有了这两个基类以后,创建者认为基本IO问题就搞定了,如果你需要输入那你就继承InputStream, 如果你需要输出那么你就继承OutputStream.
Java 提供了几种常用输入输出的实现。我们可以直接使用
ByteArrayInputStream 用于从内存的缓冲区读取数据, 如一个线程将数据写入某个缓冲区,另一个线程可以用ByteArrayInputStream读取该缓冲区的数据;
ByteArrayOutputStream, 将内容写入内存的某缓冲区;
public static void main(String[] args) throws IOException{
		ByteArrayInputStream input = new ByteArrayInputStream("memory input test".getBytes());
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		int data = 0;
		while((data = input.read()) != -1){
			output.write(data);
		}
		
		System.out.print(output.toString());
		input.close();
		output.close();
	}

FileInputStream, 用于从文件中读取数据;
FileOutputStream, 用于将内容写入文件。
public static void readFile(String inFile, String outFile) throws IOException{
		try {
			byte[] data = new byte[1];
			FileOutputStream output = new FileOutputStream(outFile);
			FileInputStream input = new FileInputStream(new File(inFile));
			try {
				while(input.available() > 0){
					input.read(data);
					output.write(data);
				}
			}  finally{
				input.close();
				output.close();
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}

这些实现都是基于InputStream/OutputStream 的,都是以字节为单位读取/写入数据。
读起来确实难用,byte[]设大了吧,如果数据没呢么多久会写好多null,设小了吧效率不高。
在使用过程中人们就发现很多问题,如文件读取很慢,因为磁盘读取本来就很耗时,以字节为单位和磁盘交互,每次只读取一个字节效率自然太慢了;
如果只是单纯的都一个文件然后写到另一个文件,这个方法还算能用。
但是当要解析里面的数据时就有问题了,不同的数据类型所占用的存储空间也不同,如在某些机子上int占4个byte, double占8个byte, 而在另一些机器上可能所占空间又不同,当我需要读取一个int时我是读取4个byte呢,还是5个byte呢?

此时想解决这个问题有两种方法,一种是将前面的推倒重来,放弃之前的类再写两个新的替代它们;另一个方法就是使用装饰折模式在前面基础上进行修补。
Java选择了后者,它可以简单的装饰一下原有的类,然后和原有类提供完全一样的接口,只是对它的功能稍加装饰,满足现在的需求。
于是针对上面两个问题就有了BufferedInputStream, DataInputStream, BufferedOutputStream, DataOutputStream 来对FileInputStream/FileOutputStream进行装饰。
BufferedInputStream/BufferedOutputStream 就可以使你在读写文件的时候一次读写一批数据,而不是一个byte一个byte读取,大大加强磁盘读写效率;
public static void BufferTest(String inputName, String outputName) throws IOException{
		try {
			byte[] data = new byte[1];
			BufferedInputStream input = new BufferedInputStream(new FileInputStream(new File(inputName)));
			BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(outputName));
			try {
				while(input.available() > 0){
					input.read(data);
					output.write(data);
				}
			}  finally{
				input.close();
				output.close();
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

虽然看上去操作和FileInputStream/FileOutputStream没差,但BufferedInputStream/BufferedOutputStream内部其实并不是一次读一个byte,而是每次读得时候预取好多,下次再读可直接从内存得到,大大提升效率。

DataInputStream/DataOutputStream 则可以让你直接按照字符来读写,而不是基于字节,这样我们就不用关心这个整数在这台机子上到底占用几个字节了,InputStream.readInt()直接读取到一个int.
public static void dataInputOutputTest(String fileName) throws FileNotFoundException{
		DataInputStream input = null;
		DataOutputStream output = new DataOutputStream(new FileOutputStream(fileName));
		int a = 10;
		float b = 12;
		String c = "test";
		
		try {
			output.writeInt(a);
			output.writeFloat(b);
			output.writeUTF(c);
			output.flush();
			output.close();
			
			input = new DataInputStream(new FileInputStream(fileName));
			System.out.println(input.readInt());
			System.out.println(input.readFloat());
			System.out.println(input.readUTF());
			input.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


貌似所有问题都解决了,文件copy用字节流,读取字符用DataInputStream, DataOutputStream。
但是DataOutputStream的问题是它写的文件不可读;而且读得顺序必须和写的顺序一模一样,比较适合于固定格式的输入,输出。而我们90%的情况下其实都是在读取String。
Java1.1 发现了这些问题,于是乎对IO又有了一个革命性的改写,我们下回分析

猜你喜欢

转载自jaler.iteye.com/blog/2230395