IO流基础(字符流)

来源:互联网 发布:62078端口入侵iphone 编辑:程序博客网 时间:2024/06/18 01:19


IO流: 用于处理设备之间的数据传输。

流:可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,对象都存在IO包中。
流也进行分类:
1:输入流(读)和输出流(写)。
2:因为处理的数据不同,分为字节流和字符流。 

字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

字节流其实就是字符流加编码表


那么为什么要有字符流呢?因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

注意:流的操作只有两种:读和写。

流的体系因为功能不同,但是有共性内容不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。

字节流:InputStream  OutputStream
字符流:Reader  Writer

在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。
==============================================================================================
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()
     |---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。特有方法 行读取String readLine(),方便对
文本数据的获取 不获取换行符,当返回null时,表示读到文件末尾
        |---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当
前行号。从当前行开始读取  此流还具备 行的读取 readLine()
     |---InputStreamReader:是字节流通向字符流的桥梁:读取字节并将其按编码表解码为字符。  InputStreamReader(InputStream in,Charset) 接收的是字节输入流对象 InputStream  字节输入流-->字符读取流      特有方法:readLine() 以换行符"\r\n"为结束标记,直到读取到换行符才结束读取数据否则一直阻塞在数据读取阶段
     |---FileReader:操作文件用来读取字符文件的便捷类。以默认编码表 操作文件。  特点:能够操作文件的字符流
     |---CharArrayReader: 源目的为内存
     |---StringReader:


Reader 主要方法
int read()  读取单个字符。返回作为整数读取的字符。。如果已到达流的末尾,则返回 -1
int read(char[] cbuf) 
          将字符读入数组。返回读取的字符个数
int read(CharBuffer target) 
  试图将字符读入指定的字符缓冲区。返回:数量
boolean ready()   判断是否准备读取此流。 
 void reset()   重置该流。 
 long skip(long n)  跳过字符。

 

FileReader使用Reader体系,读取一个文本文件中的数据。返回 -1,标志读到结尾。import java.io.*;

FileReader一个专门用于操作文件的Reader子类对象。

读取数据的第一种方式:一个字符一个字符的读取

              创建可以读取文本文件的流对象,FileReader让创建好的流对象和指定的文件相关联。

              FileReader fr = newFileReader("demo.txt");

        int ch = 0;//用于存储读取流读取的字符

        while((ch =fr.read())!= -1) { //条件是没有读到结尾

                     System.out.println((char)ch); //读取一个字符,输出。

              }

              fr.close();   //finally中一定要关闭流

读取数据的第二种方式:第二种方式较为高效,自定义缓冲区字符数组char[]=new char[1024]

              FileReader fr = newFileReader("demo.txt"); //创建读取流对象和指定文件关联。

              //因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度一般是1024的整数倍。

              char[]buf = new char[1024];//充当缓冲区

              intlen = 0; //用于存储每次读取的字符个数

              while((len=fr.read(buf)) != -1) {

                     System.out.println(newString(buf,0,len)); //读取一次就需要处理一次,写入或输出

              }

              fr.close();


-------------------------------------------------
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
     |---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。特有方法 换行方法 void newLine();
     |---OutputStreamWriter:是字符流通向字节流的桥梁:使用指定的编码表 charset 将要写入流中的字符编码成字节。接收的是字节输出流对象 
OutputStream    字符写入流-->字节输出流    特有方法newLine()
        |---FileWriter:操作文件用来写入字符文件的便捷类。以默认编码表 操作文件。
     |---PrintWriter:
     |---CharArrayWriter:
     |---StringWriter:

Writer主要方法 
abstract  void close()  关闭此流,但要先刷新它。 
abstract  void flush()  刷新该流的缓冲。 
 void write(char[] cbuf)  写入字符数组到流。 
abstract  void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分到流。 
 void write(int c)    写入单个8位字符到流。 
 void write(String str)   写入字符串到流。 
 void write(String str, int off, int len) 
          写入字符串的某一部分到流。

字符流的写入 由于编码,使用到了缓冲区技术 所以 任何字符流必须刷新 才能写入目标     

单纯的字节流(没涉及到缓冲区的字节流)可以不用刷新直接写入目标 

=======================================================================
字符流的特点。
既然IO流是用于操作数据的,
那么数据的最常见体现形式是:文件。
那么先以操作文件为主来演示。
需求:在硬盘上,创建一个文件并写入一些文字数据。
FileWriter一个专门用于操作文件的Writer子类对象。

代码演示:

import java.util.*;import java.io.*;  class FileWriterDemo {public static void main(String[] args) throws IOException{//FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。//创建一个FileWriter对象。该对象一被创建就必须要明确被操作的文件名//而且 该文件会被创建在指定目录下,,如果该目录下有同名文件 则新文件会覆盖替换旧文件//其实该步骤就是在明确数据要存放的目的FileWriter fw = new FileWriter("c:\\000Deno11.txt");//调用write方法,将字符串写入(流)内存中fw.write("zhangtingting");//刷新流对象中的数据 把流的字节传递给操作系统进行写入//fw.flush();//将字符串写入文件  需要两步fw.write("jiangzuyun");//fw.flush();//close()关闭流资源 但是在关闭之前 会自动刷新一次内部缓冲中的数据//和flush()的区别 :flush刷新后 流可以继续使用,close刷新后 会将流关闭fw.close();//关闭之前必须判断fw是否为空,如果fw=null那么fw.close()就会抛出NopointException空指针异常}}
close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。此方法必须描述在finally中

---------------------------------------------------
BufferedWriter 
  将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
特有方法 换行方法 void newLine();
是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,可以实现跨屏平台换行。
并非所有平台都使用新行符 ('\n') 来终止各行。window中的换行符:\r\n两个符号组成。 linux:\n。
bfw.write("huanhangcaozuo"+i);
bfw.newLine(); //写入换行符 达到换行的效果
//刷新即数据从写入文件 降低突然断电的文件丢失
bfw.flush();
----------------------------------------------------
FileReader:
   使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
FileReader一个专门用于操作文件的Reader子类对象。

读取数据的第一种方式:一个字符一个字符的读取
代码演示:

import java.io.*;class FileReaderDemo {public static void main(String[] args) {//通过FileReader创建一个文件读取流对象FileReader fi = null;try{//通过FileReader创建一个文件读取流对象 和指定文件名的文件相关联//要保证该文件是已经存在的 否则 会抛出异常 FileNotFoundExceptionfi = new FileReader("0Deno11.txt");//int read() 取单个字符返回的是int型数据  当读取到文件末尾时 返回-1int ch=0;while((ch = fi.read()) != -1){System.out.print((char)ch);//将返回的int数据强转为char}}catch (IOException e){System.out.println(e.toString());}finally{try{if(fi!=null)fi.close();}catch (IOException e){System.out.println(e.toString());}}}}
int Read(); 读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
返回 作为整数读取的字符,如果已到达流的末尾,则返回 -1 

读取数据的第二种方式:读取一定长度的字节数据存放到字节数组中 第二种方式较为高效,
自定义字符数组缓冲区 字符数组char[]=new char[1024]。一般定义缓冲字符数组长度为1024 不会占用太多内存

FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。
//因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度一般是1024的整数倍。
char[] buf = new char[1024];//充当缓冲区
int len = 0; //用于存储每次读取的字符个数
while(( len=fr.read(buf)) != -1) {
System.out.println(new String(buf,0,len)); //读取一次就需要处理一次,写入 或输出
}
fr.close();

 int read(char[] cbuf)  将字符读入数组。 返回:读取的字符个数,如果已到达流的末尾,则返回 -1 

练习:字符文件的拷贝:FileWriter和FileReader配合使用
文件复制
1,创建 目标文件用于存储被复制文件中的数据
2,定义读取流 关联 需要被复制的文件
3,将被复制的文件字符存到缓存区
4,从缓存区写入文件到目标文件
5,关闭资源


代码演示:

import java.io.*;class copyDemo {public static void main(String[] args) {FileWriter fw = null;FileReader fr = null;copy02(fw,fr);}//先全部读取 到缓冲区 然后一次全部写入到目标文件public static void copy01(FileWriter fw,FileReader fr){try{//创建一个被写入的文件 将流中的数据写入该文件fw = new FileWriter("00staticDemo.java");//关联 读取流 和 指定文件fr = new FileReader("F:\\java30\\d17\\staticDemo.java");//创建缓存区char[] buf = new char[1024];int len = 0;//读取 判断while ((len = fr.read(buf)) != -1)//fr.read(buf){//写入文件fw.write(buf,0,len);}}catch (IOException e){throw new RuntimeException("读写出错!");}//关闭 资源finally{try{if (fw!=null)fw.close();}catch (Exception e){throw new RuntimeException("读写出错!");}finally{try{if (fw!=null)fw.close();}catch (Exception e){throw new RuntimeException();}}}}public static void copy02(FileWriter fw,FileReader fr){try{fw = new FileWriter("00new01.txt");fr = new FileReader("F:\\java30\\d17\\new01.java");int hc = 0;while ((hc = fr.read())!= -1) //!=不等号之间 不要存有空格{fw.write(hc);}}catch (Exception e){throw new RuntimeException("读写出错!");}finally{if (fr!=null)try{fr.close();}catch (Exception e){throw new RuntimeException();}if(fw!=null)try{fw.close();}catch (Exception e){throw new RuntimeException();}}}}
-------------------------------------------------------------------------
BufferedReader
高效的缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 
是给字符读取流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
*特有方法 按行读取String readLine(),方便对文本数据的获取 不获取换行符,当返回null时,表示读到文件末尾*

String s = null; //用于存储读取到的行数据while ((s = bfr.readLine()) != null)  //不获取换行符{System.out.println(s);//处理 读取到的整行数据}

自定义readLine()功能:
1, 一个个读取单字符然后存储到 StringBuilder
2, 当遇到换行符时 返回读取的数据StringBuilder中的数据

public String myReadLine() throws IOException{StringBuilder sb = new StringBuilder();//用于存储读取行的数据 StringBuilder方便添加数据int ch = 0;while ((ch=fr.read())!=-1)  //fr.read()下一次阅读下一个{//windows中的换行符为 \r\n 由两字符组成if(ch=='\r')continue;if(ch=='\n')return sb.toString(); //当读取到的字符是'\n'时返回存储的字符串 return 结束整个方法sb.append((char)ch);}if(sb.length() != 0)return sb.toString();//当读到末尾时read()==-1用于返回读到的数据 也就是最后一行return null;}
----------------------------------------------------------------
LineNumberReade  extends BufferedReader
   跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
默认情况下,行编号从 0 开始,从第1行开始获取数据。该行号随数据读取在每个行结束符处递增,
并且可以通过调用 setLineNumber(int) 更改行号。
但要注意的是,setLineNumber(int) 不会实际更改流中的当前位置;它只更改将由 getLineNumber() 返回的值。

常用方法:
String readLine()  按行读取文本。 
int getLineNumber() 获得当前行号。 
void setLineNumber(int lineNumber) 设置当前行号。
int read(char[] cbuf, int off, int len)  将字符读入数组中的某一部分。 
void reset()   将该流重新设置为最新的标记。 
long skip(long n)跳过字符。

代码演示:

import java.io.*;class LineNumberReaderDemo{public static void main(String[] args) throws Exception{FileReader fr = new FileReader("mybufferedreader.java");LineNumberReader lnr = new LineNumberReader(fr);String line = null;lnr.setLineNumber(30);//设置第一行为31  默认为0从第一行开始读取while ((line = lnr.readLine())!=null){System.out.println(lnr.getLineNumber()+":"+line);}lnr.close();}}
===============================================================================

装饰设计模式。
IO中的使用到了一个设计模式:装饰设计模式。
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。

装饰设计模式解决:对一组类进行功能的增强。
装饰:写一个类(装饰类)对被装饰对象进行装饰;
 * 1、装饰类和被装饰对象要实现同样的接口;
 * 2、装饰类要持有一个被装饰对象;
 * 3、装饰类在实现接口时,大部分方法是靠调用被装饰对象来实现的,对于需要修改的方法我们自己实现;


代码演示:

class Person{public void chifan(){System.out.println("吃饭");}}class SuperPerson {private Person p ;SuperPerson(Person p){this.p = p;}public void superChifan(){System.out.println("开胃酒");p.chifan();System.out.println("甜点");System.out.println("来一根");}}class  PersonDemo{public static void main(String[] args) {Person p = new Person();//p.chifan();SuperPerson sp = new SuperPerson(p);sp.superChifan();}}

装饰和继承的区别:

MyReader    专门用于读取数据的类。

    |--MyTextReader

    |--MyMediaReader

    |--MyDataReader

    |--MyBufferReader

 

以前是通过继承将每一个子类都具备缓冲功能。

那么继承体系会复杂,并不利于扩展

现在优化思想。单独描述一下缓冲内容。

将需要被缓冲的对象传递进来。也就是谁需要被缓冲,谁就作为参数传递给缓冲区。

这样继承体系就变得很简单。优化了体系结构。

装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。

所以装饰类和被装饰类通常是都属于一个体系中的。从继承结构变成组合结构。


0 0
原创粉丝点击