Java中的I/O流是一组有顺序的、有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输。流可以是基于字节的,也可以是基于字符的。Java中的流,可以从不同的角度进行分类。按照数据流的方向:输入流和输出流;根据处理数据流的单位(即数据类型):字节流和字符流;根据功能层次的不同:节点流和处理流。
1 I/O流的分类
1)字节流和字符流(根据处理数据流的单位)
字节流:以字节为基本单位处理数据流,一次读入/出的是8位二进制。一般用于二进制数据(如声音、图像)的读写
字符流:以字符为基本单位处理数据流,一次读入或读出是16位二进制,一般用于对文本类型数据(如文本文件)的读写。
这两种流主要由4个抽象类来表示,分别是InputStream、OutputStream、Reader、Writer。输入输出各两种,后缀是Stream的是字节流、Reader和Writer表示字符流。
字节流和字符流的原理是相同的,只是处理数据的单位不同。
2)节点流与处理流
节点流(Node Streams):一般用于直接从指定的位置进行读写操作,提供基本的读写操作方法,功能单一
处理流(Processing Streams):用于对其他IO流进行封装,提供功能更加丰富的读写方法
实际的开发中,通常是将节点流与处理流结合起来使用,节点流直接与指定的源或目标相连,如某个文件、某个网络连接等。处理流
下面这幅图是Java中的一些基本的IO流
对上图的简单介绍
- 文件操作:FileInputStream,FileOutputStream,FileReader,FileWriter
- 管道操作:PipedInputStream ,PipedOutStream,PipedReader,PipedWriter ;PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
- 字节/字符数组:ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter;这些流在内存中开辟了一个字节或字符数组。
- Buffered缓冲流::BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter;带缓冲区的处理流,缓冲区的作用:Java I/O默认是不缓冲流的,所谓“缓冲”就是先把从流中得到的一块字节序列暂存在一个被称为buffer的内部字节数组里,然后可以一次取到这一整块的字节数据,避免每次和硬盘打交道,提高数据访问的效率。
- 转化流:InputStreamReader/OutputStreamWriter,把字节转化成字符
- 数据流:DataInputStream,DataOutputStream。输出一个8个字节的long类型或4个字节的float类型时,处理方式可以是一个字节一个字节输出,或者把其转换成字符串输出,但是这样转换费时间。上述数据流就解决了输出数据类型的困难,DataInputStream的好处在于在从文件读出数据时,不用费心地自行判断读入字符串时或读入int类型时何时将停止,使用对应的readUTF()和readInt()方法就可以正确地读入完整的类型数据,提高了数据读写的效率。
- 打印流:printStream,printWriter,一般是打印到控制台或可以进行控制打印的地方。
- 对象流:ObjectInputStream,ObjectOutputStream,把封装的对象直接输出,而不是一个个在转换成字符串再输出。
- 序列化流:SequenceInputStream
2 使用流进行文件操作
使用流来进行文件操作是流应用中最常见的应用之一。使用流可以读取文件内容,也可以写入文件内容。
2.1 使用File类进行文件与目录操作
Java中有一个表示目录和文件的类——java.io.File,可以获取文件、目录等信息,对文件、目录进行管理。File类仅仅封装了对应文件或目录的信息,其中并没有包含文件的内容,若需要获取文件中具体内容就需要使用流。
如下示例列举了一些常用的File方法操作
import java.io.*;
public class FileExample{
pubic static void main(String[] args){
createFile();
fileInputStream();
}
private static createFile(){
//创建File对象
File f1 = new File("C:\\");
File f2 = new File("C:\\file.text");
if(f1.isDiretory()) //判断f1对象是否为目录
System.out.println("f1对象是一个目录");
if(f1.isFile()) //判断f1对象是否为文件
System.out.println("f1对象是一个对象");
//程序运行之前,需要在C盘根目录下创建一个file.text文件,如果没有这个文件,程序会出现异常
try{
f2.creatNewFile(); //若f2对象不存在,则可以创建一个空文件,创建成功返回true
System.out.println("文件名"+f2.getName()); //返回File对象表示的文件或目录的名称
System.out.println("文件父目录字符串 "+f.getParent());// 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
f2.mkdirs(); //创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
} catch (Exception e) {
e.prinlStackTrace();
}
}
//FileInputStream读取字节数据
public static void fileInputStream(){
File f = new File("C:\\file.text"); //创建File对象
FileInputStream fInStream = new FileInputStream(f);//创建FileInputStream对象
char ch;
for(int i=0;i<f.length(0;i++){
ch = (Char)fInStream.read();//从该输入流中读取一个数据字节,返回int类型
System.out.println(ch);
}
fInStream.close(); //关闭输入流
}
}
2.2 FileInputStream和FileOutputStream
FileInputStream是InputStream的子类,主要用于从文件系统中的某个文件获取输入字节,如读取诸如图像数据之类的原始字节流;FileOutputStream是OutputStream的子类,用于将数据以字节形式写入目标文件的输出流。每次使用完流对象后,需要显式的调用close方法进行关闭操作,及时释放系统资源。
2.3 FileReader和FileWriter
FileReader和FileWriter针对字符操作,两个类的间接父类是字符流Reader和Writer。FileReader和FileWriter中没有自定义的方法,所有方法都来自其父类及间接父类。
pubic static void main(String[] args){
File f = new File("C:\\file.text"); //创建File对象
String str = “FileReader and FileWriter”;
char[] buffer = new char[str.length()]; //创建一个char数组,初始化
buffer = str.toCharArray();
try{
if(!f.exists())
f.createNewFile();
FileWriter writer = new FileWriter(f);
writer.write(buffer);//输出整个字符数组到文件
writer.close();
//从文件读取数据
FileReader reader = new FileReader(f);
int BUFFER_SIZE = 512;
char[] readBuf = new char[BUFFER_SIZE];
int readSize;
while ((readSize = reader.read(readBuf)) != -1) {
String readData = String.valueOf(readBuf);
System.out.println(readData);
}
} catch (Exception e) {
e.prinlStackTrace();
}
}
注:所谓链接流就是就多次对流的封装,这样能更好的操作个管理数据(如DataInputStream(BufferedInputStream(FileInputStream))。