在Java程序中,对于数据的输入/输出操作以"流" stream 的方式进行,stream的意思其实就是一个管道,连接数据和数据要到达的地方。需要声明的是,在这里所谓的输入和输出都是针对程序而言的。比如说一个程序从一个文件读数据,这叫输入。将程序中的计算结果传递给文件,叫做输出。
I/O的操作几乎都会涉及到异常,所以要搞清楚相关异常的分类,大小,便于捕捉和调试。
java提供的所有流的类型都位于包java.io内,分别继承以下四种抽象流类型:字节流:InputStream, OutputStream,一个字节一个字节读(8-bit)
字符流:Reader, Writer,一个字符(2个字节)一个字符读(1-bit)
1.InputStream---抽象类,字节输入流的所有类的超类
主要方法:
1) public abstract int read() throws IOException
从输入流中读取数据的下一个字节,返回0到255范围内的int字节值,以前这里没有太看懂,其实读取的是字节,也就是一个英文字母,但是最后要以Asic II码把它转换为int类型的数值返回。如果想要显示的话,那么就强制类型转换为char类型。
声明一下:换行符(\n)的码字为10
如果因为已经到达流末尾而没有可用的字节,则返回值为 -1(至于这个是怎么实现的,我还没有搞清楚)。子类必须提供此方法的一个实现。
返回值:下一个数据字节,即0到255的一个int值,如果已经到达流的末尾,则返回-1。当然,大部分情况下应该不会需要自己去实现,那么多已经实现的类,肯定有可以用到的。
2)public int read (byte[] b) throws IOException
从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。以整数的形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值-1。
将读取的第一个字节存储在元素b[0]中,下一个存储在b[1]中,依次类推。读取的字节数最多等于b的长度。
// 需要注意的是有些异常,记得使用try catch 形式
返回值:下一个数据字节,即0到255的一个int值,如果已经到达流的末尾,则返回-1。当然,大部分情况下应该不会需要自己去实现,那么多已经实现的类,肯定有可以用到的。
2)public int read (byte[] b) throws IOException
从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。以整数的形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值-1。
将读取的第一个字节存储在元素b[0]中,下一个存储在b[1]中,依次类推。读取的字节数最多等于b的长度。
// 需要注意的是有些异常,记得使用try catch 形式
为了避免内存泄漏,比如读到一半出错了,然后stream就一直开着,占用系统资源,无论如何都要在最后保证打开的streams被关闭掉。通常的做法就是在最后写一个finally块,判定stream对象是否不为null,如果是,那么调用其close方法。
3) public int read (byte[] b, int off, int len) throws IOException
将输入流中len个数据字节读入byte数组(实际读取的字节数可能小于该值)
将读取的第一个字节存储在元素b[off]中,下一个存储在b[off+1]中,依次类推。读取的字节数最多等于len。
其它的OutputStream,Reader, Writer都很类似,只不过可能有一些特殊的方法可以用,具体用的时候查看API就行。
3) public int read (byte[] b, int off, int len) throws IOException
将输入流中len个数据字节读入byte数组(实际读取的字节数可能小于该值)
将读取的第一个字节存储在元素b[off]中,下一个存储在b[off+1]中,依次类推。读取的字节数最多等于len。
其它的OutputStream,Reader, Writer都很类似,只不过可能有一些特殊的方法可以用,具体用的时候查看API就行。
2. OutputStream---抽象类,字节输出流的所有类的超类
其实读和写的操作基本上都是相互对应的。
需要注意的一点就是,有些共同使用的文件或者数据,需要在读或者写的时候进行同步,以免发生数据的错乱。
3. Reader
其实最常用的就是BufferReader,因为它有个方法叫做readLine(),直接读取一行,非常方便。
4. Writer
其中有一个write(String string)方法很有用,其实是调用了String类里面的toCharArray()方法将他们都转换成字符然后再写出去。
5.转换流,缓冲流,数据流
不想说太多细节的东西,前面4个抽象类的东西都很基础,比如说一个字节一个字节地读写,一个字符一个字符地读写。但是到了实际情况的时候就不一样了,这些方法可能并不方便。
1).首先这样读的话硬盘读写次数太多,损伤很大。所以呢,就出现了缓冲流,你可以先写到一个buffer里面,然后再读写,岂不是减少了硬盘的实际读写次数。这样就相当于一根管道插在文件上,然后再在这个管道外面包了一个更大的管道(或者说桶),等桶装满了再往外写。
2).第二个问题就是你本来定义了一个InputStream类型的对象,但是你发现你想一行一行的读,于是转换流的概念就出来了,你可以用转换流将其转换为Writer,它里面的一些方法就可以拿来用
3).第三个问题就是,读写字符串没问题,但是如果想读写原生数据的话怎么办呢,不可能一个个的先转换成字符再读写,太浪费空间而且效率很低,怎么解决呢,数据流的概念又出来了。无非就是在基础流对象的外面接一根数据流的管子,用它的方法读写就好。
4).关于编码问题
1).首先这样读的话硬盘读写次数太多,损伤很大。所以呢,就出现了缓冲流,你可以先写到一个buffer里面,然后再读写,岂不是减少了硬盘的实际读写次数。这样就相当于一根管道插在文件上,然后再在这个管道外面包了一个更大的管道(或者说桶),等桶装满了再往外写。
2).第二个问题就是你本来定义了一个InputStream类型的对象,但是你发现你想一行一行的读,于是转换流的概念就出来了,你可以用转换流将其转换为Writer,它里面的一些方法就可以拿来用
3).第三个问题就是,读写字符串没问题,但是如果想读写原生数据的话怎么办呢,不可能一个个的先转换成字符再读写,太浪费空间而且效率很低,怎么解决呢,数据流的概念又出来了。无非就是在基础流对象的外面接一根数据流的管子,用它的方法读写就好。
4).关于编码问题
首先我前一段时间遇到的一个问题就是,我想传输图片,却使用了BufferedReader的readline方法,导致了编码不正确,图片读不出来。
其实问题不仅仅是readline方法,而是字符流和字节流的选择,对于图片来说,必须保证每一个字节都能正确地得到解析。如果用字符流,有可能按照转义字符给解析了,导致读不出来。