javaIO流
其他常用与流有关的对象:
字节流 | 字符流 |
---|---|
文件类 | File |
打印流 | PrintStream、PrintWriter |
管道流 | PipedInputStream、PipedOutputStream |
序列流 | SequenceInputStream |
对象序列化流 | SequenceInputStream |
IO流:用于处理设备上的数据
设备:硬盘,内存,键盘录入
IO流具体分类:
1.根据处理的数据类型不同:字节流和字符流
2.根据流向不同:输入流和输出流
3.根据功能不同:节点流和处理流
|
字节流 | 字符流 |
---|---|---|
输入流 | InputStream(读) | Reader |
输出流 | OutputStream(写) | Writer |
字符流的由来:
因为文件编码的不同,而有了对字符进行高效操作的字符流对象。
原理:其实就是基于字节流读取字节时,去查了指定的码表。
字节流和字符流的区别:
1.字节流读取的时候,读到一个字节就返回一个字节。字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8中是3个字节)时,先去查指定的编码表,将查到的字符返回。
2.字节流可以处理所有类型数据,如图片,mp3,avi。而字符流只能处理字符数据。
结论:只要是处理纯文本数据,就要优先考虑使用字符流。
节点流和处理流:
①节点流为可以从一个特定的数据源(节点)读写数据。
②处理流是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
IO的体系,所具备的基本功能就有两个:读 和 写。
基本的读写操作方式:
因为数据通常都以文件形式存在,所以就要找到IO体系中可以用于操作文件的流对象。通过名称可以更容易获取该对象。因为IO体系中的子类名后缀绝大部分是父类名称。而前缀都是体现子类功能的名字。
Reader
|--InputStreamReader
|--FileReader:专门用于处理文件的字符读取流对象。
Writer
|--OutputStreamReader
|--FileWriter:专门用于处理文件的字符写入流对象。
Reader中的常见方法(数据单位为字符16bit):
int read():读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1。
int read(char[]):将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1。
void close():关闭流释放内存资源
Writer中常见的方法:
write(int c):将一个字符写入到流中。
write(char[] cbuf):将一个字符数组写入到流中。
write(String str):将一个字符串写入到流中。
flush():刷新流,将流中的数据刷新到目的地中,流还存在。
close():关闭资源,在关闭前会先调用flush(),刷新流中的数据去目的地。
FileWriter:
该类没有特有的方法,只有自己的构造函数。
特点在于:
1.用于处理文本文件
2.该类中有默认的编码表
3.该类中有临时缓冲。
构造函数:在写入流对象初始化时,必须要有一个存储数据的目的地。
FileWriter(String filename):调用系统资源,在指定位置,创建一个文件,如果该文件已经存在,会被覆盖。
FileWriter(String fileName, boolean append):当传入的boolean类型值为true时,会在指定文件末尾处进行数据的续写。
FileReader:
用于读取文本文件的流对象,用于关联文本文件。
package com.xue.javaIO;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* Created by xue on 2016-10-12.
*/
public class DemoIO {
public static void main(String[] args) {
DemoIO demoIO = new DemoIO();
//demoIO.Demo001();
//demoIO.Demo002();
demoIO.Demo003();
}
/**
* 将文本数据存储到一个文件中
* 当指定绝对路径时,定义目录分隔符有两种方式:1.反斜线 \\ 要写两个。2.斜线 / 写一个即可。
*/
public void Demo001() {
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt");
fw.write("abc中国人");
fw.flush();//对缓冲区的数据进行刷新,将数据刷到目的地中。
fw.write("KKK");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println();
}
}
}
}
//读取一个已有的文本文件,将文本数据打印出来
public void Demo002(){
FileReader fr = null;
try {
fr = new FileReader("D:/webpro/java001.java");
int ch=0;
while ((ch=fr.read())!=-1){
System.out.print((char)ch);
}
fr.close();
} catch (FileNotFoundException e) {
System.out.println("文件没有找到");
}catch (IOException e){
e.printStackTrace();
}
}
//读一个字符就存入字符数组里,读完1Kb再打印
public void Demo003(){
FileReader fr = null;
try {
fr = new FileReader("D:/webpro/java001.java");
char[] buf = new char[1024];
int len;
while ((len=fr.read(buf))!=-1){
System.out.print(new String(buf,0,len));
}
fr.close();
} catch (FileNotFoundException e) {
System.out.println("文件没有找到");
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
字符流的缓冲区:
缓冲区的出现提高了对流的操作效率。
原理:其实就是将数组进行封装。
- 对应的对象:
- BufferedWriter:特有方法:newLine():跨平台的换行符。
- BufferedReader:特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回null。
readLine():方法的原理: 其实缓冲区中的该方法,用的还是与缓冲区关联的流对象的read方法。只不过每一次读到一个字符,先不进行具体操作,先进行临时存储,当读取到回车标记时,将临时容器中存储的数据一次性返回。
在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象存在。其实缓冲内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。
代码上的体现:
写入缓冲区对象。
//建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。
BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
bufw.write("abce");//将数据写入到缓冲区
bufw.flush();//对缓冲区的数据进行刷新。将数据刷到目的地中。
bufw.close();//关闭缓冲区,其实关闭的是被包装在内部的流对象。
上面代码使用了匿名的FileWriter流,这个流无法关闭。之所以需要关闭流资源,是为了释放资源,因为io是操作系统的动作,例如如果没有关闭,那么这个IO资源就一直被java进程占用,知道java应用结束,这期间操作系统的其他进程就不能访问该IO资源了。我的看法是既然是匿名对象,肯定是要被回收的,但是什么时候回收就不一定了,因此建议你自己处理输入输出流的关闭操作。
字节流
抽象基类:InputStream,OutputStream。
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
注意:字符流使用的数组是字符数组。char[] chs
字节流使用的数组是字节数组。byte[] bt
字节流可以操作任何数据。
转换流
特点:
1.是字节流和字符流之间的桥梁。
2.该流对象中可以对读取到的字节数据进行指定编码表的编码转换。
什么时候使用呢?
1.当字节和字符之间有转换动作时。
2.流操作的数据需要进行编码表的指定时。
具体的对象体现:
InputStreamReader:字节到字符的桥梁
OutPutStreamWriter:字符到字节的桥梁
这两个流对象是字符流体系中的成员,它们有转换作用,而本身又是字符流,所以在构造的时候,需要传入字节流对象进来。
构造函数:
InputStreamReader(InputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK.
InputStreamReader(InputStream,String charSet):通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream)
OutputStreamWriter(OutputStream,String charSet)
操作文件的字符流对象是转换流的子类。
Reader
|--InputStreamReader
|--FileReader
Writer
|--OutputStreamWriter
|--FileWriter
转换流中的read方法已经融入了编码表,在底层调用字节流的read方法时将获取的一个或者多个字节数据进行临时存储,并去查指定的编码表,如果编码表没有指定,查的是默认码表,那么转换流的read方法就可以返回一个字符,比如中文。
转换流已经完成了编码转换的动作,对于直接操作的文本文件的FileReader而言,就不用在重新定义了,只要继承转换流,获取其方法,就可以直接操作文本文件中的字符数据了。
注意:在使用FileReader操作文本数据时,该对象使用的是默认的编码表。如果要使用指定编码表,必须使用转换流。
FileReader fr = new FileReader("a.txt");//操作a.txt的中的数据使用的本系统默认的GBK。
操作a.txt中的数据使用的也是本系统默认的GBK。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
这两句的代码的意义相同。
如果a.txt中的文件中的字符数据是通过utf-8的形式编码,那么在读取时,就必须指定编码表。那么转换流必须使用。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"utf-8");
IO流的使用规律总结。
解决的问题,就是在开发过程中具体要使用哪个流对象的问题。
1,明确 数据源,数据汇(数据的目的)
其实就是在明确要使用的IO体系,InputStream OutputStream Reader Writer
需求中操作的是源:意味着是 读。
InputStream Reader
需求中操作的是 目的: 意味着是 写
OutputStream Writer
2,明确 操作的数据是否是存文本数据?
是使用字符流
否字节流
是并且是源。Reader
是并且是目的 。Writer
通过前两个明确,明确了具体要使用的体系。
接下来应该明确具体的体系中要使用哪个对象。
3,明确 要操作的具体设备。每个设备都有对应的流对象。
源设备:
硬盘,能操作File的流对象都是。File开头。、
键盘,System.in。
内存,数组。
网络,socket流。
目的设备:
硬盘,能操作File的流对象都是。File开头。、
显示器,System.out。
内存,数组。
网络,socket流。
到第三步明确就可以找到具体的流对象了。
4,要明确是否需要额外功能?
需要高效吗? 用缓冲区,Buffered开头的。
需要编码转换吗? 转换流。
------------------------------------------------------
下面通过具体的需求,来利用上面的规律来确定流对象。
需求1:通过键盘录入数据,将数据保存到一个文件中。
明确一:用源码?有, 有目的吗? 有。
源:InputStream Reader
目的:OutputStream Writer
明确二:是存文本数据吗? 是
源:Reader
目的:Writer
明确三:具体设备。
源设备:键盘, System.in
目的设备:硬盘 操作File
通过上面三步明确大致可以确认
InputStream is=System.in;//源对象
FileWriter fw=new FileWriter("a.txt");//目的对象。
//将读取的字节存储到数组中 read(byte[])
//将字节数组转换成字符串。
//通过fw.write(String)写入到文件中。
//但是这样做比较麻烦,因为要明确的源是Reader ,需要将字节流转成字符流,这样操作文字就便捷了。
明确四:需要功能吗?
需要。 转换 字节-->字符。InputStreamReader
InputStreamReader isr=new InputStreamReader(System.in);
FileWriter fw=new FileWriter("a.txt");
//一次读取一个字符,将读到的字符写入。当然也可以定义字符数组缓冲区。
需要其他功能吗?
需要,高效。 Buffered
所以最终的流对象可以确认了。
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BUfferedWriter bufw=new BufferedWriter(new FileWriter("a.txt"));
//line=readLine();
//bufw.write(String);bufw.flush();