学习IO部分的知识后,被处理流的各种类搞得有点乱,所以得写篇文章总结下。IO包里面有很多输入输出类,一般我们是通过控制流来控制输入输出的。
IO包里面对于控制流分为两大类,一类是字节流,一类是字符流。字符流的两个大类是Reader,Writer,字节流的是InputSream和OutputStream。通过这两个流以及其子类,我们可以控制系统与程序的输入输出。
1. 从类名看流(以字符流为例)
1.1 根据命名规则来选择类
由上图可以看出Reader、Writer类有很多子类,每个子类的名称构造都是“功能+对应的类别”,这就是IO包类命名的规则。所以我们可以从类名的后一部分看出它是属于字符流还是字节流的,然后从前一部分看出它的功能是什么。根据这个规则,我们可以选择合适的类来处理流。
1.2 各类的作用以及关系
IO包的类不少,但是他们之间都是有关系的,想Reader用来读取字节流,其子类是根据要读取不同的源而需要不同的功能而划分的。比如Reader的子类BufferedReader就是提供缓冲读取,提高效率。CharArrayReader则是把字符数组当做源来读取流数据,FilterReader则是对已过滤流的读取,OutputStreamReader则是转换流,是字符流与字节流之间的桥梁,因为他可以把字节流当做源,读取数据,当做字节流输出,所以它在初始化可以选择编码,如果不设置,则设置为系统的默认编码。这个类会被经常用到。而StringReader跟CharArrayReader类似,只是他是把字符串当做源来读数据。另外常用到的还有InputSreamReader的子类FileReader,它可以直接从File对象读取数据,一般能直接操作文件的类都会比较常用到。
至于Writer类跟Reader类似,但是它有个功能比较强大的子类,就是PrintWriter。该类在开发中很经常用到。
上图是PrintWriter的构造方法,可以看出他面向的目的流是多样化的,可以直接往File对象(同时可以为其采用相应的字符集)、OutputStream输出流(可设置是否每次写入后自动flush)写数据,可以通过字符串构造的文件路径名建立文件并写写入,同时可以传入Writer对象,增强Writer的功能。而且它还可以使用print,printf,println往目的流输入各种数据。一下是其使用的一个案例:
package tst.IODemo;
import java.io. * ;
publicclass PrintWriterDemo {
publicstaticvoid main(String[] args) throws IOException {
writeMethods ();
}
publicstaticvoid writeMethods() throws FileNotFoundException {
PrintWriter pw =new PrintWriter( "d:\\a.txt" ); //可以写到指定的文件里
char [] buf = { 'a' , 'b' , 'c' };
pw.write(buf); //可以写入字符数据
pw.println( "123" ); //可以直接用 println 方法往目的流输入数据
pw.print( "456" );
pw.println(); //写入一个换行符
pw.write( 49 ); //可以写入整数,但是写到文本里会根据ASCII表写入相应的字符
pw.write( "weuroei" ); //可以直接写字符串
pw.format( "%s" , "wcc" ); //可以给字符串规定格式写入
pw.close();
}
}
各类的关系:虽然上面讲的都是字符流,但是他们的道理是相同的,只是一个是以字符形式读取和写入,而一个是以字节形式读取写入。他们的区别是源或目的不同,所以他们的构造方法传入的参数不同,会根据是什么作用而传入何种参数,比如StringReader的构造参数会传入字符串,而CharArrayReader传入的则是字节数组。大体方法相同,但是不同功能的类会有区别于其他类的特有功能,比如处理字符串的,处理字节数组的,缓冲的。
2. 程序实践
2.1 实践中使用的思路总结
一般该类的使用前,首先判断是要操作字节流还是字符流,再者确定是要读取或者是写入。确定后一般分三层进行,必须要有源流或是目的流,然后根据该流是文件的,字符串的还是字节的。接下来就是是否需要字节流与字符流之间的转换,需要则选InputSreamReader、OutputStreamWriter。然后就考虑读写效率的提高,这个就可以选择带Buffered的了。还有其他各种各样的功能,比如合并流的SequenceInputStream、处理音频的AudioInputStream等等,记住大概使用思路,使用时忘记不懂再查API就OK了。
2.2 各种程序实践
(1)FileReader的使用(Reader-InputSreamReader子类)
由该类的名字我们可以知道他是操作文件的,可以直接打开文件,读取文件的数据到输入流。
package tst.IODemo;
import java.io.*;
publicclass FileReaderDemo {
publicstaticvoid main(String[] args) throws IOException {
FileReader fr =new FileReader( "WriterException.java" );
char [] buf =newchar [1024];
int num;
while ((num = fr.read(buf)) !=-1) {
System. out .print( new String(buf,0,num));
}
fr.close();
}
}
(2)拥戴Buffered相关类提高FileReader和FileWriter读写效率:
该时间里面写用BufferedWriter但是自己根据BufferedReader的原理,自己写了一个MyBufReader模拟BufferedReader的运行,结果同样可读取数据。该例子里的BufferedReader和BufferedWriter主要是用于提高FileReader和FileWriter两者个工作效率,读N个数据放到缓存,再一次性写到输出流,避免读一个字节写一次,提高效率。
package tst.IODemo;
import java.io.*;
publicclass BufCopyText {
publicstaticvoid main(String[] args) {
MyBufReader fr = null ;
BufferedWriter fw = null ;
try {
fr =new MyBufReader( new FileReader( "E:\\学习资源\\cmd.exe" ));
fw =new BufferedWriter( new FileWriter( "E:\\学习资源\\cmd1.exe" ));
String buf = null ;
while ((buf = fr.myReadLine()) != null ) {
fw.write(buf);
fw.newLine();
fw.flush();
}
} catch (IOException e) {
thrownew RuntimeException( "读写失败!" );
} finally {
if (fr != null ) {
try {
fr.close();
} catch (IOException e) {
thrownew RuntimeException( "读缓存关闭失败。" );
}
}
if (fw != null ) {
try {
fw.close();
} catch (IOException e) {
thrownew RuntimeException( "写缓存关闭失败。" );
}
}
}
}
}
/**
* 该类模拟BufferedReader的工作原理,作用相同。
* */
class MyBufReader {
FileReader r ;
int buf ;
public MyBufReader(FileReader r) {
this . r = r;
}
public String myReadLine() throws IOException {
StringBuilder sb =new StringBuilder();
while (( buf = r .read()) !=-1) {
if ( buf =='\r' ) {
continue ;
}
if ( buf =='\n' ) {
return sb.toString();
}
sb.append(( char ) buf );
}
if (sb.length() !=0) {
return sb.toString();
}
return null ;
}
publicvoid close() throws IOException {
r .close();
}
}
(3)接下来则是字节流的实践,从键盘往文件里面写数据:
该例子实现的功能是在控制台通过键盘输入数据,然后通过FileOutputStream打开一个文件,通过OutputStreamWriter把输入的字节流转换成字符流输出到a.txt文本,同时运用BufferedWriter提高写入效率。
package tst.IODemo;
import java.io.*;
publicclass OutputStreamWriterDemo {
publicstaticvoid main(String[] args) throws IOException {
BufferedReader br =new BufferedReader( new InputStreamReader(System. in ));
BufferedWriter bw =new BufferedWriter( new OutputStreamWriter( new FileOutputStream( "a.txt" ), "UTF-8" ));
String buf = null ;
while ((buf = br.readLine()) != null ) {
if (buf.equals( "over" )) break ; //当输入“over”时则写入结束,over不写入文件
bw.write(buf);
bw.newLine();
}
br.close();
bw.close();
}
}
(4)读取一个文件的内容,并输出到控制台上:
读取一个文件,把内容输出到控制台上,同时获取开始和结束时间,比较BufferedInputSream和BufferedOutputStream与DataInputStream和DataOutputStream的效率,果然发现Buffered类的效率搞了将近一倍,使用Buffered类的我们不需要去控制他的缓冲区,只要跟其他类一样正常使用就行,他内部自己有使用缓冲方法的机制。
package tst.IODemo;
import java.io.*;
publicclass BufInputStream {
publicstaticvoid main(String[] args) throws IOException {
BufferedInputStream bis =new BufferedInputStream( new FileInputStream( "SysInfo.txt" ));
BufferedOutputStream bop =new BufferedOutputStream(System. out );
// DataInputStream bis = new DataInputStream(new FileInputStream("SysInfo.txt"));
// DataOutputStream bop = new DataOutputStream(System.out);
int buf;
long start = System. currentTimeMillis ();
while ((buf = bis.read()) !=-1) {
bop.write(buf);
bop.flush();
}
long end = System. currentTimeMillis ();
System. out .println((end - start) +"毫秒" );
bis.close();
bop.close();
}
}
(5)合并流
把多个输入流合并成一个大的输入流,然后一起写到输出流中。
package tst.IODemo;
import java.io.*;
import java.util.*;
publicclass SequenceDemo {
publicstaticvoid main(String[] args) throws IOException {
Vector<FileInputStream> v =new Vector<FileInputStream>();
PrintWriter pw;
try {
//从当前目录读取三个txt文件1.txt,2.txt,3.txt
FileInputStream fis1 =new FileInputStream("D:\\1.txt");
FileInputStream fis2 =new FileInputStream("D:\\2.txt");
FileInputStream fis3 =new FileInputStream("D:\\3.txt");
//创建4.txt文件
pw =new PrintWriter(new FileOutputStream("D:\\4.txt"));
//把3个文件存入向量集合v中,是为了生成下面的Enumeration<FileInputStream>集合
v.add(fis1);
v.add(fis2);
v.add(fis3);
} catch(FileNotFoundException e) {
thrownew RuntimeException("文件打开失败。请查询文件是否存在!");
}
Enumeration<FileInputStream> e = v.elements();
//SequenceInputStream构造函数只要两个方法,一个是传入两个InputSream,这只能合并两个流,
//想要合并多个流,则需要传入一个Enumeration集合才可以。
SequenceInputStream sis =new SequenceInputStream(e);
int buf;
while((buf = sis.read()) !=-1) {
pw.write(buf); //三个流一起写入到输出流
}
sis.close();
pw.close();
System.out.println("Done!");
}
}
转载于:https://blog.51cto.com/cthhqu/1262101