20,字节流和字符流
20.1 IO流
IO流是用来处理设备之间的数据传输,IO流按照操作的数据不同分为:字节流和字符流,按照流向分为:输入流和输出流,他们都在java.io包中。
如图:
用来处理字节数据的流对象,就是字节流。
用来处理字符数据的流对象,就是字符流。
字符流相当于,字节流+编码表,也就是通过字节流读取字节数据,再进行查表获取字符,再对字符进行操作。
因为操作字节数据和字符数据的流对象很多,对他们进行共性内容的向上抽取,就形成了字节流和字符流的继承体系。
字节流的两个抽象基类:InputStream和OutputStream.
字符流的两个抽象基类:Reader和Writer.
他们各自的子类,都以其父类作为后缀。
20.2字符流的子类FileReader和FileWriter
FileReader是用于读入字符数据的输入流,其构造函数,可以接收一个File对象,也可以是字符串路径。其构造函数接收的File对象或一个目标文件的路径,代表的是源数据,也就是被读取的数据,如果文件不存在会抛出异常。
FileWriter 是用来写入字符数据的输出流,其构造函数,也可以接收一个File对象,或字符串路径。其构造函数接收的File对象或一个目标文件的路径,表示的是目标数据,也就是被写入的数据。
FileReader 和FileWriter是用来操作文件对象的流,可以将读取到的源文件数据,写到另一个文件中。FileWriter中接收的目标文件,如果不存在,会创建一个文件,如果存在就会覆盖,FileWriter支持续写,在创建FileWriter对象时,可以使用FileWriter
(File file, boolean append)
或FileWriter(String fileName, boolean append)构造函数,在第二个参数传入true,就可以对目标文件进行续写,而不是一次次的覆盖。
FileReader对象的read()方法,一次读取一个字符,如果读取不到字符则返回-1.
FileWriter对象的writer()方法,将读取到的字符写入目标文件。
注意:字符流每次写入后都要调用一次flush()将流上的数据刷入目标文件。当然,最后的close()也会自动刷新流上的数据。
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
public static void demo_1()throws IOException {
FileReader fr=new FileReader(new File("d:\\a.txt"));//创建一个FileReader对象,接收一个源文件。
FileWriter fw=new FileWriter(new File("d:\\b.txt"),true);//创建一个FileWriter对象,并支持续写,接收一个目标文件。
int ch=0;//用于记录数据的变量。
while((ch=fr.read())!=-1){//如果数据量大,这里需要循环读取。
fw.write(ch);//将读取到的数据写入输出流。
fw.flush();//刷新数据到目的地。
}
fw.close();//关闭输出流。
fr.close();//关闭输入流。
}
}
上面代码中,只用了一个变量来记录读取到的字符,对于较大的文件,效率会很低时,这时可以自定义一个字符数组,用来作为临时容器存储读取到的字符数据,然后再写入目标文件中。
如:字符流,使用自定义缓冲区。
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
public static void demo_1()throws IOException{
FileReader fr=new FileReader(new File("d:\\a.txt"));//创建一个FileReader对象。
FileWriter fw=new FileWriter(new File("d:\\b.txt"),true);//创建一个FileWriter对象,并支持续写。
char[]chs=newchar[1024];//定义一个字符数组作为临时容器,存储读取到的字符。
int len=0;//定义一个变量记录读取的字符数。
while((len=fr.read(chs))!=-1){//循环读取源文件数据,将数据读入字符数组。
fw.write(chs,0,len);//将读取到的数据写入输出流。
fw.flush();//刷新数据到目的地。
}
fw.close();//关闭输出流。
fr.close();//关闭输入流。
}
}
20.3字符缓冲区流对象BufferedReader和BufferedWriter
当读取的字符数据较大时,也可以使用字符流缓冲区,这两个是装饰类,用于增强字符流的功能,内部维护一个8kB临时容器,用于提高读写的效率。构造函数可以接收字符流的所有子类。
BufferedReader对象的readLine()方法,通过判断换行和回车标记,一次可以读取一行,增强了对字符数据读取的效率。
如:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
public static void demo_1()throws IOException {
BufferedReader br=new BufferedReader(new FileReader(new File("d:\\a.txt")));//创建一个BufferedReader对象,接收一个字符读取流。
BufferedWriter bw=new BufferedWriter(new FileWriter(new File("d:\\b.txt"),true));//创建一个BufferedWriter对象,接收一个字符写入流
String line=null;
while((line=br.readLine())!=null){//将读取的一行行字符存储在字符串常量line中。
bw.write(line);//将读取到的字符数据写入输出流。
bw.newLine();//在行结尾写入换行符。
bw.flush();//刷新数据到目的地。
}
bw.close();//关闭输出流。
br.close();//关闭输入流。
}
}
BufferedReader对象的readLine()方法,内部实际也是调用了,被装饰对象的read(),只是增加了对换行标记和回车标记的判断。提高字符的读取效率。
BuffereWriter对象的newLine()方法,可以写入换行符,将读到的行,写入目标文件,并加上换行符。
20.4字节流的子类:FileInputStream、FileOutputStream
FileInputStream是用来读取字节数据的字节输入流,其构造函数可以接收File对象或字符串路径。字节流既可以读取字符数据,可以读取字节数据。
FileOutputStream是用来写入字节数据的字节输出流,其构造函数可以接收File对象或字符串路径,并支持续写。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
public static void demo_1()throws IOException {
FileInputStream fis=new FileInputStream(new File("d:\\a.txt"));//创建字节输入流对象,关联源文件
FileOutputStream fos=new FileOutputStream(new File("d:\\b.txt"),true);//创建字节输出流对象,关联目标文件。
int ch=0;//定义一个变量,用于存储字节数据。
while((ch=fis.read())!=-1){//循环读取字节数据。
fos.write(ch);//通过字节输出流写入目标文件。
}
fos.close();//关闭资源。
fis.close();//关闭资源。
}
}
FileInputStream类中的read()方法,一次读取一个字节,所以对于数据大的文件,可以循环读取。
以上代码中,只用了一个变量存储读取的字节数据,效率较低,在字符流中可以定义一个字符数组,作为临时容器,在字节流中同样可以定义一个字节数据,作为字节输入流的临时容器,提高读写的效率。
FileOutputStream类的write()将字节数据写入目标文件,因为是字节数据,会直接写入目标文件,所以,不需要调用flush()方法。
如:字节输入流,使用自定义缓冲区
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
public static void demo_1()throws IOException {
FileInputStream fis=new FileInputStream(new File("d:\\a.txt"));//创建字节输入流对象,关联源文件
FileOutputStream fos=new FileOutputStream(new File("d:\\b.txt"),true);//创建字节输出流对象,关联目标文件。
byte[]buf=newbyte[1024];//创建字节数组,作为临时缓冲区。
intlen=0;//定义一个变量,记录读取到的字节数。
while((len=fis.read(buf))!=-1){//循环读取字节数据并存储到字节数组中。
fos.write(buf,0,len);//通过字节输出流写入目标文件。
}
fos.close();//关闭资源。
fis.close();//关闭资源。
}
}
20.5字节缓冲区流对象:BufferedInputStream ,BufferedOutputStream
字节流缓冲区和字符流缓冲区一样,都是为了增强流对象的功能,字节缓冲区流也是使用装饰设计模式,用于装饰字节流,其构造函数可以接收字节流的所有子类。
字节缓冲区流中维护一个8KB临时容器提高字节流的读写效率。
如:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo{
public static void main(String []args)throws IOException{
demo_1();
}
publicstaticvoid demo_1()throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(new File("d:\\a.txt")));//创建字节缓冲区输入流对象,关联源文件
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(new File("d:\\b.txt"),true));//创建字节缓冲区输出流对象,关联目标文件。
int len=0;//定义一个变量,记录读取到的字节数。
while((len=bis.read())!=-1){//循环读取字节数据并存储到字节数组中。
bos.write(len);//通过字节输出流写入目标文件。
bos.flush();//刷新缓冲区。
}
bos.close();//关闭资源。
bis.close();//关闭资源。
}
}
总结:字符流与字节流的读写操作非常相似,只不过字符流中使用了字符数组,而字节流使用的是字节数组。其读取和写入的方式一样的。
20.6 IO流的异常处理
IO流在读写操作中,因为许多方法都抛出了异常,所以在不声明异常的情况下,就要对异常进行处理。按照异常处理的原则,对IO流的异常进行处理如下:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo{
public static void main(String []args) {
demo_1();
}
public static void demo_1(){
//1,将流对象的声明放在try块的外面。
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
try{
//2,流对象的创建放在try块内部
bis=new BufferedInputStream(new FileInputStream(new File("d:\\a.txt")));//创建字节缓冲区输入流对象,关联源文件
bos=new BufferedOutputStream(new FileOutputStream(new File("d:\\b.txt"),true));//创建字节缓冲区输出流对象,关联目标文件。
int len=0;//定义一个变量,记录读取到的字节数。
while((len=bis.read())!=-1){//循环读取字节数据并存储到字节数组中。
bos.write(len);//通过字节输出流写入目标文件。
bos.flush();//刷新缓冲区。
}
}catch(IOException e){
}finally{
//3,流的关闭要放在finally块中,并进行健壮性判断。
if(bos!=null)//因为,如果流对象创建失败,如果调用close()方法关闭资源,就会抛出异常,所以进行判断。
try {
bos.close();//关闭资源。
} catch (IOException e) {
thrownew RuntimeException("关闭失败");
}
if(bis!=null)//因为,如果流对象创建失败,如果调用close()方法关闭资源,就会抛出异常,所以进行判断。
try {
bis.close();//关闭资源。
} catch (IOException e) {
throw new RuntimeException("关闭失败");
}
}
}
}
20.7键盘录入
IO流对象操作的数据,有些是文件中数据,还可能键盘录入的数据。
键盘录入:System.in;
其实就是调用System类中的静态子段in,能够一个字节输入流:InputStream= System.in;
所以,键盘录入也就是用字节流读取键盘录入的数据。
如:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
public class IODemo{
public static void main(String []args) {
demo_1();
}
public static void demo_1() {
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
int len=0;
try {
bis=new BufferedInputStream(System.in);//读取键盘录入数据。
bos=new BufferedOutputStream(System.out);//输出键盘录入数据到控制台。
while((len=bis.read())!=-1){
bos.write(len);
bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
if(bis!=null)
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
if(bos!=null)
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
PrintStream=System.out;
PrintStream是OutputStream的子类,System.out代表控制台。以上代码将键盘录入的数据,输出到了控制台。
20.8转换流:InputStreamReader ,OutputStreamWriter
转换流,是字节流和字符流间的桥梁,可以实现字节流转字符流和字符流转字节流。
什么时候需要用转换流?
1,当源和目标数据都是字节数据,而需要用字符流对他们进行操作时。
2,需要用指定编码表进行编码或解码时,因为转换流的输入流和输出流都可以使用指定的编码表进行编解码。
如:需要用字符流操作键盘录入的数据。因为键盘录入是字节流,所以需要用转换流将键盘录入的数据转换成字符流。
再由字符流进行操作。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class IODemo{
public static void main(String []args) {
demo_1();
}
public static void demo_1() {
BufferedReader br=null;
BufferedWriter bw=null;
int len=0;
try {
br=new BufferedReader(new InputStreamReader(System.in));//使用转换流,将键盘录入转换成字符流。
bw=new BufferedWriter(new OutputStreamWriter(System.out));//System.out.是字节流,所以把字符流再转成字节流。
while((len=br.read())!=-1){
bw.write(len);
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
if(br!=null)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
if(bw!=null)
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
当需要用指定的码表进行编码和解码时,可以使用转换流。
InputStreamReader
(InputStream in,String charsetName)//charsetName
表示指定的码表。
OutputStreamWriter
(OutputStream out,String charsetName)//
同上
如:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class IODemo{
public static void main(String []args) {
demo_1();
}
public static void demo_1() {
BufferedReader br=null;
BufferedWriter bw=null;
int len=0;
try {
br=new BufferedReader(new InputStreamReader(System.in,"ISO8859-1"));//使用转换流,将键盘录入转换成字符流。
bw=new BufferedWriter(new OutputStreamWriter(System.out,"ISO8859-1"));//System.out.是字节流,所以把字符流再转成字节流。
//上面的转换流中,都指定了码表。
while((len=br.read())!=-1){
bw.write(len);
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
if(br!=null)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
if(bw!=null)
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:用什么码表编码,就用什么码表解码。
20.9字符打印流:PrintWriter,字节打印流:PrintStream
PrintWriter是Writer的字类,是用于将数据打印到目的地的输出流。
PrintWriter的构造函数,可以接收:字节流,File对象,字符串路径,字符流,并有自动刷新的功能和指定编码表。
构造函数:
PrintWriter(File file) PrintWriter(File file,String csn) PrintWriter(OutputStream out) PrintWriter(OutputStream out, boolean autoFlush) PrintWriter(String fileName) PrintWriter(String fileName,String csn) PrintWriter(Writer out) PrintWriter(Writer out, boolean autoFlush) |
PrintWriter对象的print(),println()方法,可以将数据打印到目的地,并保证数据的原有表示形式。
且print().println()方法可以接收各种数据类型参数,如:int,long,float,double,字符数组等,
而writer()方法,在某些情况下并不能保证按原有表示形式写入目的地的。
且,PrintWriter对象,可以将数据输出到一个字节流上。
PrintStream字节打印流,相当于:System.out;此流与其他输出流不同,永远不会抛出IOException.
构造函数:
PrintStream(File file) PrintStream(File file,String csn) PrintStream(OutputStream out) PrintStream(OutputStream out, boolean autoFlush) PrintStream(OutputStream out, boolean autoFlush,String encoding) PrintStream(String fileName) PrintStream(String fileName,String csn) |
PrintStream可将数据打印到File对象,字节流,字符串路径所表示的目的地。也具有自动刷新的功能。将数据自动刷入目的地。其print().println()方法可以接收各种数据类型参数,如:int,long,float,double,字节数组,字符数组等,也能够保持数据原有的表示形式。
如:
public class IODemo{
public static void main(String []args)throws FileNotFoundException {
demo_1();
}
public static void demo_1()throws FileNotFoundException {
PrintWriter pw=new PrintWriter("d:\\b.txt");
pw.print(97);//文件中的内容是:97,但是write(97),文件中的内容是:a
pw.flush();
pw.close();
}
}
字节流和字符流都有很多的流对象,如何去明确具体使用哪一个对象呢?
总结,流对象的操作规律。
1,分析源和目的
操作源的流:InputStream ,Reader
操作目的的流:OutputStream ,Writer
2,是否是纯文本数据
源,是纯文本数据:Reader,否则,InputStream
目地,是纯文本数据:Writer,否则,OutputStream
3,分析设备
源:
硬盘:File 键盘:System.in 内存:数组 网络:Socket流
目的:
硬盘:File 键盘:System.out 内存:数组 网络:Socket流
4,是否需要额外功能
1,是否需要缓冲区:
源:BufferedReader ,BufferedInputStream
目的:BufferedWriter ,BufferedOutputStream
2,是否需要转换流
源:InputStreamReader
目的:OutputStreamWriter
……
---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------