文件

File类引入

  • 存储在变量,数组和对象中的数据是暂时的,当程序终止时他们就会丢失.为了能够永久的保存程序中创建的数据,需要将他们存储到硬盘或光盘的文件中.这些文件可以移动,传送,亦可以被其他程序使用.
  • 由于数据存储在文件中,所以我们需要学习一个和文件有密切关系的类,叫做File类,将要掌握获取文件的属性以及删除和重命名文件.最终如何向文件中写入数据和从文件中读取数据.

绝对路径与相对路径

  • 对于UNIX平台,绝对路径名的前缀是”/“。相对路径名没有前缀。

  • 对于Windows平台,绝对路径名的前缀由驱动器号和一个”:”组成,例c:\…。相对路径没有盘符前缀。

  • 相对路径:相对路径是指相对于某位置的路径,是指相对于当前目录。

  • 在执行Java程序时,相对路径为执行java命令时当前所在的目录。

File类

概述

  • 那么File类关心的是在磁盘上文件的存储.

  • File类是java.io包下代表与平台无关的文件和目录,也就是说,如果希望在程序中操作文件和目录,都可以通过File类来完成。

  • 该类的出现是对文件系统的中的文件以及文件夹进行对象的封装。可以通过对象的思想来操作文件以及文件夹。通过该对象的方法,可以得到文件或文件夹的信息方便了对文件与文件夹的属性信息进行操作。文件包含很多的信息:如文件名、创建修改时间、大小、可读可写属性等。

  • File类主要用来获取文件(或目录)本身的一些信息,如文件的名字,不涉及文件的读写操作。

  • 文件目录的分隔:\\或反斜杠(/)

构造方法

说明:如果指定的路径不存在(没有这个文件或是文件夹),不会抛异常,这时file.exists()返回false。

  • 构造方法

    • File对象没有无参数构造.创建对象需要传参.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    new File(String pathname);
    通过将给定路径来创建一个新File实例。

    new File(String parent, String child);
    根据parent路径名字符串和child路径名创建一个新File实例。
    parent是指上级目录的路径,完整的路径为parent+child.

    new File(File parent, String child);
    根据parent抽象路径名和child路径名创建一个新File实例。
    parent是指上级目录的路径,完整的路径为parent.getPath()+child.
  • 示例代码

    • 创建File对象需要导包, import java.io.File

      1
      2
      3
      4
      5
      6
      //File类的对象,既可以代表文件也可以代表文件夹。
      public static void main(String[] args) {
      //file 是一个文件夹
      String path = "c:/test";
      File file = new File(path);
      }

成员方法

  • 创建

    方法名 描述
    createNewFile() 在指定位置创建一个空文件
    成功就返回true,如果已存在就不创建然后返回false
    mkdir() 在指定位置创建目录
    这只会创建最后一级目录,如果上级目录不存在就抛异常。
    mkdirs() 在指定位置创建目录
    这会创建路径中所有不存在的目录。
    renameTo(File dest) 重命名文件或文件夹
    也可以操作非空的文件夹,文件不同时相当于文件的剪切
    剪切时候不能操作非空的文件夹。
    移动/重命名成功则返回true,失败则返回false。
  • 删除

    方法名 描述
    delete() 删除文件或一个空文件夹
    如果是文件夹且不为空,则不能删除
    成功返回true,失败返回false。
    deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录,保证程序异常时创建的临时文件也可以被删除
  • 判断

    方法名 描述
    boolean canExecute() 判断文件是否可执行
    boolean canRead() 判断文件是否可读
    boolean canWrite() 判断文件是否可写
    exists() 文件或文件夹是否存在。
    isFile() 是否是一个文件,如果不存在,则始终为false。
    isDirectory() 是否是一个目录,如果不存在,则始终为false。
    isHidden() 是否是一个隐藏的文件或是否是隐藏的目录。
    isAbsolute() 测试此抽象路径名是否为绝对路径名。
  • 获取

    方法名 描述
    getName() 获取文件或文件夹的名称,不包含上级路径。
    getPath() 返回绝对路径,可以是相对路径,但是目录要指定
    getAbsolutePath() 获取文件的绝对路径,与文件是否存在没关系
    getParent() 返回此抽象路径名父目录的路径名字符串;
    如果此路径名没有指定父目录,则返回null。
    lastModified() 获取最后一次被修改的时间。
    length() 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。
  • 文件夹相关

    方法名 描述
    staic File[] listRoots() 列出所有的根目录(Window中就是所有系统的盘符)
    list() 返回目录下的文件或者目录名,包含隐藏文件。对于文件这样操作会返回null。
    list(FilenameFilter filter) 返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。
    listFiles() 返回目录下的文件或者目录对象(File类实例),包含隐藏文件。对于文件这样操作会返回null。
    listFiles(FilenameFilter filter) 返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。
  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public static void main(String[] args) throws Exception {
    // 构造方法
    //默认为当前项目
    File file = new File("a.txt");
    File file2 = new File("d:/a/a.txt");
    File file3 = new File("d:/a", "a.txt");

    // 判断文件是否存在
    System.out.println(file.exists());

    // 新建文件
    file.createNewFile();
    System.out.println("---------"+file.exists());
    // 获得文件的名称
    System.out.println(file.getName());

    // 获得文件路径路径
    // 路径,如果是绝对路径显示绝对,如果是相对路径显示相对
    System.out.println(file.getPath());
    // 绝对路径
    System.out.println(file.getAbsolutePath());

    //获取父目录
    System.out.println(file.getParent());

    //是否可读,可写
    System.out.println(file.canRead());
    System.out.println(file.canWrite());

    //是不是文件
    System.out.println(file.isFile());

    //上一次修改时间

    System.out.println(new Date(file.lastModified()));

    //文件大小
    System.out.println(file.length()); //0

    //删除文件
    System.out.println(file.delete());
    }

路径分隔符

  • 路径就是文件或文件夹所在的位置。

  • 上下级文件夹之间使用分隔符分开:

    • windows: / \都可以
    • Unix/Linux中分隔符为/
    • 如果windows选择用\做分割符的话,那么请记得替换成\\,因为Java中\代表转义字符
    • 所以推荐都使用/,也可以直接使用代码File.separator,表示跨平台分隔符。
  • 跨平台的目录分隔符

    • 更专业的做法是使用File.separatorChar,这个值就会根据系统得到的相应的分割符。
    1
    2
    new File("c:" + File.separatorChar + "a.txt");
    //注意,如果是使用"\",则需要进行转义,写为"\\"才可以,如果是两个"\",则写为"\\\\"。

文件信息遍历

  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    File file = new File("C:\\Users\\fulsun\\Desktop");
    System.out.println(file.getAbsolutePath());

    // 判断是否是文件夹
    System.out.println(file.isDirectory());

    // 判断文件夹存在
    System.out.println(file.exists());

    // 获得目录下的子目录 和 文件的字符串形式
    file.list(new FilenameFilter() {

    @Override
    public boolean accept(File dir, String name) {
    return name.endsWith(".java");
    }
    });

    // lambda表达式
    String[] fileList = file.list((d, name) -> name.endsWith("java"));

    //遍历输出
    for (String str : fileList) {
    System.out.println(str);
    }

    // 获得目录下的子目录 和 文件的File形式

    File[] listFiles = file.listFiles();
    listFiles = file.listFiles(new FileFilter() {

    @Override
    public boolean accept(File path) {
    return path.getName().endsWith("java");
    }
    });

    // 表达式
    listFiles = file.listFiles(f -> f.getName().endsWith("java"));

    // 查看
    for (File ff : listFiles) {
    if (ff.isDirectory()) {
    System.out.println(ff.getPath());
    } else {
    System.out.println("文件:" + ff.getName());
    }

    }

递归遍历文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
File file = new File("C:\\Users\\sun\\Desktop\\day15");

// 递归遍历文件
while (true) {
File[] listFiles = file.listFiles();
if (listFiles == null) {
return;
}

for (File file1 : listFiles) {
if (file1.isDirectory()) {
System.out.println(file1.getPath());
} else {
System.out.println(file1.getAbsolutePath());
}
file = file1;
}
}

}

FileNameFilter

  • 该接口是个文件过滤器,包含了一个accept(File dir,String name)方法,该方法依次对指定File的所有子目录或者文件进行迭代,按照指定条件,进行过滤,过滤出满足条件的所有文件。

  • file目录下的所有子文件如果满足后缀是.mp3的条件的文件都会被过滤出来。

    1
    2
    3
    4
    5
    6
    7
    // 文件过滤
    File[] files = file.listFiles(new FilenameFilter() {
    @Override
    public boolean accept(File file, String filename) {
    return filename.endsWith(".mp3");
    }
    });

RandomAccessFile

简介

  • RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
  • 由于RandomAccessFile可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
  • 与OutputStream、Writer等输出流不同的是,RandomAccessFile允许自由定义文件记录指针,RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
  • RandomAccessFile的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。
  • RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。

RandomAccessFile构造函数

  • RandomAccessFile类有两个构造函数,其实这两个构造函数基本相同,只不过是指定文件的形式不同

    • 一个需要使用String参数来指定文件名,一个使用File参数来指定文件本身。
    • 除此之外,创建RandomAccessFile对象时还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,一共有4种模式。
  • 关于权限说明

    “r”: 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

    “rw”: 打开以便读取和写入。

    “rws”: 打开以便读取和写入。相对于 “rw”,”rws”。还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。

    “rwd” : 打开以便读取和写入,相对于 “rw”,”rwd”。还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

RandomAccessFile重要方法

  • RandomAccessFile既可以读文件,也可以写文件,所以类似于InputStream的read()方法,以及类似于OutputStream的write()方法,RandomAccessFile都具备。除此之外,RandomAccessFile具备两个特有的方法,来支持其随机访问的特性。
  • RandomAccessFile对象包含了一个记录指针,用以标识当前读写处的位置
  • 当程序新创建一个RandomAccessFile对象时,该对象的文件指针记录位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会后移n个字节。
  • 除此之外,RandomAccessFile还可以自由移动该记录指针。下面就是RandomAccessFile具有的两个特殊方法,来操作记录指针,实现随机访问:
    • long getFilePointer( ):返回文件记录指针的当前位置
    • void seek(long pos\):将文件指针定位到pos位置

RandomAccessFile使用

  • 利用RandomAccessFile实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。

  • 下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    /**
    * 测试利用多线程进行文件的写操作
    */
    public class Test {

    public static void main(String[] args) throws Exception {
    // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件
    RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");
    raf.setLength(1024*1024); // 预分配 1M 的文件空间
    raf.close();

    // 所要写入的文件内容
    String s1 = "第一个字符串";
    String s2 = "第二个字符串";
    String s3 = "第三个字符串";
    String s4 = "第四个字符串";
    String s5 = "第五个字符串";

    // 利用多线程同时写入一个文件
    new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据
    new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据
    new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据
    new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据
    new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据
    }

    // 利用线程在文件的指定位置写入指定数据
    static class FileWriteThread extends Thread{
    private int skip;
    private byte[] content;

    public FileWriteThread(int skip,byte[] content){
    this.skip = skip;
    this.content = content;
    }

    public void run(){
    RandomAccessFile raf = null;
    try {
    raf = new RandomAccessFile("D://abc.txt", "rw");
    raf.seek(skip);
    raf.write(content);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally {
    try {
    raf.close();
    } catch (Exception e) {
    }
    }
    }
    }

    }
  • 当RandomAccessFile向指定文件中插入内容时,将会覆盖掉原有内容。如果不想覆盖掉,则需要将原有内容先读取出来,然后先把插入内容插入后再把原有内容追加到插入内容后。