Java的文件IO编程

IO问题是任何编程语言都无法回避的,可以说,IO问题是整个人机交互的核心问题。在Java中,提供了包名为java.io的专门操作类库。
在这个包下提供了将近有80个类,这些类大致分为四组:

  • 基于字节操作的InputStreamOutputStream
  • 基于字符操作的IO接口: WriterReader
  • 基于磁盘操作的IO接口: File
  • 基于网络操作的IO接口:Socket

要学好Java的文件IO操作,重点需要掌握:

  • 2个代码模型
  • 5个类
    • File
    • OutputStream
    • InputStream
    • Reader
    • Writer
  • 1个接口(Serializable)

1. File类的基本使用

这个类的使用直接产生实例化对象即可。

1.1 File类实例化对象

如果要实例化对象,需要用到两个构造方法。

  1. public File(String pathName)
  2. public File(File parent,String child),设置父路径和子文件

1.2 File类操作文件

如果要进行文件的操作,可以使用下列方法:

  • createNewfile: 根据制订的路径创建新文件
  • exists(); 判断文件是否存在
  • file.delete(); 删除文件

实例: 判断文件是否存在,存在的话就删除,不存在的话就创建

public class FileDemo {
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\hello.txt");   // 定义要操作的文件路径
        if (file.exists()) {   // 判断文件是否存在
            System.out.println("文件已经存在,已经将其删除");
            file.delete();   //删除文件
        } else {
            System.out.println("不存在文件,已经创建了一个新的!");
            file.createNewFile();   //创建一个空文件
        }
    }
}

说明,如果此处操作的都是根路径,如果是含有子路径的话,如果路径不存在,是不会自动创建路径的,会报错

Java的文件IO编程_第1张图片
File类基本操作

注意:路径分隔符
由于不同的操作系统对路径分隔符是不同的,因此,使用路径的时候,需要将上述指定操作文件的位置修改为如下:
File file = new File("D:"+File.separator+"hello.txt"); // 定义要操作的文件路径

1.3 File类操作目录

有以下常用的方法操作目录

  • 获得父目录的方法1: getParentFile(),该方法返回的是File类型的值(推荐使用)
  • 获取父目录的方法2: getParent(),该方法返回的是String类型的值
  • 创建父目录,就对获取的父目录的返回对象执行: file.getParentFile().mkdirs(),该方法如果有多层路径,都可以自动创建。
public class FileDemo {
    public static void main(String[] args) throws IOException {
     /**
         * 对路径的操作
         * */
        File file = new File("D:"+File.separator+"hello"+File.separator+"hello.txt");   // 定义要操作的文件路径
        if(!file.getParentFile().exists()){  // 判断父目录是否存在
            System.out.println(file.getParentFile().toString());
            file.getParentFile().mkdirs();   // 默认创建多级父目录
        }
        file.createNewFile();
    }
}

1.4 File类取得文件信息

File类提供很多取得文件信息的方法。方法有很多,使用方式也都类似,直接通过一个例子记录一下吧。

范例

class MyMath {
    // 保留几位小数
    public static double round(double num, int scale) {
        return Math.round(num * Math.pow(10, scale)) / Math.pow(10, scale);
    }
}

public class FileInfoDemo {
    public static void main(String[] args) {
        File file = new File("D:\\4. 暂存待整理\\截图\\Screenshot_2016-07-17-11-21-27.png");   // 定义要操作的文件路径
        if (file.exists() && file.isFile()) {  // 文件存在且是文件
            System.out.println("文件大小:" + MyMath.round((file.length()) / 1024, 2));   // 文件大小为字节数,太大了,仅保留2位小数
            System.out.println("上次修改时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date(file.lastModified())));
        }
    }
}

2. 字节流与字符流

File类本质上不操作文件的内容,要进行文件内容的处理,就需要用到字节流和字符流的概念。
字节流和字符流的区别是什么?

  1. 字节流是指文件作为数据的二进制传输形式,而字符流则是在内存中进行操作的对象的感觉。这么理解不太严密,但是可以简单认为,字符流更靠近文件。
  2. 字节流更多的是文件在磁盘上的保存,网络的数据传输形式等。
  3. 字符更适合处理中文

不管是字节流还是字符流,其处理文件内容的基本步骤都是如下的思路:

  • 要根据文件的路径创建File类的对象
  • 根据字节流或者字符流的子类实例化父类对象
  • 进行数据的读取或者写入操作
  • 关闭流:IO操作属于资源操作,所有的资源操作都必须执行关闭操作。

2.1 字节输出流OutputStream

如果要通过程序进行内容的输出,则可以使用字节输出流OutputStream。

Java的文件IO编程_第2张图片
OutputStream的继承关系

OutputStream是一个抽象类,实现了Closeable接口和Flushable接口,通过这两个接口,继承了他们各自的关闭方法和刷新方法。同样的,由于他是一个抽象类,必须使用它的子类进行实例化,那么就会需要针对不同的操作,实现不同的子类实例化。对于文件的操作,使用FileOutputStream进行实例化。 FileOutputStream拥有不同的构造函数,根据构造函数的不同,可以进行文件的覆盖写入和文件的追加写入设置等。

FileOutputStream的不同构造方法

  • 覆盖写入: FileOutputStream(File file)
  • 追加写入:+ 覆盖写入: FileOutputStream(File file,boolean append)

FileOutputStream的写入方法

  • 全部写入: write(byte[] content)
  • 局部写入: write(byte[] content,int start, int len)
  • 写入单个字节: write(int content)

范例:使用FileOutputStream进行文件内容的写入/追加等。

/** 文件内容的写入操作范例 */
public class OutputStreamDemo {
  public static void main(String[] args) throws IOException {
    // 1. 确定要操作的文件
    File file = new File("D:\\filewrite.txt");
    // 2. 实例化一个文件类型的OutputStream
    OutputStream os = new FileOutputStream(file); // 覆写
    //      OutputStream os = new FileOutputStream(file, true);   // 追加

    // 3. 写入文件
    // 3.1 全部写入
    String content = "this is a test String!\r\n";
    os.write(content.getBytes());

    // 3.2 部分写入
    os.write(content.getBytes(), 0, 9);

    // 3.3 写入单个字节
    os.write(65);

    // 4. 关闭流
    os.close();
  }
}

说明:

  1. 使用FileOutputStream进行文件操作的时候,就不在需要进行文件的创建,调用写入的方法时,会进行自动的创建。(只创建文件,如果有多级,那么目录一定要确保是存在的)
  2. 一定要记得关闭文件字节流。

2.2 字节输入流InputStream

InputStream是字节输入流,其使用方法和OutputStream类几乎一样。
原理是通过read方法将流对象中的数据读入的byte数组中,之后通过操作数组实现读取到的内容的操作。

import java.io.*;

public class InputStreamDemo {
  public static void main(String[] args) throws IOException {
    // 1. 定位到要处理的文件
    File file = new File("D:\\ProgramFiles\\kibana-5.6.2-windows-x86\\LICENSE.txt");

    // 2. 使用FileInputStream类实例化InputStream对象
    InputStream input = new FileInputStream(file);

    // 3. 读取流的内容到字节数组中
    byte[] content = new byte[1024];
    int len = input.read(content);
    
    System.out.println("读取文件内容为:【" + new String(content, 0, len) + "】");
    // 4. 关闭流
    input.close();
  }
}

2.3 字符输出流Writer

Writer是一种字符输出流的处理类,使用方法也和OutputStream使用类似,还是使用FileWriter进行父类对象的实例化。
比较有特色的就是,可以直接向文件输出字符数组或者字符串。

范例:使用Writer类进行字符输出。

package org.liyubo.java8demos.fileops;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriterDemo {
  public static void main(String[] args) throws IOException {
      // 1. 定位到要操作的文件
      File file = new File("D:\\test\\fileops\\testwriter.txt");

      if(!file.getParentFile().exists()){  // 如果父目录不存在的话,创建父目录
          file.getParentFile().mkdirs();
      }
      // 2. 创建Writer对象,并使用FileWriter进行实例化
      Writer output  = new FileWriter(file);

      // 3. 执行输出,调用输出字符串或者
      output.write("你好,中国!");
      output.append('H');
      char[] content = new char[]{'s','v'};
      output.write(content);

      // 4. 关闭
      output.close();
  }
}
Java的文件IO编程_第3张图片
Writer写入结果

2.4 字符输入流Reader

Reader类是一个字符输入类,使用方法和Writer类似,不同的是,其只有将字符输入流读入字符数组的功能,没有读入字符串的功能。

不写例子了,没啥意思。

字节输入/出流和字符输入/出流区别

其实,字符流的核心还是因为对中文操作更好,但是现在使用一些方法也能将中文使用字节流很好的处理了。 在正常的开发中,还是字节流使用更多一些。

那么,所谓的一些操作又是怎么实现的呢?
原因就在于,其实,从流到文件中间,还有一层就是内存的缓冲。在流关闭之前,内容都是缓冲在内容中的,只有通过flush()操作强制刷新,内容才会写入到文件。因此,所谓的一些操作,就是如何在内存中对流的缓冲进行操作的问题。在内存的缓冲区中,就可以进行字节流和字符流之间的相互转换。

3. 转换流

如何在缓冲区中进行字符流和字节流的互相转换呢,此处就主要涉及两个类:

  • OutputStreamWriter: 将字节输出流转换成字符输出流。
  • InputStreamReader:将字节输入流转换成字符输入流。
Java的文件IO编程_第4张图片
image.png

范例: 使用转换流的操作

public class OutputStreamWriterDemo {
  public static void main(String[] args) throws IOException {
      // 1. 定位到要操作的文件
      File file = new File("D:\\test\\fileops\\testwriter.txt");

      if(!file.getParentFile().exists()){  // 如果父目录不存在的话,创建父目录
          file.getParentFile().mkdirs();
      }
      OutputStream os = new FileOutputStream(file);
      OutputStreamWriter writer = new OutputStreamWriter(os);

      // 3. 执行输出,调用输出字符串或者
      writer.write("你好,中国!");
      writer.append('H');

      char[] content = new char[]{'s','v'};
      writer.write(content);

      // 4. 关闭
      writer.close();
      os.close();
  }
}

你可能感兴趣的:(Java的文件IO编程)