11、IO输入输出流
11.1、File类
File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操纵文件,通过调用File类提供的各种方法,我们能够创建、删除文件,重命名文件,判断文件的读写权限及是否存在,设置和查询文件的最近修改时间。
import java.io.*;
public class FileTest{
public static void main(String[] args){
File f=new File("c:\\1.txt");
if(f.exists()){
f.delete();
}else{
try{
f.createNewFile();
}catch(Exception e){
System.out.println(e.getMessage());
}
}
System.out.println("File name:"+f.getName());
System.out.println("File path:"+f.getPath());
System.out.println("Abs path:"+f.getAbsolutePath());
System.out.println("Parent:"+f.getParent());
System.out.println(f.exists()?"exists":"does not exist");
System.out.println(f.canWrite()?"is writeable":" is not writeable");
System.out.println(f.canRead()?"is readable":"is not readable");
System.out.println(f.isDirectory()?"is ":"is not"+" a directory");
System.out.println(f.isFile()?"is normal file":" might be a named pipe");
System.out.println(f.isAbsolute()?"is absolute":" is not absolute");
System.out.println("File last modified:"+f.lastModified());
System.out.println("File size:"+f.length()+" Bytes");
}
}
11.2、RandomAccessFile类
RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。RandomAccessFile类支持“随机访问”方式,我们可以跳转到文件的任意位置处读写数据。在你访问一个文件的时候,不想把文件从头读到尾,并希望像访问一个数据库一样的访问一个文本文件,使用RandomAccessFile类就是你的最佳选择。
创建RandomAccessFile类对象的构造方式:
new RandomAccessFile(f,"rw"); //读写方式
new RandomAccessFile(f,"r"); //只读方式
注:当我们的程序需要以读写的方式打开一个文件时,如果这个文件不存在,程序会为你创建它。
11.3、InputStream与OutputStream
程序可以从中连续读取字节的对象叫输入流,用InputStream类完成,程序能向其中连续写入字节的对象叫输出流,用OutputStream类完成。
InputStream定义了Java的输入流模型。该类中的所有方法在遇到错误的时候都会引发IOException异常,下面是InputStream类中方法的一个简要说明:
int read()返回下一个输入字节的整型表示,,如果返回-1表示遇到流的末尾,结束。
int read(byte[] b)读入b.length个字节放到b中并返回实际读入的字节数。
int read(byte[] b,int off,int len) 这个方法表示把流中的数据读到,数组b中,第off个开始的len个数组元素中.
long skip(long n) 跳过输入流上的n个字节并返回实际跳过的字节数。
int availabale() 返回当前输入流中可读的字节数。
void mark(int readlimit)在输入流的当前位置处放上一个标志,允许最多再读入readlimit个字节。
void reset() 把输入指针返回到以前所做的标志处。
boolean markSupported() 如果当前流支持mark/reset操作就返回true。
void close() 在操作完一个流后要使用此方法将其关闭, 系统就会释放与这个流相关的资源。
InputStream是一个抽象类,程序中实际使用的是它的各种子类对象。不是所有的子类都会支持InputStream中定义的某些方法的,如skip,mark,reset等,这些方法只对某些子类有用。
OutputStream是一个定义了输出流的抽象类,这个类中的所有方法均返回void,并在遇到错误时引发IOException异常。下面是OutputStream的方法:
void write(int b) 将一个字节写到输出流。注意,这里的参数是int型,它允许write使用表达式而不用强制转换成byte型。
void write(byte[] b) 将整个字节数组写到输出流中。
void write(byte [] b,int off,int len) 将字节数组b中的从off开始的len个字节写到输出流。
void flush彻底完成输出并清空缓冲区。
void close关闭输出流。
11.4、FileInputStream与FileOutputStream
这两个流节点用来操作磁盘文件,在创建一个FileInputStream对象时通过构造函数指定文件的路径和名字,当然这个文件应当是存在的和可读的。
有两种创建方式:
FileInputStream inOne=new FileInputStream("hello.test");
File f = new File("hello.test");
FileInputStream inTwo = new FileInputStream(f);
例如:
import java.io.*;
public class FileStream{
public static void main(String[] args){
File f = new File("hello.txt");
try{
FileOutputStream out = new FileOutputStream(f);
byte buf[]="www.it315.org".getBytes();
out.write(buf);
out.close();
}catch(Exception e){
System.out.println(e.getMessage());
}
try{
FileInputStream in = new FileInputStream(f);
byte [] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
11.5、Reader与Writer
Java中的字符是unicode编码,是双字节的,而InputStream与OutputStream是用来处理字节的,在处理字符文本时不太方便,需要编写额外的程序代码。Java为字符文本的输入输出专门提供了一套单独的类,Reader、Writer两个抽象类与InputStream、OutputStream两个类相对应,同样,Reader、Writer下面也有许多子类,对具体IO设备进行字符输入输出,如FileReader就是用来读取文件流中的字符。
import java.io.*;
public class FileStream{
public static void main(String[] args){
File f = new File("hello.txt");
try{
FileWriter out = new FileWriter(f);
out.write("www.it315.org");
out.close();
}catch(Exception e){
System.out.println(e.getMessage());
}
try{
FileReader in = new FileReader(f);
char [] buf = new char[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
11.6、PipedInputStream与PipedOutputStream
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据。这两个类主要用来完成线程之间的通信,一个线程的PipedInputStream 对象能够从另外一个线程的PipedOutputStream对象中读取数据。
import java.io.*;
public class PipeStreamTest{
public static void main(String args[]){
try{
Thread t1=new Sender();
Thread t2=new Receiver();
PipedOutputStream out = t1.getOutputStream();
PipedInputStream in = t2.getInputStream();
out.connect(in);
t1.start();
t2.start();
}catch(IOException e){
System.out.println(e.getMessage());
}
}
}
class Sender extends Thread{
private PipedOutputStream out=new PipedOutputStream();
public PipedOutputStream getOutputStream(){
return out;
}
public void run(){
String s=new String("hello,receiver ,how are you");
try{
out.write(s.getBytes());
out.close();
}catch(IOException e){
System.out.println(e.getMessage());
}
}
}
class Receiver extends Thread{
private PipedInputStream in=new PipedInputStream();
public PipedInputStream getInputStream(){
return in;
}
public void run(){
String s=null;
byte [] buf = new byte[1024];
try{
int len =in.read(buf);
s = new String(buf,0,len);
System.out.println("the following message comes from sender:\n"+s);
in.close();
}catch(IOException e){
System.out.println(e.getMessage());
}
}
}
11.7、ByteArrayInputStream与ByteArrayOutputStream
ByteArrayInputStream是输入流的一种实现,它有两个构造函数,每个构造函数都需要一个字节数组来作为数据源:
ByteArrayInputStream(byte[] buf)
ByteArrayInputStream(byte[] buf, int offset, int length)
第二个构造函数指定仅使用数组buf中的从offset开始的length个元素作为数据源。
ByteArrayOutputStream是输出流的一种实现,它也有两个构造函数。
ByteArrayOutputStream()
ByteArrayOutputStream(int)
第一种形式的构造函数创建一个32字节的缓冲区,第二种形式则是根据参数指定的大小创建缓冲区,缓冲区的大小在数据过多时能够自动增长。
11.8、BufferedInputStream与 BufferedOuputStream
对I/O进行缓冲是一种常见的性能优化。缓冲流为I/O流增加了内存缓冲区。增加缓冲区有两个基本目的:
允许Java的I/O一次不只操作一个字节,这样提高了整个系统的性能。
由于有缓冲区,使得在流上执行skip、mark和reset方法都成为可能。
BufferedInputStream
Java的BufferedInputStream类可以对任何的InputStream进行带缓冲区的封装以达到性能的改善。BufferedInputStream有两个构造函数:
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
第一种形式的构造函数创建了一个带有32字节缓冲区的缓冲流,第二种形式的构造函数按指定的大小来创建缓冲区。
BufferedOutputStream
往BufferedOutputStream输出和往OutputStream输出完全一样,只不过BufferedOutputStream有一个flush方法用来将缓冲区的数据强制输出完。与缓冲区输入流不同,缓冲区输出流没有增加额外的功能。在Java中使用输出缓冲也是为了提高性能。它也有两个构造函数:
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out,int size)
第一种形式创建一个32字节的缓冲区,第二种形式以指定的大小来创建缓冲区。
11.9、DataInputStream与 DataOutputStream
DataOutputStream类提供了三种写入字符串的方法,分别是
public final void writeBytes(String?s) throws IOException
public final void writeChars(String?s) throws IOException
public final void writeUTF(String?str) throws IOException
11.10、字节流与字符流的转换
InputStreamReader 和 OutputStreamWriter
这两个类是字节流和字符流之间转换的类,InputStreamReader可以将一个字节流中的字节解码成字符,OuputStreamWriter将写入的字符编码成字节后写入一个字节流。其中InputStreamReader有两个主要的构造函数:
InputStreamReader(InputStream?in) //用默认字符集创建一个InputStreamReader对象
InputStreamReader(InputStream in,String CharsetName) //接受以指定字符集名的字符串,并用//该字符集创建对象
OutputStreamWriter也有对应的两个主要的构造函数:
OutputStreamWriter(OutputStream?in) //用默认字符集创建一个OutputStreamWriter对象
OutputStreamWriter(OutputStream?in,String CharsetName) //接受以指定字符集名的字符串,并用该字符集创建OutputStreamWriter对象
为了达到最好的效率,避免频繁的字符与字节间的相互转换,我们最好不要直接使用这两个类来进行读写,应尽量使用BufferedWriter类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader。例如:
BufferedWriter out=new BufferedWriter(newOutputStreamWriter(System.out));
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
读取到键盘上输入的一整行字符,只要用下面的两行程序代码就可以解决这个问题:
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String strLine = in.readLine();
11.11、IO包中的类层次关系图
1. 字节输入流类:
2. 字节输出流类:
3. 字符输入流类:
4. 字符输出流类: