程序在运行期间,可能需要从外部的存储媒介或其他程序中读入所需的数据,这就需要使用输入流。输入流的指向称为它的源,程序通过输入流读取源中的数据。另一方面,程序处理数据后,可能需要将处理的结果写入到永久的存储媒介中或传送给其他的应用程序,这就需要用输出流。输出流的指向称为它的目的地,虽然I/O流经常与磁盘文件存取有关,但是源和目的地也可以是键盘、内存或显示器窗口。
java.io包(I/O流库)提供大量的流类,所有输入流都是抽象类InputStream(字节输入流)或抽象类Reader(字符输入流)的子类,而所有输出流都是抽象类OutputSteam(字节输出流)或抽象类Writer(字符输出流)的子类。
File类的对象主要用来获取文件本身的一些信息,例如文件目录、文件长度或文件读写权限等,不涉及对文件的读写操作。
创建一个File对象的构造方法有3个:
其中,filename是文件名字,directoryPath是文件的路径,dir为一个目录。使用File(String filename)创建文件时,该文件被认为与当前应用程序在同一目录中。
经常使用File类的下列方法获取文件本身的一些信息。
File对象调用方法public boolean mkdir() 创建一个目录,成功返回true。
如果File对象是一个目录,那么该对象调用下述方法列出该目录下的文件和子目录。
public String[] list()
public File[] listFiles()
返回目录下的所有文件。
有时需要列出目录下的指定类型的文件,比如.java、.txt等文件,可以使用File类的下述两个方法,列出指定类型的文件。
public String[] list(FilenameFilter obj)
public File[] listFiles(FIlenameFilter obj)
上述两个方法的参数FilenameFilter是一个接口,该接口有一个方法:
public boolean accept(File dir,String name);
File对象dirFile调用list方法时,需向该方法传递一个实现FilenameFilter接口的对象,list方法执行时,参数obj不断回调接口方法accept(File dir,String name),该方法中的参数dir为调用list的当前目录dirFile,参数name被实例化为dirFile目录中的一个文件名,返回true时,list方法就将名字为name的文件存放到返回的数组中。
当使用File类创建一个文件对象后,
File file = new File("C:\\myletter","letter.txt");
如果C:\myletter目录中没有letter.txt文件,文件对象file调用方法
public boolean createNewFile();
可以在C:\myletter目录建立一个letter.txt文件。文件对象调用方法public boolean delete()可以删除当前文件。
file.delete();
使用输入流通常包括4个基本步骤:
如果对文件读取需求比较简单,可以用FileInputStream类(文件字节输入流),该类是InputStream类的子类(以字节为单位读取文件),该类的实例方法都是从InputStream类继承而来的。
可以使用FileInputSteam类的下列构造方法创建指向文件的输出流。
FileInputStream(String name);//创建FileInputSteam流
FileInputStream(File file);
参数name和file指定的文件称为输入流的源。
文件字节流可以调用从父类继承的read方法顺序地读取文件,只要不关闭流,每次调用read方法就顺序的读取文件中的其余内容,知道文件的末尾或文件字节输入流被关闭。
字节输入流的read方法以字节为单位读取源中的数据。
注:FileInputStream流顺序地读取,只要不关闭流,每次调用read方法就顺序地读取源中其余地内容,知道源地末尾或流被关闭。
输入流都提供了关闭方法close(),尽管程序结束时会自动关闭所有打开的流,但这是好习惯。如果没关闭流,就可能不允许另一个程序操作这些流所用的资源。
可以使用FileOutputStream类(文件字节输出流),它是OutputSteam类的子类(以字节为单位向文件写入内容),该类的实例方法都是从OutputStream类继承来的。
可以使用FileOutputStream类的下列具有刷新功能的构造方法创建指向文件的输出流。
FileOutputStream(String name);
FileOutputStream(File file);
FileOutputStream输出流开通一个到达文件的通道(目的地就是这个文件,输出流指向这个文件),如果指向的文件不存在,Java就会创建该文件,如果指向的文件已存在,输出流将刷新该文件(使得文件的长度为0).
可以使用 FileOutputStream类的下列能选择是否具有刷新功能的构造方法创建指向文件的输出流
FileOutputStream(String name,boolean append);
FileOutputStream(File file,boolean append)
当用构造方法创建指向一个文件的输出流时,如果参数append取值true,输出流不会刷新所指向的文件(假如文件已存在),输出流的write的方法将从文件的末尾开始向文件写入数据,参数append取值false,输出流将刷新所指向的文件(假如文件已存在)。
文件字节流可以调用从父类继承的write方法顺序地写文件。FileOutputStream流顺序地写入文件,即只要不关闭流,每次调用write方法就顺序地向文件写入内容,直到流被关闭。
调用close()方法,可以保证操作系统把流缓冲区地内容写到目的地,即关闭输出流可以把该流所用缓冲区地内容冲洗掉(通常冲洗到磁盘空间上)
文件字节输入、输出流的read和write方法使用字节数组读写数据,即以字节为单位处理数据。因此,字节流不能很好地操作Unicode字符,例如,一个汉字在文件中占用两个字节,如果使用字节流,读取不当会出现“乱码”现象。
与FileInputStream、FileOutputStream字节流相对应地是FileReader、FileWriter字符流(文件字符输入、输出流),FileReader和FIleWriter分别是Reader和Writer的子类,其构造方法分别是:
FileReader(String filename); FileReader(File filename);
FileWriter(String filename); FileWriter(File filename);
FileWriter(String filename,boolean append); FileWriter(File filename,boolean append);
字符输出流和输出流的read和write方法使用字符数组读写数据,即以字符为基本单位处理数据。
注:对于Writer流,writer方法将数据首先写入到缓冲区,每当缓冲区溢出时,缓冲区的内容被自动写入到目的地,如果关闭流,缓冲区的内容会立刻被写入到目的地。流调用flush()方法可以立刻冲洗当前缓冲区,即将当前缓冲区的内容写入到目的地。
BufferedReader和BufferedWriter类创建的对象称为缓冲输入、输出流,二者增强了读写文件的能力。
两者的源和目的地必须是字符输入流和字符输出流。
构造方法:
BufferedReader(Reader in);
BufferedWriter(Writer out);
BufferedReader流能读取文本行,方法 readLine()。
通过向BufferedReader传递一个Reader子类的对象(如FileReader的实例),来创建一个BufferedReader对象,如:
FileReader inOne = new FileReader("Student.txt");
BufferedReader inTwo = new BUfferedReader(inOne);
然后inTwo流调用readLine()方法中读取Student.txt,例如
String strLine = inTwo.readLine();
类似的,可以将BufferedWriter流和FileWriter流连接在一起,然后使用BufferedWriter流将数据写到目的地,例如:
FileWriter tofile = new FileWriter("hello.txt");BufferedWroter out = BufferedWriter(tofile);
然后out使用BufferedReader类的方法writer(String s,int off,int len)把字符串s写到hello.txt中,参数off是s开始处的偏移量,len是写入的字符数量。
BufferedWriter流有一个独特的向文件写入一个回行符的方法
newLine();
可以把BufferedReader和BufferedWriter称为上层流,把它们指向的字符流称为底层流,Java采用缓存技术将上层流和底层流连接。底层字符输入流首先将数据读入缓存,BufferedReader流再从缓存读取数据;BufferedWrirter流将数据写入缓冲,底层字符输出流会不断地将缓冲中的数据写入到目的地。当BufferedWriter流调用flush()刷新缓冲或调用close()方法关闭时,即使缓冲没有基础,底层流也会立刻将缓存地内容写入目的地。
注:关闭输出流时要首先关闭缓冲输出流,然后关闭缓冲输出流指向的流,即先关闭上层流再关闭底层流。再编写代码时指向关闭上层流,那么上层流的底层流将自动关闭。
通过该流能读文件也能写文件。
RandomAccessFile类创建的类被称为随机流,与前面不同的是,RandomAccessFile类既不是InputStream类的子类,也不是OutputStream类的子类,RandomAccessFile类创建的流的指向既可以作为流的源,也可以作为流的目的地。
构造方法:
注:RandomAccessFile流指向文件时,不刷新文件。
RandomAccessFile类中有一个方法seek(long a) 用来定位RandomAccessFile流的读写位置,其中参数 a 确定读写位置距离文件开头的字节个数。另外流还可以调用 getFIlePointer() 方法获取流的当前读写位置。RandomAccessFile流对文件的读写比顺序读写更灵活。
RandomAccessFile类的常用方法
方法 | 描述 |
---|---|
close() | 关闭文件 |
getFilePointer() | 获取当前读写的位置 |
length() | 获取文件长度 |
read() | 从文件读取一个字节的数据 |
readBoolean() | 从文件读取一个布尔值,0代表false,其他值代表true |
readByte() | 从文件读取一个字节 |
readChar() | 从文件中读取一个字符(2个字节) |
readDouble() | 从文件读取一个双精度浮点值(8个字节) |
readFloat() | 从文件读取一个单精度浮点值(4个字节) |
readFully(byte[] b) | 读b.length()字节放入数组b,完全填满该数组 |
readInt() | 从文件读取一个int值(4个字节) |
readLine() | 从文件读取一个文本行 |
readLong() | 读取一个长整型(8个字节) |
readShort() | 读取一个短型值(2个字节) |
需要注意的是,RandomAccessFile流的readLine()方法在读取含有非ASCII字符的文件时(比如有汉字)会出现乱码,因此,需要把readLine()读取的字符串用“iso-8859-1”编码重新编码存放到byte数组中,然后再用当前机器的默认编码将该数组转化为字符串,操作如下。
String str = in.readLine();
byte[] b = str.getBytes("iso-8859-1");
如果机器的默认编码是“GB2312”,那么
String content = new String(b);
等同于
String content = new String(b,"GB2312);
流的源和目的地除了可以是文件外,还可以是计算机内存
字节数组输出流ByteArrayInputStream和字节数组输出流ByteArrayOutputStream分别使用字节数组作为流的源和目的地。
ByteArrayInputStream的构造方法:
ByteArrayInputStream(byte[] buf);ByteArrayInputStream(byte[] buf, int offset, int length);
第一个构造方法的源:buf指定的数组的全部字节单元。
第二个构造方法的源:buf指定的数组从offset处按顺序取得length个字节单元。
字节数组输出流:
调用public int read();方法可以顺序地从源中读出一个字节,该方法返回读出的字节值;
public int read(byte[], int off, int len) 方法可以顺序地从源中读出参数指定地字节数,并将读出的字节存放到参数b指定的数组中,参数off指定数组b存放读出字节的起始位置,该方法返回实际读出的字节个数。如果未读出字节read方法返回 -1,
ByteArrayOutputStream流的构造方法:
ByteArrayInputStream();ByteArrayInputStream(int size);
第一个构造方法:构造的字节数组输出流指向一个默认大小为32字节的缓冲流,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。
第二个构造方法:构造的字节数组输出流指向的缓冲区的初始大小由参数size指定,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。
字节数组输出流:
调用public void writer(int b) 可以顺序地向缓冲区写入一个字节;
public void write(byte[] b, int off, int len) 将参数b中指定的len个字节顺序地写入缓冲区,参数off指定从b中写出的字节的起始位置 ;
public byte[] toByteArray() 返回输出流写入到缓冲区的全部字节;
与字节数组流对应的是字符数组流CharArrayReader和CharArrayWriter类,字符数组流分别使用字符数组作为流的源和目标。
DataInputStream和DataOutputStream类创建的对象称为数据输入流和数据输出流。这两个流很有用,它们允许程序按着机器无关的风格读取Java原始数据。也就是说,当读取一个数值时,不必再关心这个数值应当是多少个字节。
DataInputStream和DataOutputStream的构造方法: