目录
概述
- IO处理的对象其实就是内存与外围设备(硬盘,U盘,打印机等),分为两大类:输入流(从硬盘到内存),输出流(从内存到硬盘)
- 字节流:处理所有数据。
- 字符流:只能处理字符,之所以有字符流,是因为存在GBK、Unicode等不同的码表,相同的字节根据不同的码表解析出的文字不一样,于是便将码表与字节流进行了封装,产生了字符流。(读取文字字节数据后,先查编码表,获取对应的文字,再对文字进行操作)
体系结构
一般来说,子类名的后缀是父类名,子类名的前缀表示子类的功能。
字符流
顶层父类:Reader、Writer
FileWriter(字符输出流)
FileWriter fw=new FileWriter(".\\demo02.txt",true);
// new对象之后,便创建了相应的文件与流对象,默认根目录为项目目录(与src同级别),而不是此java文件目录
// 没有则创建,有,第二个参数为true续写,第二个参数为false覆盖
fw.write("haha");
// 内存中写入,未保存
fw.flush();
// 表示保存,防止断电后数据在内存中丢失
fw.close();
// 表示保存并退出,关闭流对象
标准的IO流处理异常
- try 外面声明 fw=null
- try 内部为fw赋值对象
- finally 写fw.close语句,本身还需要再围绕try catch 以防止关闭异常
- finally 在写fw.close之前,需要判断是否fw==null以防止之前的try没有读到文件,报空指针异常,这样的话根本就没有流可以关闭。
FileReader(字符输入流)
- 使用read()方法读取单个字符,返回数字。
- 文件在存入的时候,肯定会有首部标识和尾部标识,使用read()方法的时候,是肯定要读取到末尾的,如果读到了末尾标识,将返回-1(这里其实是走了底层)
使用char[] buffer读取示例(自制缓冲区)
FileReader fr=new FileReader("demo02.txt");
char[] buf=new char[1024];
//一般大小为1024
int len;
while((len=fr.read(buf))!=-1) {
System.out.println(new String(buf,0,len));
}
要注意为什么不直接打印buffer,之前存在buffer的数据,在buffer重新读取的时候不会自动清空,毕竟在堆内存只有一个buffer,java没有义务在每次读取的时候先把这个buffer里面的内容清空,如果读到了就覆盖,如果没读到就不覆盖还是以前的东西,我们只能通过返回值来判断读到哪个地方的数据是新旧数据分界线。
字符流缓冲区(java提供的缓冲区)
缓冲区其实主要目的是对两个不同的流进行关联(比如将一个文件内容复制到另一个文件,需要两个流,而缓冲区便是对这两个流进行关联的)
缓冲区的出现其实大大省去的频繁的new,提高了性能。缓冲区仅仅起到了提高效率的作用,没有调用底层。关闭缓冲区本质上其实是关上了流。
BufferedWriter
FileWriter fr=new FileWriter("demo03.txt");
BufferedWriter bufr=new BufferedWriter(fr);
bufr.write("hahaaxiefafe");
bufr.newLine();
//newLine()缓冲区特有
bufr.write("hahaaxiefafe");
bufr.flush();
bufr.close();
其实将流对象赋给构造函数后,以后对流的各种处理直接操作这个缓冲区就行了。
BufferedReader
BufferedReader是读的缓冲区,还提供了特有的readLine()方法。注意返回的数据不包含换行符!!!
FileReader fr=new FileReader("demo03.txt");
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null) {
System.out.println(line);
//注意readline()返回的每一行不包含换行符号!
}
bufr.close();
字节流
- 顶层父类:InputStream、OutputStream
- 缓冲区:缓冲区使用的数组是byte[] 而不是char[] 因为最小单位和字符流不一样
- 注意一点:写数据的时候不需要临时缓冲,因为不需要像字符流那样编码解码!所以直接write()方法之后就会在目的地写文件了。FileOutputStream的flush()方法其实内部根本啥都不做!当然close()还是有意义的。
- fis.available()返回文件大小(字节表示)
举例——复制图片文件
- 自己设计buffer
FileOutputStream fos=new FileOutputStream("bat.jpg");
FileInputStream fis=new FileInputStream("batman.jpg");
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
- 使用提供的buffer
FileOutputStream fos=new FileOutputStream("bat.jpg");
FileInputStream fis=new FileInputStream("batman.jpg");
BufferedOutputStream bufo=new BufferedOutputStream(fos);
BufferedInputStream bufi=new BufferedInputStream(fis);
int len=0;
while((len=bufi.read())!=-1){
bufo.write(len);
}
bufo.close();
bufi.close();
注意如果不用buffer,使用上面读一个字节写一个字节的方式效率会非常慢!!!
装饰设计模式
描述
当对一组对象的功能进行增强的时候,就可以使用该模式进行问题的解决。其实IO里的buffer就是使用装饰设计模式。
装饰和继承都能实现一样的特点:对功能扩展增强。
解释原因:只是为了扩展功能而进行继承会使得体系越来越臃肿,所以会有装饰设计模式,可以这样比喻:我们有一堆相同的对象,谁需要扩展功能,那就把它給穿上一层“铠甲”(放入装饰类的构造函数),让它自己有扩展功能就好了。其它的对象不需要穿铠甲的,就不需要了。这样与继承相比,无疑是简单明了的,如果都继承的话,我这个实例即使不想扩展功能也不行,因为我继承了之后就“天赋异禀”了,这个能力与生俱来,穿铠甲就没有任何意义了。身边都是一群一样天赋的人,我的优越性又有何在呢。
拿BufferWriter举例子,这就是铠甲,穿上这层铠甲的Writer写效率会大大增强!不穿的就效率低。
代码示例
public class NewPerson {
private Person p;
public NewPerson(Person p) {
this.p=p;
}
public void eat() {
System.out.println("have a couple of tea");
p.eat();
System.out.println("have some wines");
}
}
注意
装饰类和被装饰类必须同属于一个接口或者父类。即:铠甲与穿铠甲的骑士同属于一个父亲。
本文深入讲解Java中的IO流概念,包括字节流与字符流的区别,标准IO流处理,异常处理,以及装饰设计模式的应用。详细介绍了FileWriter、FileReader、BufferedWriter和BufferedReader的使用方法,并通过实例演示了如何复制图片文件。
4万+

被折叠的 条评论
为什么被折叠?



