三 流资源类结构
1 Java中的流资源可操作文件中的数据 – IO流
① 将数据写入到指定的文件中
② 将数据从指定的文件中读出
2 分类 – 四大基流,八大子流
按照流向分:输入流和输出流
按照操作数据源的类型分:
① 字符流
Reader – 字符输入流 – InputStream – 基流
FileReader/BufferedReader
Writer – 字符输出流 – OutputStream – 基流
FileWriter/BufferedWriter
② 字节流
InputStream – 字节输入流
FileInputStream/BufferedInputStream
OutputStream – 字节输出流
FileOutputStream/BufferedOutputStream
③ 子流的名字,前缀表示该子流操作的是什么(File表示文件本身,Buffered表示存储缓存区);后缀表示该子流属于那种流资源
3 由于四大基流都是抽象类,仅参考,具体实现操作要通过对应的子流完成
四 字符流 – 字符输出流系列 Writer体系
1 Writer类-用于写入字符流的抽象类
// Writer类设计
public abstract class writer
Writer – FileWriter/BufferedWriter
IO流通用编写的步骤
① 将对象声明放在try块外 ,并且赋初始值为null
② 在try块中实例化对象,编写操作数据的代码
③在finally块中编写关闭资源代码,一定要在关闭前进行对象非空判断
2 FileWriter类设计–需要flush
// Writer类设计 可实例化FileWriter类
Class FileWriter
// 根据指定文件名直接构造一个新的文件
public FileWriter(String fileName)
// 例子
public class Test {
public static void main(String[] args) {
// 提升作用域,并赋初值为null
FileWriter fw=null;
try {
// 项目目录下创建play.txt
fw=new FileWriter("play.txt");
// 向文件中添加数据
// public Writer append(char ch) 写一个字符
fw.append('a');
// public void write() 写一个字符(可以是ASCII码)
fw.write(97);
// public void write(char[] chs) 写一个字符数组
fw.write(new char[]{'c','q','q'});
// public void write(char[] chs,int start,int len) 写入某字符数组的其中一段(含首尾)
fw.write(new char[]{'a','b','h'},1,2);
// public void write(String str) 写入字符串
// 换行操作 java \n linux \n windows \r\n
fw.write("想吃橙子了\r\n");
// public void write(String str,int start,int len) 写入字符串的一部分
fw.write("风也温柔",1,3);
// 将flush之前的内容写入文件
fw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 若资源没关闭,将资源关闭
if(fw!=null){
// 当结束所有操作后,关闭流资源,并且将最后书写的内容加载到文件中
fw.close();
System.out.println("资源fw已关闭");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
① FileWriter构造器必须传文件名,不能带有路径
② 使用FileWriter可以直接创建文件,并且如果没有就创建,如果有就覆盖
③ 可用flush方法将前面的内容加载到指定文件中
④ close方法 当结束所有操作后,关闭流资源,并且将在最后书写的内容加载到指定文件中
⑤ 可通过转义字符实现换行,但此写法Windows下识别换行 但linux识别不了 \r\n,为了避免此类问题定义了一个新的类实现操作,也就是BufferedWriter类
3 BufferedWriter类
常用方法,与Writer类同名方法用法一致,参考FileWriter
// Writer的具体实现类
Class BufferedWriter
// 根据Writer的实现对象,于缓存区定义,创建默认大小的输出缓冲区的缓冲字符输出流
public BufferedWriter(Writer out)
// 例子
public class Test2 {
public static void main(String[] args) {
// 声明IO流对象,定义在try外,赋值为null
BufferedWriter bw = null;
try {
// 实例化对象,创建一个名为"Bf.txt"的新文件,并将其用于创建一个新的BufferedWriter对象bw。
// BufferedWriter类是用于写入文本的高效类,而FileWriter类是用于将字符写入文件的类。
bw=new BufferedWriter(new FileWriter("Bf.txt"));
bw.write("今晚月色很美");
// 换行操作
bw.newLine();
bw.write("风也温柔");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(bw!=null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五 字符流 – 字符输入流系列 Reader 体系
1 Reader–用于读取字符的抽象类
// Reader类设计
public abstract class Reader
Reader – FileReader/BufferedReader
2 FileReader类设计
// 用于读取字符流 可实例化Reader类对象
Class FileReader
// 例子-读一个
public class Test3 {
public static void main(String[] args) {
FileReader fr=null;
try {
fr=new FileReader("play.txt");
// public int read() 读取一个字符
// public int read(char[] chs) 读取多个字符存放在指定数组中,最后显示数组中的内容
// public int read(char[] chs,int start,int len)
// 读取多个字符存放在指定数组中,从start位置开始,执行len的长度
// 文件字符不确定,故读取时会读取直到文件末尾,不确定循环次数时用while循环
// read方法返回-1表示到文件末尾,若不是-1则表示指定字符对应的ASCII值
// 定义接收读取数据的变量
int data=0;
while (((data=fr.read())!=-1)){
System.out.println((char)data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 例子-读一组
public class Test3 {
public static void main(String[] args) {
FileReader fr=null;
try {
fr=new FileReader("play.txt");
// public int read() 读取一个字符
// public int read(char[] chs) 读取多个字符存放在指定数组中,最后显示数组中的内容
// public int read(char[] chs,int start,int len)
// 读取多个字符存放在指定数组中,从start位置开始,执行len的长度
// 文件字符不确定,故读取时会读取直到文件末尾,不确定循环次数时用while循环
// read方法返回-1表示到文件末尾,若不是-1则表示指定字符对应的ASCII值
// 定义一个临时存放读取到数据的数组
char[] chs=new char[1024*5];
int data=0;
while (((data=fr.read(chs))!=-1)){
// 数组定义过大时,直接打印数组会打印出多余的字符,故需将字符数组转为String
System.out.println(new String(chs,0, data));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
① 中文解析是有可能乱码:是因为文件的编码集与系统编码集不一致,需要调整编码集
② 读一个的方法是通过计算机底层的游标指针频繁的在文件和指定显示区域之间移动,对内存资源消耗大
③ 读一组是将所有的数据先读取到临时存储区域,最后将区域中的内容整体显示
④ 读一组降低了内存资源的消耗,但是定义字符数组长度不好确定,过大则浪费内存空间,此时可用BufferedReader解决
3 BufferedReader类设计
// 从字符输入流读取文本,缓冲字符,提供字符,数组和行的高效读取
public class BufferedReader
// 创建指定大小的输入缓冲区的缓冲字符输入流,参数为(阅读器,缓冲区大小)
public BufferedReader(Reader in,int sz)
// 使用默认大小输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in)
// 读一行文字,一行指由换行符\n或回车符\r中的任何一个或随后的换行符终止,至流的末尾则返回null
public String readLine()
// 例子
public class Test4 {
public static void main(String[] args) {
BufferedReader br=null;
try {
br = new BufferedReader(new FileReader("play.txt"));
String data=null;
while ((data=br.readLine())!=null){
System.out.println(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
readLine方法的实现原理
// 模拟BufferedReader,并为每一行加上行号
public class DIYBufferedReader extends Reader {
private Reader in;
private int line;
public DIYBufferedReader(Reader in){
this.in=in;
}
// 实现readLine方法,读一个拼接一个,读到linux \n 或者windows \r\n时结束此行
// windows读到\r 表示跳出本次循环,执行下一次
// 以StringBuffer/StringBuilder完成String拼接
public String DIYReadLine() throws IOException{
// 显示数据的行号,自1递增
line++;
// StringBuffer拼接
StringBuffer sb=new StringBuffer();
// 定义临时存数据的变量,调用读的方法
int data=0;
// 循环读取数据
while ((data=in.read())!=-1){
// 读到\r跳过本次循环
if('\r'==data){
continue;
}else if('\n'==data){
return line+""+sb.toString();
}else {
sb.append((char)data);
}
}
// 当读到最后一行 没有\n结尾 读取sb无法返回时需判断sb长度是否大于0
if(sb.length()>0){
return line+""+sb.toString();
}
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
// 测试
public class DIYTest {
public static void main(String[] args) {
DIYBufferedReader dbr=null;
try {
dbr=new DIYBufferedReader(new FileReader("play.txt"));
String data=null;
while ((data=dbr.DIYReadLine())!=null){
System.out.println(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(dbr!=null){
try {
dbr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
流总结
在流中定义操作数据的多种方法实质上都是调用计算机操作系统的写一个或者读一个的方法:
① Writer系列
写一个的方法(append(char ch)/write(int ch))
写一堆(write(String data)/write(char[] cbuf))
② Reader系列
读一个(read())
读一组/行(read(char[] cbuf)/readLine())