JAVA学习第十一天之io流

学习目的

  1. 了解java流的概念
  2. 了解java对流的分类
  3. 掌握java流的对象创建,以及常用方法的使用
  4. 掌握java流常见的应用场景
  5. 掌握重点流:FileStream、PrintStream、ObjectStream
  6. 了解序列化与反序列化的概念和使用

I/O流

  1. 概念
    流 在java中指的是文件的转移过程,通常是指文件在计算机硬盘和计算机内存之间的传递方式。该传递过程包括了文件数据和方向。

  2. 字节流继承结构图


    InputStream 和 OutputStream 继承结构图.png
  3. 字符流继承结构图


    Reader 和 Writer 继承结构图.png
  4. 流与文件路径
    输入流对文件的读取和输出流对文件的写入,都需要提前知道文件的路径在哪,一般使用相对路径和绝对路径来存储文件。

  • 相对路径:在IDEA或eclipse中,相对路径是项目/工程的根目录,即项目所在的路径就是相对路径。项目工程所在的根目录才是相对路径起点,注意项目底下的子模块路径不是相对路径的起点
  • 绝对路径:绝对路径是任意盘符下所保存的文件路径,引用绝对路径的文件时,必须将绝对路径从盘符开始具体写明。

1、 IO流与Properties

  1. .properties文件
    .properties为后缀名的文件是java中的属性配置文件,通常存储一些可改变的配置信息。该文件的存储内容与其后缀名一样,采用properties集合存储key=value,即采用Map的键值对形式存储,且properties的键与值得类型只能为String类型。在properties文件中允许key重复,但key重复时,会覆盖value值。
  2. .properties文件原由
    在开发中,经常遇到改变频繁的数据,因此可以将这些数据单独写到一个配置文件中,使用程序动态读取。将来只需要修改这个文件的数据内容,java代码不需要改动,不需要重新编译,服务器也不需要重启,就可以拿到动态的信息。
  3. IO读取.properties文件
    将.properties文件作为一个File对象,使用输入流来读取该文件的内容,就可以获取得到需要的配置信息,可以让配置信息更加灵活多变,且不会触及修改java源代码(修改违背开闭原则)。
  4. 示例(假设已存在对应的.properties文件)
//Properties是一个Map集合,key和value都是String类型
//将XXX.properties文件中的数据读取,就是加载到Properties对象当中
        // 新建一个输入流对象
        FileReader reader = new FileReader("路径\\XXX.properties");
        // 新建一个Map集合(具体是Hashtable的子类Properties)
        Properties pro = new Properties();
        // 调用Properties对象的load方法将属性配置文件中的数据加载到Map集合中
        pro.load(reader); // 文件中的数据顺着管道加载到Map集合中
        // 通过Properties的key来获取对应value
        String username = pro.getProperty("username");
        System.out.println(username);

一.字节字符流

  1. 流概念
    文件由字符或字节构成,将文件加载到内存 或 将内存数据输出到文件,需要有输入和输出流的支持。因此可以把输入和输出流分为了两个,字节输入 和 字符输入,字节输出流 和 字符输出流。

  2. 以流的方向分类

  • 输入流:Input,Java中 将文件或其它输入设备的数据 从硬盘加载到内存的过程;
  • 输出流:Output,Java中 将内存中的数据 保存到硬盘中的文件或其他输出设备的过程。
输入流与输出流.png
  1. 以读取文件数据的方式分类
  • 字节流Stream:以Stream结尾命名的流都是字节流,字节流一次读取1个字节byte(即8个二进制位)。
  • 字符流Reader、Writer:以Reader或Writer 结尾命名的流都是字符流,字符流一次读取一个字符char(两个字节16个二进制位)。
  1. 流关系图


    流关系图.png
  2. 区别
  • 字节流:每次读取一个字节,使用byte[]数组存储、读写多个字节;
  • 字符流:每次读取一个字符,流本身自带并使用char[]数组存储、读写多个字符。

1.1 字节输入流

  1. 概念
    InputStream 就是字节输入流,是一个抽象类。所有继承了 InputStream 的类都是字节输入流,InputStream是用来从文件中读取基本数据类型值的 Java API接口。

  2. 主要子类

  • 文件输入流
  • 对象输入流
  • 过滤输入流
  • 缓存输入流
字节输入流.png
  1. 字段
  • int MAX_SKIP_BUFFER_SIZE = 2048:用于确定 跳过时要使用的最大缓冲区大小。
  1. 常用方法
  • int read() :从输入流读取下一个数据字节,读到文件末尾时返回 -1;
  • int read(byte[] b) :从输入流中每次最多读取b.length个字节,并将读取的数据存储在缓冲区数组b中;
  • int read(byte[] b, int off, int len) :将输入流中 从偏移量off开始的最多len个数据字节读入byte数组b中;
  • long skip(long n):跳过和丢弃此输入流中数据的 n个字节(从下标0开始);
  • void close() :关闭此输入流并释放与该流关联的所有系统资源,finally中执行释放。

1.2 字节输出流

  1. 概念
    OutputStream 就是字节输出流,是一个抽象类,所有继承了 OutputStream 的类都是字节输出流。OuputStream是用来将 基本数据类型数据写入文件的 Java API接口。
  2. 主要子类
  • 文件输出流
  • 对象输出流
  • 过滤输出流
  • 缓存流
  • 打印流
字节输出流.png
  1. 主要方法
  • void write(int b):将指定的字节内容写入此输出流,通过输出流将字节数据写到文件中;
  • void write(byte[] b) :将 b.length个字节从指定的字节数组写入此输出流;
  • void write(byte[] b, int off, int len) :将指定字节数组中 从偏移量off 开始的 len个字节写入此输出流;
  • void flush() :write()写完后,刷新此输出流,并强制写出所有管道中缓冲的输出字节;
  • void close() :关闭此输出流并释放与此流有关的所有系统资源。

1.3 字符输入流

  1. 概念
    Reader 就是字符输入流,是一个抽象类,所有继承了 Reader 的类都是字符输入流。Reader是用于读取字符流的抽象类,子类必须实现其read(char[], int, int) 和 close()方法。同时,多数子类将重写其定义的一些方法,以提供更高的效率和其他功能。

  2. 主要子类

  • 缓存输入流
  • 字节转字符输入流
  • 字符文件输入流
字符输入流.png
  1. 字段
  • Object lock:用于同步 针对此流的操作的对象。
  1. 主要方法
  • Reader():无参构造,创建一个新的字符输入流对象,并同步自身;
  • Reader(Object lock):带参构造,创建一个新的字符输入流对象,并同步给定的对象;
  • int read() :读取单个字符,如果已读到文件末尾,则返回 -1;
  • int read(char[] cbuf) :将输入流读取得到的字符数据存入char[]数组;
  • int read(char[] cbuf, int off, int len) :从数组的偏移量off开始,将len个字符读入数组的某一部分;
  • long skip(long n):读取某个文件时跳过n个字符,从文件下标0开始;
  • void close() :关闭该流并释放与此流有关的所有系统资源。

1.4 字符输出流

  1. 概念
    Writer 就是字符输出流,是一个抽象类,所有继承了 Writer 的类都是字符输出流。Writer是写入字符流的抽象类,子类必须实现其write(char[], int, int)、flush() 和 close()方法,完成数据到文件的写入。同时,多数子类将重写其定义的一些方法,以提供更高的效率和其他功能。

  2. 主要子类

  • 缓存流
  • 字节到字符转换流
  • 字符文件输出流
  • 打印流
字符输出流.png
  1. 主要方法
  • Writer append(char c) :将指定字符追加到此 writer;
  • void write(int c) :往文件中写入单个字符,写入前先清空文件原内容;
  • void write(char[] cbuf) :往文件中写入字符数组,每次最多写入cbuf.length个字符;
  • void write(char[] cbuf, int off, int len) :往字符数组的某一部分,从字符数组的下标off开始,写入len个长度字符到文件中;
  • void write(String str) :往文件中写入字符串;
  • void write(String str, int off, int len) :往文件中写入字符串的某一部分,从字符串下标off开始,写入len个长度字符;
  • void flush() :刷新此输出流,将管道中的数据刷出写入到文件中;
  • void close() :关闭此流并释放与此流有关的所有系统资源,但要先执行 flush() 刷新它。

1.5 flush()方法

flush 的含义是刷新缓冲区,就是将缓存区中的数据写到磁盘上,而不再把数据放到内存里。在执行 os.close()时,会默认执行 os.flush(),编写代码时可以不用显示的调用。

二、文件流

  1. 概念
    文件流是指在程序开发中,完成对文件的读写操作的流称为文件流。
    该流用于读取和写入普通文本、图像、音频、视频等格式文件。
  2. 分类
  • 文件字节输入流
  • 文件字节输出流
  • 文件字符输入流
  • 文件字符输出流
  1. 区别
  • 字节流:每次读取一个字节,使用byte[]数组存储、读写多个字节;可以读取所有的形式文件(文本、视频、音频、图片等);
  • 字符流:每次读取一个字符,使用char[]数组存储、读写多个字符;只适合于读取普通文本文件。

2.1 文件字节输入流

  1. 概念
    FileInputStream是从文件系统(硬盘)中的某个文件中 获得输入字节。主要按照字节的方式读取硬盘上的文件内容,每次读取一个字节。FileInputStream 用于读取诸如图像数据之类的原始字节流。
    <文件内容在计算机底层都是以 二进制的补码形式存在,1字节 = 8位>。
  2. 常用方法
  • FileInputStream(String name):带参构造方法,根据指定文件路径创建字节输入流对象;
  • int read() :从输入流读取文件的下一个数据字节,若已到达文件末尾则返回 -1;
  • int read(byte[] b) :从 输入流中每一次读取 最多b.length个字节,并将读取的内容存储在缓冲区的byte数组中;
  • int available():每一次读取时,剩余未读的字节个数,结合 read(byte[] b) 方法作为byte[ available() ]数组的容量 最佳,但过大时会超出byte数组的容量
  • long skip(long n):跳过n个字节"选择"读取,返回读取时实际跳过的字节数;
  • void close() :关闭此输入流并释放与该流关联的所有系统资源。
  1. FileInputStream读取文件图解


    FileInputStream读取文件.png
  2. 代码实现
             //创建文件输入流引用
             FileInputStream fis = null;
             try {
                  //创建 从指定路径读取文件的 输入流对象,
                  fis = new FileInputStream("c:\\test.txt");
                  int b = 0;//存储每次读取文件返回的字节数据 个数
                  while ((b = fis.read()) != -1) { 
                           //System.out.print(b);//输出读取到的单个字节                                                                                                                                
                           System.out.print((char)b);//将读取得到的单个字节 转换成字符输出  
                   }                  
                   //采用read(byte[] bytes)方法读取时,结合使用available()作为bytes数组的容量,但数据过大时不能使用
                   // byte[] bytes = new byte[fis.available()];

                   //采用字节数组存储字节流读取文件的数据
                   byte[] b1 = new byte[4];
                   int readcount = 0;//存储每次读取数组 返回的字节个数
                   //当返回读取的字节个数不为0
                   while ((readcount = fis.read(b1)) != -1){
                           //将每一次读取byte数组中的数据转换为字符串输出,每次读取几个就输出几个
                           String s1 = new String(b1,0,readcount);
                           System.out.print(s1);//
                    }
              }catch(FileNotFoundException e) {
                          e.printStackTrace();
              }catch(IOException e) {
                          e.printStackTrace();
              }finally {      //当输入流为空时,关闭流,释放内存
                         try {
                                  if (fis != null){
                                      is.close();
                                  }
                         }catch(IOException e) {}
              }
  1. 注意点
  • 常常使用 fis.read() != -1判断是否读取到了文件末尾;
  • 使用 byte数组存储读取文件的数据时,想要每次读取几个就输出几个,就使用一个变量temp 接收每次读取的个数;
  • 采用字节输入流读取汉字文本文件时,因为每个汉字占据两个字节,而字节输入流每次只能读取一个字节,导致每个汉字的读取分为"不完整的"两部分,从而产生乱码。

2.2 文件字节输出流

  1. 概念
    文件输出流是 将数据写入文件的输出流。"写入文件"是否可用 或 能否被创建,取决于基础平台,某些平台一次只允许一个文件流对象打开文件进行写入。若所涉及的文件已经打开,则此类中的构造方法将失败(一个文件无法被两个输出流同时写入数据)。

  2. 特点
    文件字节输出流按照 字节方式写出文件,适用于写入诸如图像数据之类的原始字节的流。

  3. 经典案例
    文件复制:首先判断该文件是否存在。文件存在,则(采用输入流)读取文件,读取后将该文件(采用输出流)另写一份保存到磁盘上,完成文件的复制备份。

  4. 文件字节输出图


    文件字节输出流.png
  5. 常用方法

  • FileOutputStream(String name):带参构造,创建一个 向指定文件写入数据的 字节输出流对象,写入时默认清空文件的所有内容再写入
  • FileOutputStream(String name, boolean append):带参构造,创建一个 向指定文件写入数据的 字节输出流对象;第二个参数为 true,表示将字节追加写入文件末尾处,而不是清空文件再写入文件开始处
  • void write(int b):输出流将指定的字节b写入文件,一次写入一个长度的字节;
  • void write(byte[] b) :输出流每次将最多 b.length 个字节从指定 byte 数组写入此文件,通常byte数组是由输入流读取得到的"复制内容";
  • void writeBytes(byte b[], int off, int len, boolean append):输出流将数组的一部分内容写入文件,并使用append判断是清空原文件内容写入,或是在原文件的末尾追加写入;
  • void close() :关闭此输出流 并释放与此流有关的所有系统资源。
  1. 代码实现
           //创建文件输出流引用
           FileOutputStream fos = null;
               try {
                    //创建 指定路径写出文件的 输出流文件对象,若文件不存在则在该路径新建文件,若文件存在则清空文件的内容再写入
                    fos = new FileOutputStream("d:\\test.txt.bak");
                    //创建一个输出流对象,写入时不清空已存在文件的内容,而是在文件末尾追加写入的内容
                    // fos = new FileOutputStream("d:\\test.txt.bak",true);  

                    //写入单个字符
                    fos.write(1);
                    //写入字符串
                    fos.write("Hello world!");
                    //写入char数组
                    char[] ac = {'我','是','中','国','人'};
                    fos.write(ac);
                    //写入完成,刷新管道
                    fos.flush();
                    System.out.println("文件复制完毕!");
                 }catch(FileNotFoundException e) {
                            e.printStackTrace();
                 }catch(IOException e) {
                            e.printStackTrace();
                 }finally {
                    // 分开try,不要一起try, 一起try时,其中一个出现异常,可能会影响到另一个流的关闭。
                     if (fos != null) {
                          try {
                              fos.close();
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                     }
  1. 注意点
  • 对于FileOutPutStream要写入的文件,若文件在对象创建前已存在,直接对已存在文件进行写入;若文件不存在,则会在指定路径下新建文件再写入;
  • FileOutPutStream对象的创建:调用构造器时,注意区分单参构造和双参构造,单参构造会清空写入文件的所有内容再将新数据写进;双参构造append若是true时,则不会清空文件内容,而在文件末尾追加写入的数据。

2.3 文件字符输入流

  1. 概念
    FileReader是用来读取字符文件的便捷类。主要按照 字符(双字节)的方式读取文件内容,即每次读取两个字节。<底层使用两个字节位00110101 10111001>。
  2. 文件字符输入流图


    文件字符输入流.png
  3. 常用方法
  • FileReader(String fileName):带参构造,在指定路径下创建一个字符输入流对象,读取文件数据(底层调用FileInputStream对象的创建方式);
  • int read() :从输入流读取单个字符,以int类型变量接收读取的字符;
  • int read(char[] c) :从输入流 将字符读入数组,一次读入最多c.length个长度字符;
  • void close() :关闭该流并释放与此流有关的所有系统资源。
  1. 代码实现
//创建字符输入流引用
        FileReader fileReader = null;
        try {
            //创建实际的字符输入流对象
            fileReader = new FileReader("");
            //读取单个字符
            fileReader.read();
            //用于存储 每次读取返回的字节个数
            int readcount = 0;
            char[] ac = new char[8];
            //读取数组
            fileReader.read(ac);
            while ((readcount = fileReader.read(ac)) !=-1){
                //读取数组的一部分
                fileReader.read(ac, 0, readcount);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

2.4 文件字符输出流

  1. 概念
    FileWriter是用来写入字符文件的便捷类,每一次输出按照字符方式(双字节)写出文件。
  2. 文件字符输出流图


    文件字符输出流.png
  3. 常用方法
  • FileWriter(String fileName):带参构造,创建一个 向指定名称的文件中写入数据的 字符输出流对象;
  • FileWriter(String fileName, boolean append):带参构造,创建一个 向指定名称的文件中写入数据的 字符输出流对象;第二个参数为 true,表示将字节写入文件末尾处,而不是写入文件开始处;
  • void flush() :刷新此输出流,将可能残留在管道中的数据刷出到文件中(一般在关闭流之前刷新);
  • void close() :关闭此流并释放与此流有关的所有系统资源,但要先执行 flush() 刷新;
  • void write(int c) :在输出流中写入单个字符;
  • void write(String str) :在输出流中写入字符串。
  1. 代码实现
       //创建一个文件字符输出流引用
        FileWriter fileWriter = null;
        try {
            //创建实际的字符输出流对象
            fileWriter = new FileWriter("");
            //创建实际的字符输出流对象,true表示在输出文件后面进行append追加内容
            //fileWriter = new FileWriter("",true);

            //写入单个字符
            fileWriter.write(1);
            //写入字符串
            fileWriter.write("Hello world!");
            //写入char数组
            char[] ac = {'我','是','中','国','人'};
            fileWriter.write(ac);
            //写入完成,刷新管道
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fileWriter != null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

三、缓冲流

  1. 概念
    缓冲流主要是通过减少物理读取次数(程序从硬盘中读取文件的次数)的流,通过将硬盘数据读取到一个缓冲管道中,下一次直接从缓冲管道中读取,减少内存与硬盘的交流次数。缓冲流是为了提高效率而存在的。

  2. 分类

  • 缓冲字节输入流
  • 缓冲字节输出流
  • 缓冲字符输入流
  • 缓冲字符输出流
  1. 特点
  • 构造方法节点流:缓冲流的构造方法中需要一个节点流作为第一参数,不像文件流直接使用文件路径或文件创建对象,缓冲流必须使用父类的字节输入/输出流、字符输入输出流的对象作为第一参数;
  • 流自带缓冲区:缓冲流中自带有数据缓冲区,无需像文件流一样创建自定义byte[]数组或char[]数组存储 字节/字符输入流读取文件的数据;
  1. 节点流、包装流与处理流
  • 节点流:指的是在一个流的构造方法内还需要另外一个流作为参数,而作为参数的流就称为节点流,在关闭流释放资源时,只需关闭外部流,不需要关闭节点流(外部流的close()底层自动关闭节点流);
  • 包装流/ 处理流:指的是使用节点流作为其构造方法参数的流,实际上对文件进行操作处理的流。

3.1 缓冲字节输入流

  1. 概念
    BufferedInputStream为另一个输入流InputStream扩展功能,添加了缓冲输入、支持 mark() 和 reset()方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组,在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
  2. 特点
  • mark()方法 记录输入流中的某个点;
  • reset()方法使得在 从包含的输入流中获取新字节 之前,再次读取自最后一次标记后读取的所有字节。
  1. 字段
  • int DEFAULT_BUFFER_SIZE = 8192:默认字符缓冲区的大小;
  • int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8:该流支持的最大缓冲区大小;
  • volatile byte buf[]:存储 从字节输入流中读取的数据 的内部缓冲数组;
  • int count:比缓冲区中最后一个有效字节的索引 大1的索引;
  • int pos:缓冲区中的当前位置position;
  • int markpos = -1:最后一次调用mark()方法时 缓冲区的位置。
  1. 常用方法
  • BufferedInputStream(InputStream in):带参构造,创建一个默认缓冲区大小的字节缓存输入流对象,底层使用输入流 in;
  • BufferedInputStream(InputStream in, int size):带参构造,创建一个size长度的指定缓冲区大小的 字节缓存输入流对象,底层使用输入流 in;
  • int read():从输入流中读取数据的下一个字节;
  • int read(byte b[], int off, int len):将输入流中的 len个数据字节读入byte 数组中,从数组的下标off开始存储;
  • long skip(long n):跳过和丢弃此输入流中数据的 n个字节;
  • void mark(int readlimit):在此输入流中标记当前的位置;
  • void close():关闭此输入流并释放与该流关联的所有系统资源。
 //创建字节输入流对象--缓冲流中的节点流
        FileInputStream fis = new FileInputStream("");
        //创建缓冲字节输出流对象--参数为节点流
        BufferedInputStream bis = new BufferedInputStream(fis);
        //创建缓冲字节输出流对象--参数为节点流,且缓冲流本身自带的缓冲区大小为128字节
        BufferedInputStream biss = new BufferedInputStream(fis,128);
        //查看缓冲流本身缓冲区的大小
        bis.available();
        //读取单个字节
        bis.read();
        byte[] b1 = new byte[32];
        //读取字节数组
        bis.read(b1);
        int readcount = 0;
        //读取字节数组的一部分
        bis.read(b1,0,readcount);
        //关闭流资源,释放内存
        bis.close();

3.2 缓冲字节输出流

  1. 概念
    BufferedOutputStream类是实现缓冲的输出流。通过设置这种输出流,应用程序可以将各个字节写入底层输出流InputStream中,不必针对每次字节写入都调用底层系统。
  2. 字段
  • byte buf[]:存储数据的内部缓冲区;
  • int count:缓冲区中的有效字节数。
  1. 常用方法
  • BufferedOutputStream(OutputStream out):带参构造,创建一个默认缓冲区大小为8192的 字节缓存输出流对象,底层使用输出流 out;
  • BufferedOutputStream(OutputStream out, int size):带参构造,创建一个指定size长度的缓冲区大小的 字节缓存输入流对象,底层使用输出流 out;
  • void flushBuffer():刷新此字符缓冲区;
  • void write(int b):将字符数据b写入缓冲输出流;
  • void write(byte b[], int off, int len):将字符数组中指定len个长度的数据写入缓冲输出流,从数组中的下标off开始;
  • void flush():刷新此字符缓冲输出流,将管道中的数据刷新到文件中。
//创建字节输出流对象--作为缓冲流的节点流
        FileOutputStream fos = new FileOutputStream("");
        //创建缓冲字节流输出对象
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //创建缓冲字节流输出对象,且缓冲流自身的缓冲区大小为28字节
        //BufferedOutputStream boss = new BufferedOutputStream(fos,128);
        //写出单个字节
        bos.write(12);
        //写出字节数组
        byte[] b1 = new byte[128];
        bos.write(b1);
        int writecount = 0;
        //写出字节数组的一部分
        bos.write(b1,0,writecount);
        //刷新缓冲流
        bos.flush();
        //关闭缓冲流
        bos.close();

3.3 缓冲字符输入流

  1. 概念
    BufferedReader是从字符输入流中读取文本(不是直接从文件中),缓冲各个字符,从而实现字符、数组和行的高效读取。 BufferedReader可以指定缓冲区的大小,或者使用默认大小。大多数情况下,默认值足够大。
  2. 特点
    缓冲指定文件的输入。如果没有缓冲,每次调用 read()或 readLine()方法都会导致从文件中读取字节,再将其转换为字符后返回,是极其低效的。
  3. 字段
  • Reader in:字符输入流对象(节点流),用于BufferedReader构造方法中的第一参数;
  • char cb[]:流本身自带用于存储 从字符输入流中读取的数据的字符数组;
  • int defaultCharBufferSize = 8192:默认自身缓冲区的大小为8192字节。
  1. 常用方法
  • BufferedReader(Reader in):创建一个默认大小输入缓冲区的缓冲字符输入流,需要一个节点流Reader作为对象;
  • BufferedReader(Reader in, int sz):带参构造,创建一个指定sz大小输入缓冲区的缓冲字符输入流;
  • int read():从字符输入流中读取单个字符;
  • read(char cbuf[], int off, int len):将字符读入到数组中的某一部分;
  • String readLine(): 从字符输入流中直接读取一个文本行,遇到指定字符时某行终止(换行 '\n'、回车 '\r' 、回车后跟着的换行),该方法本身读取不到换行符
  • long skip(long n):从字符输出流中读取时,跳过指定n个字符,从下标0开始;
  • void close():关闭该缓冲流,释放内存。

3.4 缓冲字符输出流

  1. 概念
    BufferedWriter将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或接受默认的大小。大多数情况下,默认值足够大。
  2. 特点
    将缓冲 PrintWriter对文件的输出。如果没有缓冲,每次调用 print()方法都会将字符转换为字节,再写入到文件,是极其低效的。
  3. 字段
  • Writer out:字符输出对象,用于BufferedWriter构造方法中;
  • char cb[]:用于存储 从字符输入流中读取的数据的 字符数组;
  • int defaultCharBufferSize = 8192:默认缓冲区大小8192字节。
  1. 常用方法
  • BufferedWriter(Writer out):带参构造,创建一个使用默认大小输出缓冲区的缓冲字符输出流;
  • BufferedWriter(Writer out, int sz):带参构造,创建一个使用给定大小输出缓冲区的新缓冲字符输出流;
  • void ensureOpen():确认该输出流打开(正在使用可输出);
  • void write(int c):写入单个字符, c-是指定要写入字符;
  • void write(char cbuf[], int off, int len):将字符数组中的某一部分写入流,从字符数组的下标off开始,写入len个长度字符;
  • void write(String s, int off, int len):将字符串的某一部分写入流,从字符串的下标off开始,写入len个长度字符;
  • void newLine():往流中写入一个行分隔符;
  • void flush():刷新该流的缓冲,将管道中缓冲的数据刷新到文件中;
  • void flushBuffer():刷新缓冲数据;
  • void close():关闭此流,关闭前要先使用flush()刷新管道中的缓冲数据。

四、转换流

  1. 概念
    转换流就是将一种流的方法转换成另一种流的方式。通常是字节流转字符流(对比基本数据类型的转换,小转大可以自动转,大转小会损失精度,单字节容纳不下双字节)。
  2. 主要子类
  • 字节转字符输入
  • 字节转字符输出
  1. 用途
    作为一个"包装类",将字节流转换为字符流,结合缓冲流的构造方法中的节点流一起使用更合适。

4.1 字节转字符输入

  1. 概念
    InputStreamReader就是将字节流输入流转换成字符输入流,由字面理解就是InputStream → Reader。它使用指定的字符编码读取字节 并将其解码为字符,使用的字符集可以由名称指定、显式给定,或者接受平台默认的字符集。
  2. 特点
  3. 字段
  • StreamDecoder sd:流解码器对象,用于处理字节转字符期间的编码和解码问题。
  1. 常用方法
  • InputStreamReader(InputStream in):带参构造,创建一个使用默认字符集的 字节转字符输入对象,底层使用字节输入流对象in;
  • InputStreamReader(InputStream in, String charsetName):带参构造,创建使用指定字符集的 字节转字符输入对象;
  • InputStreamReader(InputStream in, Charset cs):带参构造,创建使用给定字符集的 字节转字符输入对象;
  • InputStreamReader(InputStream in, CharsetDecoder dec):带参构造,创建使用给定字符集解码器的 字节转字符输入对象;
  • boolean ready():判断此流是否已经准备好用于读取,读取前判断再使用;
  • int read():读取单个字符,如果已到达流的末尾,则返回 -1 ;
  • int read(char cbuf[], int offset, int length):将字符读入到数组中的某一部分,从数组下标offset开始存储,读取length个长度;
  • void close():关闭该流并释放与之关联的所有资源。
  1. 代码示例
        //创建字符输入流
        FileReader fr = new FileReader("");
        //创建字节缓冲流--内部参数为字符流对象
        BufferedReader br1 = new BufferedReader(fr);

        //创建字节输入流
        FileInputStream fio = new FileInputStream("");
        //将字节输入流fio 转成 字符输入流
        InputStreamReader isr = new InputStreamReader(fio);
        //创建字节缓冲流--内部参数为字符流对象
        BufferedReader br = new BufferedReader(isr);

        //合并-字节流--字节转字符流--字符缓冲流(字符流对象参数)
        BufferedReader bfr = new BufferedReader(new   InputStreamReader(new FileInputStream("")));
        bfr = new BufferedReader(new InputStreamReader(System.in));
        String s = null;
        while ((s=br.readLine()) != null) {
           System.out.println(s);
           //读取到字母 q时退出循环
                 if ("q".equals(s)) {
                       break;
                 }
         }
        if (bfr != null) {
           bfr.close();
        }

4.2 字节转字符输出

  1. 概念
    OutputStreamWriter就是将字节流输出流转换成字符输出流,由字面理解就是OutputStream → Writer。它使用指定的字符编码将 要写入流中的字符编码成字节,使用的字符集可以由名称指定、显式给定,否则接受平台默认的字符集。
  2. 特点
  3. 字段
  • StreamDecoder sd:流解码器对象,用于处理字节转字符期间的编码和解码问题。
  1. 常用方法
  • OutputStreamWriter(OutputStream out):带参构造,创建使用默认字符编码的 字节转字符输出对象;
  • OutputStreamWriter(OutputStream out, String charsetName):带参构造,创建使用指定字符集的 字节转字符输出对象;
  • OutputStreamWriter(OutputStream out, Charset cs):带参构造,
    创建使用给定字符集的 字节转字符输出对象;
  • OutputStreamWriter(OutputStream out, CharsetEncoder enc):带参构造, 创建使用给定字符集编码器的 字节转字符输出对象;
  • write(int c):写入单个字符;
  • write(char cbuf[], int off, int len):将字符数组中的某一部分写入流,从数组的下标off开始,写入len个长度;
  • void write(String str, int off, int len):将字符串中的某一部分写入流,从字符串的下标off开始,写入len个长度;
  • void flushBuffer():刷新此流缓冲区;
  • void flush():刷新该流的缓冲,将管道中缓冲的数据全都写入文件;
  • void close():关闭此流,但要先flush()刷新它。
        //创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("");
        //将字节输出流--转换--字符输出流
        OutputStreamWriter osw = new OutputStreamWriter(fos);

        //创建字节输出流对象
        FileOutputStream fous = new FileOutputStream("");
        //将字节输出流--转换--字符输出流
        OutputStreamWriter os = new OutputStreamWriter(fous);
        //将字符输出流--转换--缓冲字符输出流
        BufferedWriter bw = new BufferedWriter(os);

        //合并-字节输出流--字符输出流--缓冲字符输出流()
        BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("")));
        buw.flush();

五、打印流

  1. 概念
    打印流即从程序到内存输出的流,由java程序向控制台打印输出,即完成对屏幕打印的重定向。
  2. 特点
    System.out 就是打印输出,默认输出到控制台。而打印流 PrintStream可以重定向输出到文件,System.out.println不再输出到屏幕,而是输出到指定文件。
  3. 分类
  • 打印字节流
  • 打印字符流
  1. 输出重定向原理
    System.out默认是PrintStream向控制台输出;
    PrintStream 构造时,使用指定输出路径的输出流对象作为参数,导致PrintStream的输出不再默认,而是向指定输出路径输出,从而完成输出重定向。但前提也需要System.setOut才可以修改输出方向。
  • System.setOut(new PrintStream(new OutPutStream("指定路径")));

5.1 打印字节流

  1. 概念
    PrintStream构造器底层需要一个输出流作为参数(节点流),因此是其他输出流的扩展,为其他输出流添加更多功能。PrintStream默认完成由java程序向控制台打印输出,即完成对屏幕打印的重定向。
  2. 特点
  • PrintStream永远不会抛出 IOException;
  • PrintStream在写入 byte数组之后自动调用 flush()方法完成刷新;
  • 调用println()方法,表示写入一个换行符或字节 ('\n');
  • PrintStream不需要close()关闭;
  1. 特点
    System.out就是 PrintStream,System.out默认输出到控制台。
    而当打印流PrintStream使用 指定路径的输出流作为节点流参数 来构造时,可以重定向输出到文件,System.out.println不再输出到屏幕,而是输出到指定文件。
  2. 字段
  • boolean autoFlush:自动刷新标志;
  • BufferedWriter textOut:字符输出对象;
  • OutputStreamWriter charOut字节转字符输出对象;
  • Formatter formatter:格式化程序,对输出流中的数据格式化;
  1. 常用方法
  • void flush():刷新该流的缓冲,通过将所有缓冲的输出字节写入到底层输出流;
  • void close():关闭流,通过刷新流然后关闭底层输出流完成此操作;
  • void write(int b):将指定的字节b写入此流,如果字节为新行 且启用了自动刷新,则调用 flush 方法;
  • void write(byte[] buf,int off, int len):将byte 数组的 len长度个字节从指定下标 off开始写入此流。如果启用自动刷新,则调用 flush 方法;
  • void print(Object obj):打印数据,按照平台的默认字符编码将 String.valueOf(obj)生成的字符串转换为字节,并完全以write(int)方法的方式写入这些字节;
  • void println():通过写入行分隔符字符串终止当前行,行分隔符字符串由系统属性line.separator 定义,不一定是单个换行符 '\n';
  • PrintStream format(String format, Object args):使用指定格式字符串和参数 将格式化字符串写入此输出流中;
  • PrintStream append(CharSequence csq):将指定字符序列csq添加到此输出流;
  • PrintStream append(char c):将指定字符c添加到此输出流。
  1. 代码示例
        //创建字节输出流引用
         FileOutputStream os = null;
              try {
                  System.out.println("asdfkjfd;lldffdfdrerere");//未改变输出方向,默认向控制台输出
                  //创建具体的输出流对象
                   os = new FileOutputStream("c:/console.txt");
                  //创建标准输出流对象--采用节点流参数,将指定文件输出的方向
                   PrintStream ps = new PrintStream(os);
                   //PrintStream ps =   System.out; 
                   System.setOut(ps);//改变输出方向
                   //System.setOut(new PrintStream(os));//合并--改变输出方向

                   System.out.println("asdfkjfd;lldffdfdrerere");//向指定输出流的文件console.txt输出
              }catch(FileNotFoundException e) {
                   e.printStackTrace();
             }catch(IOException e) {
                   e.printStackTrace();
            }finally {    //关闭流资源,释放内存    
                   try {
                        if (os != null) {
                            os.close();
                        }
                    }catch(IOException e) {}
           }

5.2 打印字符流

  1. 概念
    打印输入流,相当于System.in,完成 接收屏幕输入。
  2. 特点
  3. 字段
    -Writer out:字符输出流对象,构造器时使用;
  • boolean autoFlush:自动刷新标志;
  • Formatter formatter:格式化对象,对输出流中的数据格式化;
  • PrintStream psOut = null:字节打印流对象;
  • String lineSeparator:行分隔符,用于print()方法中进行行分隔的标志;
  1. 常用方法
  • PrintWriter (Writer out):带参构造,创建一个不带自动行刷新的字符打印流对象,底层调用字节输出流对象;
  • void flush():刷新该流的缓冲,底层重写了Flushable接口和Writer类的方法;
  • void close():关闭该流并释放与之关联的所有系统资源;
  • void write(int c):写入单个字符c;
  • void write(char buf[], int off, int len):写入字符数组的某一部分,从数组的下标off开始,写入len个长度;
  • void write(char buf[]):写入字符数组,此方法不能从 Writer类继承,因为它必须取消 I/O 异常;
  • void write(String s, int off, int len):写入字符串的某一部分;
  • void newLine():往流中写入一个行分隔符;
  • void print(Object obj):打印数据,按照平台的默认字符编码将 String.valueOf(obj)生成的字符串转换为字节,并完全以write(int)方法的方式写入这些字节;
  • void println():通过写入行分隔符字符串终止当前行,底层调用newLine()方法;
  • PrintWriter append(CharSequence csq):将指定字符序列csq添加到此输出流;
  • PrintWriter append(char c):将指定字符c添加到此输出流。

六、对象流<掌握>

  1. 概念
    对象流是可以将 Java对象 转换成二进制进行文件操作的流,通常对象流的输入和输出是以字节单位进行的。
  2. 分类
  • 对象字节输入流
  • 对象字节输出流
  1. 常用方法
  • writeObject(Object obj):向硬盘下的指定文件打印输出该obj对象;
  • readObject():从硬盘指定的文件中读取出对象到内存中。

6.1 对象字节输入流

  1. 概念
    ObjectInputStream类是对 使用了 ObjectOutputStream类写入的基本数据和对象进行反序列化的对象输入流,从磁盘中将对象反序列化到内存中。
  2. 特点
  3. 字段
  1. 常用方法
  1. 代码示例
        //创建文件输入流引用
        FileInputStream fis = null;
        //创建对象输入流引用
        ObjectInputStream ois = null;
            //创建实际的文件输入流对象--作为节点流参数
            fis = new FileInputStream("students");
            //创建实际的对象输入流对象--完成反序列化的流
            ois = new ObjectInputStream(fis);
            //将文件中的对象反序列化到内存中--返回的是Object类型对象
            Object o = ois.readObject();
            //将反序列化的对象进行类型强转
            Student st = (Student)o;
            //获取反序列对象的各个属性
            System.out.println(st.getName());
            System.out.println(st.getAge());
            if (ois != null){
                    ois.close();
            }
            if (fis != null){
                    fis.close();
            }
        }

6.2 对象字节输出流

  1. 概念
    ObjectOutputStream是一个用于将java对象分段写入(序列化)到硬盘中的对象输出流。
  2. 特点
  • static class Caches:静态内部类--缓存,使用HashMap存储数据,使用ReferenceQueue参考队列存储消息。
  1. 字段
  1. 常用方法
  1. 代码示例
//创建需要进行序列化的java对象
        Student stu = new Student("zhangsan",23);
        //创建文件输出流引用--作为节点参数
        FileOutputStream fos = null;
        //创建对象输出流引用--将java序列化写出的流
        ObjectOutputStream oos = null;

            //创建实际的文件输出流对象--指定输出路径
            fos = new FileOutputStream("stu");
            //创建实际的对象输出流对象,使用节点流参数
            oos = new ObjectOutputStream(fos);
            //将java对象序列化写出
            oos.writeObject(stu);
            //刷新输出流
            oos.flush();
            //关闭流资源            
            if (fos != null){
                    fos.close();
            }
            if (oos != null){
                    oos.close();
            }
        }

6.3 序列化与反序列化

  1. 概念
    Serilizable接口是一个序列化接口,是标记接口,该接口内没有任何方法。实现该接口只是作为一个标记给JVM看,然后JVM认为实现了该接口的类型对象可以进行序列化,因此该类的所有属性和方法都会自动序列化,不必关心具体序列化的过程。
  2. 图解


    对象序列化与反序列化图解.png
  3. 前提
    需要进行序列化或反序列化的对象必须实现Serilizable接口。
//必须实现Serilizable接口作为JVM认可的序列化标记
public class Student implements Serializable {
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //提供get()和set()方法,并重写toString()方法
}

6.3.1 序列化

  1. 概念
    对象流将 Java对象转换成二进制 写入磁盘的过程叫做序列化,底层采用输出流作为节点流参数。
  2. 特点
    每一个java对象都会是较大的文件,因此序列化时,对象输出流会将Java对象进行切割分段,再写入到硬盘文件当中。
  3. 代码示例
    //创建需要序列化的java对象
        Student stu = new Student("haha",21);
        //创建对象输出流引用--序列化使用的写出流
        ObjectOutputStream obj = null;
        try {
            //创建实际的对象输出流对象--采用文件输出流作为参数,指定序列化路径
            obj = new ObjectOutputStream(new FileOutputStream("students"));
            //将需要序列化的java对象写出序列化到硬盘中
            obj.writeObject(stu);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (obj != null){
                try {
                    obj.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
  1. 注意点
  • 序列化多个对象:当需要同时序列化多个对象时,提前将多个对象存储到数组或集合当中<建议使用泛型>,在序列化时ObjectOutputStream.writeObject(集合对象)即可一次序列化多个对象。采用集合序列化多个对象到硬盘中的文件,存储的也是一个集合对象,而不是散开的单个泛型对象。
  • 若想采用每次序列化单个对象的方式逐次序列化多个对象,那么会在序列化到第二个对象时出现错误。

6.3.2 反序列化

  1. 概念
    对象流从磁盘读出完整 Java 对象到内存中的过程叫做反序列化,底层采用输入流作为节点流参数。
  2. 代码示例
        //创建反序列化的对象输入流引用
        ObjectInputStream ois = null;
              try {
                    //创建实际的对象输入流对象,使用文件输入流作为参数
                    ois = new ObjectInputStream( new FileInputStream("c:/Person.dat"));
                    //反序列化,将对象从磁盘读出,并转化为对应类型的对象
                    Person person = (Person)ois.readObject();
                    //获取反序列化后对象的属性                    
                    System.out.println(person.name);
              }catch(ClassNotFoundException e) {
                    e.printStackTrace();
              }catch(FileNotFoundException e) {
                    e.printStackTrace();
              }catch(IOException e) {
                    e.printStackTrace();
              }finally {        //关闭流,释放资源
                    try {
                        if (ois != null) {
                                 ois.close();
                        }
                    }catch(IOException e) {}
          }
  1. 注意点
    反序列化多个对象:若序列化文件中的对象是集合对象(即使集合里面存储了多个泛型对象),那么反序列化时 ObjectInputStream.readObject()读取返回的obj也是一个集合对象,因此返回时可以采用集合引用<建议使用泛型>来接收返回的集合对象。再对返回的集合对象进行迭代遍历和获取集合元素(单个对象)的信息。

6.4 transient关键字

  1. 概念
    transient意为游离的。在一个需要序列化的类中,明确有些属性需要序列化,而其他属性不需要被序列化时,使用transient关键字修饰该属性将不会被序列化。
  2. 作用
    只需要实现Serilizable接口,将不需要序列化的属性添加关键字transient修饰,序列化对象的时候,该属性就不会序列化。
  3. 使用场景
    如一个用户类有一些敏感信息<密码,银行卡号>,为了安全起见,不希望在网络操作中被传输(被序列化,包括本地序列化缓存)。因此在该属性上加上 transient关键字,以免被序列化。即transient修饰的字段的生命周期仅存于调用者的内存中,而不会写到磁盘里持久化。
class Person implements Serializable{
         String name;
         transient String password;//采用 transient 关键字修饰此属性,序列化时会忽略<即不会被序列化>
}

6.5 serialVersionUID 属性

  1. 概念
    serialVersionUID 意为序列化版本号,序列化的对象在存储时都有一个标识版本号,使用该属性修饰的类保证了序列化版本号唯一。
  2. 特点
  • serialVersionUID属性使用public static final修饰;
  • 若不使用序列化版本号,在多次对原类修改添加后进行序列化时,会产生多个版本不一样的对象,反序列化时也会不清楚到底反序列化哪一个版本;
  • 实现Serilizable接口时,系统默认提供序列化版本号,但是强烈建议程序员自定义类时手动添加写上。
  1. 作用
  • java验证类唯一性的标志之一:java的序列化机制是通过判断类的序列化ID号来验证 序列化对象版本一致性的;
  • 保证序列化与反序列化的文件相同且唯一:拥有serialVersionUID属性修饰的类,即使其实例对象在进行一次序列化,之后无论对该对象进行多少次添加或修改,重新对"新对象"序列化时,得到的还是同一个对象;
  • 防止序列化兼容问题:解决序列化时对象不唯一问题,对象修改后也不出现序列化的版本问题,而是拥有统一的版本号。
  1. 图解serialVersionUID


    serialVersionUID图解.png
  2. 使用方式
        class Person implements Serializable{
                //加入版本号,防止序列化兼容问题,解决序列化时不一致
                private static final long serialVersionUID = -11111111L;
                String name;
                int age;
                boolean sex;
        }
  1. IDEA生成序列化版本号
    File --> Settings --> Editor --> Inspections -->搜索serializable --> Serializable Class without serialVersionUID --> 打钩 --> ok --> 对实现了Serializable接口的类 atl+回车 --> Add serialVersionUID

七、数据流

  1. 概念
    数据流既是数据专属的流,用于读取文件中的数据及其数据类型,或用于写出数据及其数据类型,数据流读取保存或写入生成的文件不是普通文本文件,不能直接打开。
  2. 分类
  • DataOutputStream:数据输出流,可以将数据连同数据的类型一并写入文件,写出的文件不是普通文本文件;
  • DataInputStream:数据输入流,对于DataOutputStream写出的文件,只能使用DataInputStream流去读,并且读取时需要提前知道写入时的数据顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。
  1. 作用特点
  • 安全性:使用DataOutputStream写出的数据连带其数据类型,更加安全易读;
  • 加密性:对于DataOutputStream写出的文件必须要DataInputStream流去读,否则乱码,更加安全加密。
  1. DataOutputStream
        FileOutputStream fos = new FileOutputStream("data");
       // 创建数据专属的字节输出流--使用节点流
       DataOutputStream dos = new DataOutputStream(fos);
       //合并--
       //DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
        // 写数据
        byte b = 100;
        short s = 200;
        int i = 300;
        long l = 400L;
        float f = 3.0F;
        double d = 3.14;
        boolean sex = false;
        char c = 'a';
         // 把数据以及数据的类型一并写入到文件当中。
        dos.writeByte(b);
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(sex);
        dos.writeChar(c);
        // 写出完成时,刷新管道
        dos.flush();
        // 关闭外层流(包装流)
        dos.close();
  1. DataInputStream
        FileInputStream fis = new FileInputStream("data");
       // 创建数据专属的字节输入流--使用节点流
       DataInputStream dis = new DataInputStream(fis);
       //合并--
        DataInputStream dis = new DataInputStream(new FileInputStream("data"));
        // 开始读取DataOutputStream写出的文件--前提知道写出的数据顺序
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        boolean sex = dis.readBoolean();
        char c = dis.readChar();
        //关闭流资源              
        dis.close();

八、File 类

  1. 概念
    File 类是文件和目录路径名的抽象表示形式,指的是一类可以直接对文件进行操作的类(不管是硬盘上的还是内存缓存中的)。
  2. 特点
    File类不是具体的文件,只是一条路径,可以是目录也可以是具体文件所在目录,底层封装了文件的路径和路径状态。
  3. 字段
  • String path:文件的路径,可以是根目录或绝对路径;
  • PathStatus status = null:路径的状态,
  • enum PathStatus { INVALID, CHECKED }:
  • int prefixLength:文件前缀长度;
  • char separatorChar = fs.getSeparator():文件分隔符。
  1. 常用方法
  • File(String pathname):构造方法,根据指定路径创建一个File文件类对象;
  • File(String parent, String child):构造方法,
  • String getName():
  • booLean exists():判断 根据指定路径创建的文件类对象是否存在,即在指定路径下是否能找到对应文件;
  • boolean createNewFile():若指定路径下的文件类对象不存在,则在该路径下以文件形式创建相应的文件;
  • boolean mkdir():若指定路径下的文件类对象不存在,则在该路径下以目录形式创建相应的目录文件;
  • boolean mkdirs():若指定路径下的文件类对象不存在,则在该路径下以多级目录形式创建相应的目录文件;
  • String getParent():获取此路径下创建的文件类对象的文件父目录;
  • File getParentFile():获取此路径下创建的文件类对象的文件父目录,并根据该目录生成新的文件类对象;
  • String getPath():获取该文件类对象的原文件路径;
  • String getAbsolutePath():获取该文件类对象的文件绝对路径;
  • boolean isDirectory():判断该文件类对象是否是一个目录;
  • boolean isFile():判断该文件类对象是否是一个文件;
  • boolean isHidden():判断该文件类对象是否是一个隐藏文件;
  • long lastModified(): 获取该文件类对象的文件最后一次的修改时间,返回的是自1970年1月1日开始的毫秒数;
  • long length():获取该文件类对象的文件大小;
  • File[] listFiles():获取当前目录下所有的子文件,返回的是由多级子目录构成的文件数组,可通过foreach循环遍历该数组得到所有的子文件。
  1. 代码实例
//根据指定路径创建一个文件类对象
        File f = new File("");
        // 判断该路径下的创建的文件类对象 (文件)是否存在
        System.out.println(f.exists());

        // 如果路径下文件不存在,则以文件的形式创建出来
        /*if(!f1.exists()) {
            // 以文件形式新建
            f1.createNewFile();
        }*/
        // 如果路径下文件不存在,则以单级目录的形式创建出来
        /*if(!f1.exists()) {
            // 以目录的形式新建。
            f1.mkdir();
        }*/
        // 如果路径下文件不存在,则以多级目录的形式创建出来
        File f2 = new File("D:/a/b/c/d/e/f");
        /*if(!f2.exists()) {
            // 以多重目录的形式新建。
            f2.mkdirs();
        }*/

        File f3 = new File("D:\\course\\01-开课\\学习方法.txt");
        // 获取文件的父路径
        String parentPath = f3.getParent();
        // 获取文件的父路径文件
        File parentFile = f3.getParentFile();
        //获取文件的绝对路径
        System.out.println("获取绝对路径:" + parentFile.getAbsolutePath());

常见面试题

  1. 使用字节文件流实现一个文件的复制?
    答:
    创建字节输入流对象FileInputStream;
    创建字节输出流对象FileOutPutStream;
    输入流对象从指定路径下read()读取文件,并将读取的内容使用字节数组byte[]存储;
    使用变量返回值判断文件是否有内容可读;
    输出流对象将输入流读取的字节数组中的内容write()写出;
    刷新输出流;
    关闭所有流,释放资源;
    重点:一边读,一边写,每次读取多少个就写出多少个。
public class StreamCopyTest {
    public static void main(String[] args) {
        //创建字节输入流引用
        FileInputStream fis = null;
        //创建字节输出流引用
        FileOutputStream fos = null;
        try {
            //创建实际的字节输入流对象
            fis = new FileInputStream("");
            //创建实际的字节输出流对象--输出文件若存在则覆盖,不存在则新建
            fos = new FileOutputStream("");
            //创建字节数组存储输入流读取的数据
            byte[] bs = new byte[1024 * 1024];//1024kb = 1byte,1mb = 1024kb
            //用于计算每次读取文件返回的字节个数,-1表示已读到文件末尾
            int readcount = 0;
            //一边读取,一边输出,读取多少个,输出多少个
            while ((readcount = fis.read(bs)) != -1){
                fos.write(bs,0,readcount);
            }
            //输出完成时,必须刷新管道
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流资源,释放内存
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 使用字符文件流实现一个文件的复制?
    答:
    创建字符输入流对象FileReader
    创建字符输出流对象FileWriter
    创建字符数组char[]存储字符输入流read读取的文件数据
    定义变量返回值 判断文件是否有数据可读
    字符输入流从文件读取数据
    字符输出流将读取到的数据写出到文件
    刷新输出流
    关闭所有资源
public class WriterCopyTest {
    public static void main(String[] args) {
        //创建字符输入流引用
        FileReader fr = null;
        //创建字符输出流引用
        FileWriter fw = null;
        try {
            //创建实际的字符输入流对象
            fr = new FileReader("");
            //创建实际的字符输出流对象,若输出文件已存在则覆盖,若不存在则新建
            fw = new FileWriter("");
            //创建字符数组存储输入流每一次读取的数据
            char[] cs = new char[1024*1024];
            //用于判断每一次读取文件返回的字节个数,-1表示已读到文件末尾
            int readchar = 0;
            //一边读取,一边写出,读取多少个写出多少个
            while ((readchar = fr.read(cs)) != -1){
                fw.write(cs,0,readchar);
            }
            //输出完成时,必须刷新管道
            fw.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流资源,释放内存
            if (fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 使用一个字节缓冲流 或 字符缓冲流实现一个文件的复制?
    答:
    BufferInputStream和BufferOutPutStream
    BufferReader和BufferWriter

  1. 实现从D盘的多个目录及文件复制到C盘下?
    答:
    使用File类获取得到多个目录(数组)
    遍历File数组中得到的所有File对象
    判断File数组中的File对象是目录还是文件
    (是目录继续往下遍历,是文件则直接复制到另外一个盘下)
    确定源目录的路径 和 目标目录的路径
    对File对象采用IO流进行边读边写
    如何确定对应的目录路径?
public static void main(String[] args) {
        // 拷贝源
        File srcFile = new File("D:\\course\\02-JavaSE\\document");
        // 拷贝目标
        File destFile = new File("C:\\a\\b\\c");
        // 调用方法完成拷贝
        copyDir(srcFile, destFile);
    }

    /**
     * 拷贝目录
     * @param srcFile 拷贝源
     * @param destFile 拷贝目标
     */
    private static void copyDir(File srcFile, File destFile) {
        if(srcFile.isFile()) {
            // srcFile如果是一个文件(文件下面不再有目录),递归结束
            // 并且是文件即需要拷贝,复制--一边读一边写
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                // 从源路径读取文件
                in = new FileInputStream(srcFile);
                // 处理得出要复制到的目标路径--路径的转换很难很重要,需要多重字符串截取和拼接
                //先获取目标路径的盘符根目录--再对源路径盘符下的子目录路径进行复制拼接                
                String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\")  + srcFile.getAbsolutePath().substring(3);
                //将读取的文件写入到新的盘符目录下                
                out = new FileOutputStream(path);
                // 一边读一边写,
                byte[] bytes = new byte[1024 * 1024]; // 一次复制1MB
                int readCount = 0;
                while((readCount = in.read(bytes)) != -1){
                    out.write(bytes, 0, readCount);
                }
                //写入完成时刷新管道
                out.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{   //关闭流资源,释放内存        
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return;
        }
        // 获取源路径下的所有子目录(文件)
        File[] files = srcFile.listFiles();
        for(File file : files){
            // 获取所有文件(包括目录和文件)的绝对路径
            //若是源路径下的File是一个目录,则在目标盘符下创建对应目录;若是一个File文件,则结束递归开始复制
            if(file.isDirectory()){
                // 在目标盘符下新建对应的目录
                //D:\course\02-JavaSE\document\JavaSE进阶讲义     源目录
                //C:\course\02-JavaSE\document\JavaSE进阶讲义     目标目录
                //源file目录
                String srcDir = file.getAbsolutePath();
                //新file要复制到的新目录
                String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\")  + srcDir.substring(3);
                //使用新目录创建出File文件对象
                File newFile = new File(destDir);
                if(!newFile.exists()){
                    newFile.mkdirs();
                }
            }
            // 递归调用--很难想到使用递归
            copyDir(file, destFile);
        }
    }

你可能感兴趣的:(JAVA学习第十一天之io流)