概述
对于使用过java提供了I/O来说的程序员来说,可能对其提供的api又爱又恨,爱的是java本身封装了很多功能强大的输入输出流给开发者使用,恨的是一开始开发者可能会对Java I/O系统提供了如此多的类而不知所措。本文就简要的介绍一下java的i/o,以供自己复习使用。
特殊的File 类
File类是在java I/O中常用的一个工具类,它可以帮助我们处理文件和文件目录的问题。其实File这个名字有一定的误导性,它既可以表示文件,又可以表示目录。如果其表示的是一个目录,我们就可以调用list()方法来返回文件集合的名称数组或者调用listFile()方法来返回所有的文件。File类还提供了一些常用的属性,比如File.separator表示获取所在系统的路径分隔符、File.pathSeparator表示路径之间的分隔符。但是需要注意的是:File类是针对文件或者目录的相关操作,例如删除、创建、判断文件或者目录是否存在等,但是只通过File类无法访问文件的具体内容,既只通过File既不能从文件中读取数据,也不能写入数据。这里只列出一个使用File类的demo,是有一次面试的面试题,列出指定目录下所有的内容。代码如下:
package com.vino.nio; import java.io.File; public class FileDemo { public static void main(String[] args) { String fileName = "D:" + File.separator; File file = new File(fileName); printAllFile(file); } // 打印一个文件下所有的文件,包括目录下的文件 public static void printAllFile(File file) { if (file != null) { if (file.isDirectory()) { File[] fileArray = file.listFiles(); if (fileArray != null) { for (int i = 0; i < fileArray.length; i++) { printAllFile(fileArray[i]); } } } else { System.out.println(file); } } } }
正菜开始
Java类库的I/O类分为输入和输出两部分。通过继承,任何自InputStream或Reader派生而来的类都含有read()这个基本方法,用于读取单个字节或者字节数组。同样,任何自OutputStream或Writer派生儿来的类都含有名为write()的基本方法,用于写单个字节或者字节数组。通常,我们很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所提供的功能。
四种抽象类见下图所示:
字节流 | 字符流 | |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
这里先介绍常用的文件流:
文件流是指那些主要用于从文件操作数据源中的流,主要有4种,分别如下所示:
FileInputStream vs FileOutputStream vs FileReader vs FileWriter
FileInputStream:
- fis.read() 从此输入流中读取一个数据字节。返回:下一个数据字节;如果已到达文件末尾,则返回 -1。 ;
- fis.read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。返回读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
Tips:在Unicode编码中,一个英文字符是用一个字节编码的,一个中文字符是2个字节编码的。所以用字节流读取中文时,肯定会出问题。
FileOutputStream:
常用的初始化该类的方式如下:
- OutputStream ops = new FileOutputStream(File f);
- OutputStream ops = new FileOutputStream(File f, boolean append);
第二种表示是否是追加写。默认情况下,是不追加写的。
byte[] b = str.getBytes();
- fos.write(b) 向此输出流中写入b.lenth个字节。
- fos.write(b[i]) 向此输出流中写入b[i],两者都返回void。
用字节文件输出流往文件中写入的中文字符没有乱码,因为程序先把中文字符转成了字节数组然后再往文件里面写。 另外,向输出流写入数据,还需要在写入操作完成后,调用flush()方法来强制写出所有缓冲的数据。调用close()的时候会调用flush()方法,所以调用close()的时候就相当于调用了flush()方法。
FileReader:
- fr.read(); // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。
- fr.read(char[] c);//将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。
FileWriter:
同理与FileOutputStream,只是处理的对象是字符流。这里可以直接输入字符串,而不需要你将字符串转化为字节数组。
Writer out =new FileWriter(f); String str="hello"; out.write(str); out.close();
Tips:一般来说:FileInputStream vs FileOutputStream类用来操作二进制文件比较合适,如图片,声音,视频等。而FileReader vs FileWriter类用来操作文本比较合适。
转换流:
整个IO类中除了字节流和字符流还包括字节和字符转换流。
OutputStreramWriter将输出的字符流转化为字节流
InputStreamReader将输入的字节流转换为字符流
但是不管如何操作,最后都是以字节的形式保存在文件中的。
缓存流:
缓冲流都是过滤流,就是说缓冲流并不直接操作数据源,而是对直接操作数据源的节点流的一个包装。当使用过滤流的过程中,当关闭过滤流,它会自动关闭它所包装的节点流。常见的缓存流有BufferedReader。
BufferedReader只能接受字符流的缓冲区,因为每一个中文需要占据两个字节,所以需要将System.in这个字节输入流变为字符输入流。常见的用法如下:
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
Scanner:
还有一个不得不提到的类是:Scanner。
其实在Java里头,我们比较常用的是采用Scanner类来进行数据的输入。具体的用法见下:
package com.vino.nio; import java.util.Scanner; public class ScannerDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 读一个整数 int temp = sc.nextInt(); System.out.println(temp); // 读取浮点数 float flo = sc.nextFloat(); System.out.println(flo); // 读取字符 // ...等等的,都是一些太基础的,就不师范了。 } }
总结:
本文只是对java i/o的一个简单的总结,涉及到的主要是4中常用的文件流、转换流、缓存流的用法,对于流的高级的用法没有涉及到,博主在日常的开发过程中也没有使用到。另外还要指出的是,本文没有涉及到java的nio,在以后会补充上来。
参考:http://www.cnblogs.com/rollenholt/archive/2011/09/11/2173787.html