大话 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, 将内容写入内存的某缓冲区;
FileInputStream, 用于从文件中读取数据;
FileOutputStream, 用于将内容写入文件。
这些实现都是基于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读取,大大加强磁盘读写效率;
虽然看上去操作和FileInputStream/FileOutputStream没差,但BufferedInputStream/BufferedOutputStream内部其实并不是一次读一个byte,而是每次读得时候预取好多,下次再读可直接从内存得到,大大提升效率。
DataInputStream/DataOutputStream 则可以让你直接按照字符来读写,而不是基于字节,这样我们就不用关心这个整数在这台机子上到底占用几个字节了,InputStream.readInt()直接读取到一个int.
貌似所有问题都解决了,文件copy用字节流,读取字符用DataInputStream, DataOutputStream。
但是DataOutputStream的问题是它写的文件不可读;而且读得顺序必须和写的顺序一模一样,比较适合于固定格式的输入,输出。而我们90%的情况下其实都是在读取String。
Java1.1 发现了这些问题,于是乎对IO又有了一个革命性的改写,我们下回分析
如果你也有上面的困惑,那么不妨和我一起究一究为什么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后台邮箱格式验证 博客分类: 随笔java Java格式验证
下一篇: 文本文件读取 -- 消除样板式代码工具类 博客分类: java BufferedReaderio字符流文本文件读取
推荐阅读
-
按目录递归清理文件每行开头结尾的空白 博客分类: java综合 java清理文件IO
-
大话 Java IO(一) 博客分类: java javaIO
-
Java NIO使用及原理分析 (一) 博客分类: Java基础 javaNIOIO
-
Java NIO and IO 博客分类: Java javaiochannelstreamNIO
-
JAVA Exception 博客分类: JAVA javaException拒绝访问io
-
java日期计算:某日期加一个月 博客分类: JavaSE java日期date日期加月Java日期加一个月
-
关于JAVA - gc的工作(一) 博客分类: JDK//Demo javagc垃圾回收jvm虚拟机
-
Java IO流总结 博客分类: Java IO和NIO javaio
-
一篇文章了解架构师的核心技能 博客分类: 架构师 架构师程序员java
-
写一个方法,实现字符串的反转,如:输入abc,输出cba 博客分类: Java 字符串反转