图:Java io概览图
流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类
字节流 字符流
输入流
InputStream
Reader
输出流
OutputStream
Writer
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
输入流和输出流
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
JAVA字节流
- FileInputStream和FileOutputStream
这两个类属于结点流,第一个类的源端和第二个类的目的端都是磁盘文件,它们的构造方法允许通过文件的路径名来构造相应的流。如:
FileInputStream infile = new FileInputStream("myfile.dat");
FileOutputStream outfile = new FileOutputStream("results.dat");
要注意的是,构造FileInputStream, 对应的文件必须存在并且是可读的,而构造FileOutputStream时,如输出文件已存在,则必须是可覆盖的。
- BufferedInputStream和BufferedOutputStream
它们是过滤器流,其作用是提高输入输出的效率。 - DataInputStream和DataOutputStream
这两个类创建的对象分别被称为数据输入流和数据输出流。这是很有用的两个流,它们允许程序按与机器无关的风格读写Java数据。所以比较适合于网络上的数据传输。这两个流也是过滤器流,常以其它流如InputStream或OutputStream作为它们的输入或输出。
Java的字符流
字符流主要是用来处理字符的。Java采用16位的Unicode来表示字符串和字符,对应的字符流按输入和输出分别称为readers和writers。
- InputStreamReader和OutputStreamWriter
在构造这两个类对应的流时,它们会自动进行转换,将平台缺省的编码集编码的字节转换为Unicode字符。对英语环境,其缺省的编码集一般为ISO8859-1。 - BufferedReader和BufferedWriter
这两个类对应的流使用了缓冲,能大大提高输入输出的效率。这两个也是过滤器流,常用来对InputStreamReader和OutputStreamWriter进行处理。 -
public class readFile {
public static void main(String [] args) {
BufferedInputStream bs = null;
BufferedOutputStream bo = null;
String fileName = "d:" + File.separator + "file" + File.separator + "file.txt";
String fileNameOut = "d:" + File.separator + "file" + File.separator + "file1.txt";
try {
File file = new File(fileName);
bs = new BufferedInputStream(new FileInputStream(file));
bo = new BufferedOutputStream(new FileOutputStream(fileNameOut));
byte[] buf = new byte[1024];
int len = 0;
while((len = bs.read(buf))!= -1) {
System.out.println(new String(buf,0,len));
bo.write(buf,0,len);
}
bo.flush();//如果OutputStream 的实现使用了缓存,这个方法用于清空缓存里的数据,并通知底层去进行实际的写操作
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bs.close();
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} -
DataOutputStream、DataInputStream的常见用法
包装类DataOutputStream、DataInputStream为我们提供了多种对文件的写入和读取方法,
如writeBoolean(),writeUTF(),writeChar,writeByte(),writeDouble()等和对应的read方法,
这些方法极大的方便了我们的写入和读取操作,下面结合一个程序来探讨一下这些方法的使用。
import java.io.*;
public class TestDataStream {public static void main(String[] args) throws Exception {
FileOutputStream fos=new FileOutputStream("data.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
DataOutputStream dos=new DataOutputStream(bos);
FileInputStream fis=new FileInputStream("data.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
DataInputStream dis=new DataInputStream(bis);
String str="你好hi";
dos.writeUTF(str); //按UTF-8格式写入
dos.writeChars(str); //按字符写入
//按字节写入有两种方法,第一种方法只能适应无汉字的情况;
//因为方法1在写入时会把所有的字符都按1个字节写入,而汉字的表示需要2个字节,
//这就造成了数据的丢失,读入时就会出现乱码。
//而方法2在将字符串转换为字节数组时就把汉字字符变为了2个字节,
//写入文件的时候也会按2个字节的文字写入,这样读取的时候就不会出现问题
dos.writeBytes(str);//方法1:将整个字符串按字节写入
byte[] b=str.getBytes();
dos.write(b); //方法2:将字符串转换为字节数组后再逐一写入
dos.close();
//按UTF-8格式读取System.out.println(dis.readUTF());
//字符读取
char [] c=new char[4];
for(int i=0;i<4;i++){
c[i]=dis.readChar(); //读取4个字符
}
System.out.print(new String(c,0,4));
System.out.println();
//字节读取byte [] b1=new byte[4];
dis.read(b1); //读取4个字节
System.out.print(new String(b1,0,4));//输出时会出现乱码
System.out.println();
byte [] b2=new byte[1024];
int len=dis.read(b2); //按字节读取剩余的内容
System.out.println(new String(b2,0,len));
}}
输出结果为:
你好hi
你好hi
`}hi
你好hi注意1:一般情况下在读入时尽量按照写入时的格式进行读取,
否则有可能会出现显示乱码或程序出现异常。
如首先写入文件用的是writeUTF(),在读取的时候如果不是用readUTF()就会出现乱码,
如果readUTF()读取的内容不是UTF-8格式的,程序就会抛出异常。
注意2:如程序中注释所说,对于出现汉字字符的情况不能用writeBytes(),这会在写入文件时丢弃汉字字符的第一个字节从而在读取时出现错误。
注意3:所有的读取方法都是共享一个位置指示器的,即在前面的read方法执行后,后面再执行其他read方法都是从上一个read方法读取到的位置开始向后读取的。如开始执行了1次readByte()后面的readChar是从第2个字节开始读的。