欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

大话 Java IO(一) 博客分类: java javaIO 

程序员文章站 2024-03-16 19:12:04
...
相信好多人都对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又有了一个革命性的改写,我们下回分析
相关标签: java IO