黑马程序员——java基础学习--IO流

时间:2023-02-18 07:29:19
------- android培训java培训、期待与您交流! ----------


IO流

1、概述

IO是Input与Output的缩写。

  1)它具有以下特点:

    1)IO流用来处理设备间的数据传输。

    2)Java对数据的操作是通过流的方式。

    3)Java用于操作流的对象都在IO包中。

4)流分类:按操作数据分为:字节流和字符流;

           按流向分为:输入流和输出流。

    5)流只能操作数据,不能操作文件。

  2)字符流与字节流区别:

主要区别是他们的处理方式,字节流是最基本的,采用ASCII编码,所有的InputStream和OutputStream的子类都是,主要用在二进制数据处理,而字符流采用Unicode编码,按虚拟机encode来处理,要进行字符集的转化。

类名称              字节流              字符流

 输入            InputStream            Reader

 输出            OutputStream           Writer

    这四个类派生出来的子类名称都是以其父类名作为子类名的后缀,如InputStream子类FileInputStream;Reader子类FileReader。

2、字符流

  1)概述

字符流中的对象融合了编码表,使用的是当前系统的默认编码,它只用于处理文字数据,而字节流可以处理媒体(图片,音乐)数据。

最常见的数据体现形式是文件,专门有一个用于操作文件的Writer子类对象FileWriter,该流对象一被初始化就必须有文件存在。
  2)字符流

A、写入字符流使用方法:

 a、创建一个FileWriter对象,明确数据要存放的目的地。初始化就必须要明确被操作的文件,若该目录下已有同名文件,则将被覆盖。

  如:FileWriterfw = new FileWriter("D:\\Demo.txt")。

 b、调用write(String s)方法,将字符串写入到流中。

  如:fw.write("abcd")。

 c、调用flush()方法,刷新对象中的缓冲数据,将数据刷新到目的地。

  如:fw.flush()。

 d、调用close()方法关闭流资源。关闭前会刷新一次将数据刷新到目的地中。如:fw. close()。

    B、读取字符流使用方法:

 a、创建一个文件读取流FileReader对象,和指定名称的文件相关联,要保证该文件已经存在,否则将会发生FileNotFoundException异常。

 b、调用读取流对象的read()方法。read()方法是一次读一个字符,会自动往下读直到末尾。读取有两种方式:读取单个字符和通过字符数组进行读取

 c、调用close()方法将资源关闭。

 注意:windows系统中的文件内换行用\r\n两个转义字符表示,linux系统只用\n表示换行。

  3)close()和flush()的区别:

  flush()刷新后,流可以继续使用,而close()刷新后,将会关闭流,不可再写入字符流。

  4)为什么要关闭资源呢?

因为java写入数据是调用系统内部方式完成的,所以使用完系统资源后一定要关闭资源。
  5)续写文件的数据

通过构造函数FileWriter(Strings, boolean append)来完成,在创建对象时,传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
  6)异常

由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中需要对IOException异常进行try处理。
3、字符流缓冲区

对应的类:BufferedReader和BufferedWriter。

缓冲区的出现,提高了流对数据的读写效率,其要结合流才可以使用,所以在缓冲区创建前要先创建流对象,将流对象初始化到缓冲区构造函数中。 

缓冲技术的原理:对象中封装了数组,将数据存入后,一次性取出。

  1)写入流缓冲区(BufferedWriter)的使用步骤:

A、创建一个字符写入流对象。

  如:FileWriterfw = newFileWriter("D:\\Demo.txt")。

B、加入缓冲,将需要被提高效率的流对象作为参数传递给缓冲区的构造函数。如:BufferedWriter bufw = new BufferedWriter(fw)。

C、调用write()方法写入数据到指定文件。如:bufw.write("adcd")。

D、调用flush()方法刷新缓冲区。只要用到缓冲区,就一定要刷新,虽然关闭流同样会刷新,但是为了不出意外并保证数据存在,建议写入一次就刷新一次。如:bufw.flush()。

E、关闭缓冲区。其实就是关闭缓冲区中的流对象。如:bufw.close()。
  BufferedWriter缓冲区提供了一个跨平台的换行符newLine(),其可以在不同操作系统上调用,用作数据换行。

  2)读取流缓冲区(BufferedReader)的使用步骤:

A、创建一个读取流对象和文件相关联。

 如:FileReaderfr = new FileReader("D:\\Demo.txt ")。

B、建立缓冲区,将字符读取流对象作为参数传递给缓冲区对象的构造函数。

 如:BufferedReader bufr = newBufferedReader(fr)。

C、调用缓冲区提供的readLine()方法读取,如果到达文件末尾,则返回null。

 如:String s = bufr.readLine()。

D、关闭流资源。如:bufr.close()。
  3)BufferedReader缓冲区提供了一个一次读一行的方法readLine(),其方便于对文本数据的读取,当返回null时就表示读到了文件末尾,其只返回回车符之前的数据内容,不返回回车符。该方法的原理还是read()方法一次读一个的方法,因为无论读一行还是读取多个字符,其实都是在硬盘上一个一个读取。
  4)BufferedReader中有个直接的子类:LineNumberReader类,其是个装饰类,其特有的方法:

setLineNumber():设置初始行号;

getLineNumber():获取当前行号。
4、装饰设计模式
    对已有的对象进行功能增强,可定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该个类被称为装饰类。

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

  2)装饰和继承的区别:

    装饰模式比继承要灵活,避免了继承体系的臃肿,并降低了类与类之间的关系,从继承结构转为组合结构。

  装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,灵活性较强,所以装饰类和被装饰的类通常都是属于一个体系。

5、字节流

  1)概述:

    字节流和字符流的基本操作是相同的,但字节流可以操作媒体文件,由于媒体文件数据中都是以字节存储的,所以字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

字节输入流:InputStream

    字节输出流:OutputStream

  2)InputStream特有方法:

    int available():返回文件中的字节个数。

    可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断,但是如果文件较大,而虚拟机启动分配的默认内存一般为64M,此时数组长度所占内存空间就会溢出,所以当文件不大时可以使用。
  3)字节流缓冲区:同样是提高了字节流的读写效率。

    read():将字节byte型提升为int型,为了避免-1的发生。

    write():将int型强转为byte型。

    字节流缓冲区原理就是将数据拷贝一部分,读取一部分,循环直到数据全部读取完毕。

   具体过程如下:

   A、先从数据中读取固定数组长度的字节,存入定义的数组中,然后通过read()方法读取数组中的元素存入缓冲区;

   B、循环A这个动作,直到取出最后一组数据存入数组;数组可能未填满,但同样也取出包含的元素。每次取出的时候,都有一个指针在移动,读到数组结尾就自动回到数组头部。

   C、当文件中的数据全部都被读取后,read()方法就返回-1。

  4)注意事项:

    为什么字节流的read()方法返回值类型是int而不是byte?

因为我们判断读取结束是通过结尾标记-1来确定的,读取过程中就有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1,那么就会数据还没有读完,就出现结束的情况,所以将读到的字节进行int类型的提升(在保留原字节数据的前面了补24个0)。

在写入数据时,只写该int类型数据的最低8位。
6、键盘录入

标准输入设备:System.in,键盘;InputStream;

标准输出设备:System.out:控制台;PrintStream;

PrintStream是FilterOutputStream的子类。
  1)整行录入

使用输入流进行键盘录入时,只能一个字节一个字节进行录入,为了提高效率,可以自定义一个数组将一行字节进行存储,当一行录入完毕,再将一行数据进行显示,这种录入方式和字符流读一行数据的原理是一样的,相当readLine方法。

那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中的方法,而键盘录入的read方法是字节流InputStream的方法,那么就需要将字节流转成字符流再使用字符流缓冲区的readLine方法,这就需要用到转换流。
  2)转化流

转换流是字符流与字节流之间的桥梁,方便于字符流与字节流之间的操作,当字节流中的数据都是字符时,转成字符流操作更有效率。

A、字节流转成字符流(读取转换流)

InputStreamReader()

    为了提高效率,加入缓冲区BufferedReader进行键盘录入:

BufferedReader bufr =

       new BufferedReader(new InputStreamReader(System.in));

B、字符流转成字节流(输出转换流)

OutputStreamWriter()

也加入缓冲区BufferedWriter进行输出:

BufferedWriter bufw =

      new BufferedWriter(newOutputStreamWriter(System.out));   
7、流操作的基本规律
  1)通过三个明确来完成使用哪个流对象:

A、明确源和目的:

   源:输入流,InputStream和Reader;

   目的:输出流,OutputStream和Writer。

B、操作的数据是否是纯文本:

   是:字符流;

   否:字节流。

C、明确体系后,再通过设备来明确使用哪个具体的对象:

   源设备:内存,硬盘,键盘;

   目的设备:内存,硬盘,控制台。
  2)将一个文本文件中的数据存储到另一个文件中(复制文件):

A、源还是目的:是源,使用读取流:InputStream和Reader;

  明确体系:是操作文本,Reader;

 明确设备:硬盘上的一个文件,用Reader体系中可以操作文件的对象FileReader;

 是否需要提高效率:需要,加入Reader体系中缓冲区BufferedReader;

  FileReaderfr = new FileReader("D:\\a.txt");

 BufferedReader bufr = newBufferedReader(fr);

B、源还是目的:输出流:OutputStream和Writer;

  明确体系:是操作文本,Writer;

 明确设备:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。

 是否需要提高效率:需要,加入Writer体系中缓冲区BufferedWriter;

 FileWriter fw = new FileWriter("D:\\b.txt");

 BufferedWriter bufw = newBufferedWriter(fw);
  3)将键盘录入的数据保存到一个文件中:            

A、源:InputStream和Reader;

  明确体系,是不是纯文本:是,Reader;

 明确设备:键盘,System.in,为了操作键盘的文本数据方便,转成字符流按照字符串操作,所以就明确了Reader,然后用转换流InputStreamReader将System.in转成Reader。

  InputStreamReader  ir = new InputStreamReader(System.in);

 是否需要提高效率:需要

          BufferedReaderbufr = new BufferedReader(ir);

B、目的:OutputStream  Writer;

 明确体系,是否是存文本:是,Writer;

 明确设备:硬盘的一个文件,使用FileWriter;

       FileWriter fw = newFileWriter("c.txt");

 是否需要提高效率:需要

    BufferedWriter bufw =new BufferedWriter(fw);

  4)把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。

目的:OutputStream和Writer;

是否是存文本:是,Writer;

明确设备:硬盘上的一个文件,使用FileWriter;但是默认编码表为GBK,而要求指定编码表UTF-8,而指定编码表只有转换流可以,所以要使用对象的是OutputStreamWriter转换流。该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream;

  OutputStreamWriterosw =

new OutputStreamWriter(new FileOutputStream("D:\\d.txt"),"UTF-8");

明确是否需要高效:需要

 BufferedWriter bufw = newBufferedWriter(osw);

  5)转换流什么时候使用:

 转换流是字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。
8、标准输入输出设备改变

通过System类的setIn和setOut方法可以对默认设备进行改变:

    比如:System.setIn(new  FileInputStream(“1.txt”)):将源改成文件1.txt;

      System.setOut(new PrintStream(“2.txt”)):将目的改成文件2.txt。