Java提高(五)IO流

javaIO流


IO流对象继承关系

IO流对象继承关系

其他常用与流有关的对象:

字节流 字符流
文件类 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: 
用于读取文本文件的流对象,用于关联文本文件。

 
 
  1. package com.xue.javaIO;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileReader;
  4. import java.io.FileWriter;
  5. import java.io.IOException;
  6. /**
  7. * Created by xue on 2016-10-12.
  8. */
  9. public class DemoIO {
  10. public static void main(String[] args) {
  11. DemoIO demoIO = new DemoIO();
  12. //demoIO.Demo001();
  13. //demoIO.Demo002();
  14. demoIO.Demo003();
  15. }
  16. /**
  17. * 将文本数据存储到一个文件中
  18. * 当指定绝对路径时,定义目录分隔符有两种方式:1.反斜线 \\ 要写两个。2.斜线 / 写一个即可。
  19. */
  20. public void Demo001() {
  21. FileWriter fw = null;
  22. try {
  23. fw = new FileWriter("demo.txt");
  24. fw.write("abc中国人");
  25. fw.flush();//对缓冲区的数据进行刷新,将数据刷到目的地中。
  26. fw.write("KKK");
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }finally {
  30. if(fw!=null){
  31. try {
  32. fw.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. System.out.println();
  36. }
  37. }
  38. }
  39. }
  40. //读取一个已有的文本文件,将文本数据打印出来
  41. public void Demo002(){
  42. FileReader fr = null;
  43. try {
  44. fr = new FileReader("D:/webpro/java001.java");
  45. int ch=0;
  46. while ((ch=fr.read())!=-1){
  47. System.out.print((char)ch);
  48. }
  49. fr.close();
  50. } catch (FileNotFoundException e) {
  51. System.out.println("文件没有找到");
  52. }catch (IOException e){
  53. e.printStackTrace();
  54. }
  55. }
  56. //读一个字符就存入字符数组里,读完1Kb再打印
  57. public void Demo003(){
  58. FileReader fr = null;
  59. try {
  60. fr = new FileReader("D:/webpro/java001.java");
  61. char[] buf = new char[1024];
  62. int len;
  63. while ((len=fr.read(buf))!=-1){
  64. System.out.print(new String(buf,0,len));
  65. }
  66. fr.close();
  67. } catch (FileNotFoundException e) {
  68. System.out.println("文件没有找到");
  69. } catch (IOException e) {
  70. System.out.println(e.toString());
  71. }
  72. }
  73. }

字符流的缓冲区:

缓冲区的出现提高了对流的操作效率。 
原理:其实就是将数组进行封装。

  • 对应的对象: 
    • BufferedWriter:特有方法:newLine():跨平台的换行符。
    • BufferedReader:特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回null。

readLine():方法的原理: 其实缓冲区中的该方法,用的还是与缓冲区关联的流对象的read方法。只不过每一次读到一个字符,先不进行具体操作,先进行临时存储,当读取到回车标记时,将临时容器中存储的数据一次性返回。

  在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象存在。其实缓冲内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。

代码上的体现:

 
 
  1. 写入缓冲区对象。
  2. //建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。
  3. BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
  4. bufw.write("abce");//将数据写入到缓冲区
  5. bufw.flush();//对缓冲区的数据进行刷新。将数据刷到目的地中。
  6. 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();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值