File类的使用

File类的理解

  1. File类的一个对象,代表一个文件或者一个文件目录(文件夹)。
  2. File类声明在java.io包下。
  3. File类中涉及到关于文件或者文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或者写入文件内容,则必须使用IO流来实现。
  4. 后续File类的对象常会作为参数传递到流的构造器中,指明读取或者写入的“终点“。

File的实例化

File类的创建构造器

1
2
3
File(String filePath)
File(String parentPath, String childPath)
File(File parentFile, String childPath)

image-20210203191639004

路径的分类

  • 相对路径:相较于某个路径下,指明的路径

    IDEA中:

    在单元测试方法中,默认根目录是当前Module;

    在mian方法中,默认根目录是当前Project;

    Eclipse中:

    无论是单元测试还是main(),相对路径都是基于当前Project的。

  • 绝对路径:包括盘符在内的文件或者文件目录的路径

路径分隔符

  • windows:\\
  • Unix:/

在Java中,File类提供了一个常量来根据操作系统,动态的提供分隔符:

1
public static final String separator;

File类的常用方法

File类的获取功能

1
2
3
4
5
6
7
8
9
- public String getAbsolutePath():获取绝对路径
- public String getPath():获取路径
- public String getName():获取名称
- public String getParent():获取上层文件目录路径。若无,返回null 
- public long length() :获取文件长度(即:字节数)。不能获取目录的长度。 
- public long lastModified() :获取最后一次的修改时间,毫秒值

- public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 
- public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组

File类的重命名功能

1
- public boolean renameTo(File dest):把文件重命名为指定的文件路径

注意:dest文件不能存在。

File类的判断功能

1
2
3
4
5
6
- public boolean isDirectory():判断是否是文件目录 
- public boolean isFile() :判断是否是文件 
- public boolean exists() :判断是否存在 
- public boolean canRead() :判断是否可读 
- public boolean canWrite() :判断是否可写 
- public boolean isHidden() :判断是否隐藏

File类的创建功能

1
2
3
- public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false 
- public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。
- public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建

File类的删除功能

1
- public boolean delete():删除文件或者文件夹

Java中的删除不走回收站♻️。

要删除一个文件夹,要注意该文件夹内不能包含内容,必须是一个空的文件夹。

IO流概述

流的分类

  1. 操作数据单位:字节流、字符流
  2. 数据的流向:输入流、输出流
  3. 流的角色:节点流(或者叫文件流)、处理流

image-20210220215945748

流的体系结构

image-20210220220435028

红色框的部分为IO流的四个最基本的抽象基类。

蓝色框的部分为需要重点掌握的几个流。

重点说明的几个流结构

抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
InputStream FileInputStream【read(byte[] buffer)】 BufferedInputStream
OutputStream FileOutputStream【write(byte[] buffer,0,len)】 BufferedOutputStream
Reader FileReader【read(char[] cbuf)】 BufferedReader【readLine()】
Writer FileWriter【writer(char[] cbuf,0,len)】 BufferedWriter

输入、输出的标准化过程

输入过程

  1. 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
  2. 创建相应的输入流,将File类的对象作为参数,传入流的构造器中。
  3. 具体的读入过程:创建对应的byte[]或者char[]。
  4. 关闭流资源。

说明:程序中出现的异常需要使用try-catch-finally处理。

输出过程

  1. 创建File类的对象,指明写出的数据的来源。(不要求此文件一定要存在)
  2. 创建相应的输出流,将File类的对象作为参数,传入流的构造器中。
  3. 具体的写出过程:write(char[]/byte[],0,len);
  4. 关闭流资源。

说明:程序中出现的异常需要使用try-catch-finally处理。

节点流(或文件流)

FileReader/FileWriter的使用

FileReader

将硬盘中的文件读入到内存中。

  1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
  2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally来处理。
  3. 读入的文件一定要存在,否则就会报FileNotFoundException。
 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
@Test
void testFileReader1() {
  FileReader fr = null;
  // try-catch快捷键:option+cmd+t
  try {
    // 1. 创建file对象
    File file = new File("hello.txt");
    // 2. 创建节点流
    fr = new FileReader(file);

    // 3. 读取文件流
    // 方式一:一个字符一个字符读取
    //int data;
    //while ((data = fr.read()) != -1) {
    //    System.out.print((char) data);
    //}

    // 方式二:一次型读取多个字符
    char[] cbuf = new char[5];
    int len;
    while ((len = fr.read(cbuf)) != -1) {
      String output = new String(cbuf, 0, len);
    }
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    // 4.关闭文件流
    try {
      // 记得添加if判断其是否为null
      if (fr != null) {
        fr.close();
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

FileWriter

从内存中写出数据到硬盘的文件里。

  1. 输出操作,对应的File可以不存在的。并不会报异常。
  2. File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
  3. File对应的硬盘中的文件如果存在:
    1. 如果流使用的构造器是:FileWriter(file, false) / FileWriter(file) 对原文件的覆盖
    2. 如果流使用的构造器是:FileWriter(file, true) 不会覆盖原文件,而是在原文件的基础上追加内容。
 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
@Test
void testFileWriter() {
  	// 1. 创建File对象
    File file = new File("hello1.txt");
    FileWriter fw = null;
    try {
        // FileWriter构造器分类:
        // 1. 覆盖写入:FileWriter(file) / FileWriter(file,false)
        // 2. 追加写入:FileWriter(file, true)
        //fw = new FileWriter(file);
      
      	// 2. 提供FileWriter的对象,用于数据的写入
        fw = new FileWriter(file, true);
      
      	// 3.写出的操作
        fw.write("hello world!\n");
        fw.write("nice to meet you.");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
          	// 4.流资源的关闭
            if (fw != null) {
                fw.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

文本文件(字符流)的复制

 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
/**
     * 文本文件的读写
     */
@Test
void testFileReaderAndWriter() {
  FileReader fr = null;
  FileWriter fw = null;
  try {
    //1. 创建File类的对象,指明读入和写出的文件
    // 注意:不能使用字符流来处理图片等子节数据
    File srcFile = new File("hello1.txt");
    File desFile = new File("hello2.txt");
    
    //2. 创建输入流和输出流的对象
    fr = new FileReader(srcFile);
    fw = new FileWriter(desFile);
    
    //3. 数据的读入和写出操作
    char[] cbuf = new char[5];
    // 记录每次读入到cbuf数组中的字符的个数
    int len;
    while ((len = fr.read(cbuf)) != -1) {
      // 每次写出len个字符
      fw.write(cbuf, 0, len);
    }
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    //4. 关闭流资源
    if (fw != null) {
      try {
        fw.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    if (fr != null) {
      try {
        fr.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

FileInputStream/FileOutputStream的使用

  1. 对于文本文件(.txt, .java, .c, .cpp)等使用字符流处理
  2. 对于非文本文件(.jpg, .mp3, .mp4, .avi, .doc, .ppt, …)等,使用字节流处理
 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
@Test
void testInputStreamReaderAndWriter() {
  FileInputStream fis = null;
  FileOutputStream fos = null;
  try {
    // 1. 创建输入和输出的文件File对象
    File srcImg = new File("input.png");
    File desImg = new File("out.png");
    
    // 2.创建输入流和输出流对象
    fis = new FileInputStream(srcImg);
    fos = new FileOutputStream(desImg);
    
    // 3.开始复制操作,一般使用1024个字节数组
    byte[] bytes = new byte[1024];
    // 记录每次读到bytes数组中字节的长度
    int len;
    while ((len = fis.read(bytes)) != -1) {
      // 每次写入len个字节
      fos.write(bytes, 0, len);
    }
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    // 4.流资源的关闭
    if (fos != null) {
      try {
        fos.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    if (fis != null) {
      try {
        fis.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

缓冲流的作用

缓冲流涉及到的类

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedReader
  • BufferedWriter

作用

提高流的读写速度。

提高读写速度的原因:内部提供了一个缓冲区。默认情况下是8kb。

image-20210221132604462

典型代码

使用BufferedInputStream和BufferedOutputStream处理非文本文件

 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
/*
 实现非文本文件的复制
*/
@Test
public void BufferedStreamTest() throws FileNotFoundException {
  BufferedInputStream bis = null;
  BufferedOutputStream bos = null;

  try {
    //1.造文件
    File srcFile = new File("爱情与友情.jpg");
    File destFile = new File("爱情与友情3.jpg");
    //2.造流
    //2.1 造节点流
    FileInputStream fis = new FileInputStream((srcFile));
    FileOutputStream fos = new FileOutputStream(destFile);
    //2.2 造缓冲流
    bis = new BufferedInputStream(fis);
    bos = new BufferedOutputStream(fos);

    //3.复制的细节:读取、写入
    byte[] buffer = new byte[10];
    int len;
    while((len = bis.read(buffer)) != -1){
      bos.write(buffer,0,len);
    //bos.flush();//刷新缓冲区
    }
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    //4.资源关闭
    //要求:先关闭外层的流,再关闭内层的流
    if(bos != null){
      try {
        bos.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    if(bis != null){
      try {
        bis.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
    //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
    //        fos.close();
    //        fis.close();
  }
}

使用BufferedReader和BufferedWriter处理文本文件

 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
/*
    使用BufferedReader和BufferedWriter实现文本文件的复制

*/
@Test
public void testBufferedReaderBufferedWriter(){
  BufferedReader br = null;
  BufferedWriter bw = null;
  try {
    //创建文件和相应的流
    br = new BufferedReader(new FileReader(new File("dbcp.txt")));
    bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

    //读写操作
    //方式一:使用char[]数组
    //            char[] cbuf = new char[1024];
    //            int len;
    //            while((len = br.read(cbuf)) != -1){
    //                bw.write(cbuf,0,len);
    //    //            bw.flush();
    //            }

    //方式二:使用String
    String data;
    while((data = br.readLine()) != null){
      //方法一:
      //                bw.write(data + "\n");//data中不包含换行符
      //方法二:
      bw.write(data);//data中不包含换行符
      bw.newLine();//提供换行的操作

    }


  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    //关闭资源
    if(bw != null){
      try {
        bw.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    if(br != null){
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
  }

}

⚠️注意:

  • BufferedReader中多了一个readLine()方法可以直接读取整行字符,但是不包括换行符,所以如果调用此方法,需要手动调用newLine()方法添加换行符。

  • flush()方法会手动的将内存中的数据写出去。

转换流的作用

转换流涉及到的类

  • InputStreamReader:将一个字节的输入流转换为字符的输入流

    解码:字节、字节数组 —>字符数组、字符串

  • OutputStreamWriter:将一个字符的输出流转换为字节的输出流

    编码:字符数组、字符串 —> 字节、字节数组

都属于字符流。

作用

提供字节流与字符流之间的转换。

图示

image-20210221142422125

典型实现

解码过程:将字节转换成字符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/*
    此时处理异常的话,仍然应该使用try-catch-finally
    InputStreamReader的使用,实现字节的输入流到字符的输入流的转换
     */
@Test
public void test1() throws IOException {
  FileInputStream fis = new FileInputStream("dbcp.txt");
  //        InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
  //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
  InputStreamReader isr = new InputStreamReader(fis,"UTF-8");

  char[] cbuf = new char[20];
  int len;
  while((len = isr.read(cbuf)) != -1){
    String str = new String(cbuf,0,len);
    System.out.print(str);
  }

  isr.close();

}

编码过程:将字符转换成字节

 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

/*
    此时处理异常的话,仍然应该使用try-catch-finally
    综合使用InputStreamReader和OutputStreamWriter
     */
@Test
public void test2() throws Exception {
  //1.造文件、造流
  File file1 = new File("dbcp.txt");
  File file2 = new File("dbcp_gbk.txt");

  FileInputStream fis = new FileInputStream(file1);
  FileOutputStream fos = new FileOutputStream(file2);

  InputStreamReader isr = new InputStreamReader(fis,"utf-8");
  OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

  //2.读写过程
  char[] cbuf = new char[20];
  int len;
  while((len = isr.read(cbuf)) != -1){
    osw.write(cbuf,0,len);
  }

  //3.关闭资源
  isr.close();
  osw.close();


}

说明

文件编码的方式(比如说是GBK),决定了解析时使用的字符集(也只能是GBK)。

1
2
FileInputStream fis = new FileInputStream("bgk_file.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");

补充:编码表

常见的编码表:

  • ASCII:美国标准信息交换码。用一个字节的7位可以表示。

  • ISO8859-1:拉丁码表。欧洲码表。用一个字节的8位表示。

  • GB2312:中国的中文编码表。最多两个字节编码所有字符

  • GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码

  • Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的 字符码。所有的文字都用两个字节来表示。

  • UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

客户端/浏览器端。<—–>后台(java,go,php…)<—–>数据库

要求前前后后使用的字符集都要统一:UTF-8

其他的流的使用

标准的输入流输出流

  • System.in:标准的输入流,默认从键盘输入

  • System.out:标准的输出流,默认从控制台输出

修改默认的输入和输出的行为:

System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流。

打印流

PrintStream 和PrintWriter

二者都是输出流

  • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
  • System.out返回的是PrintStream的实例

数据流

DataInputStream 和 DataOutputStream

  • 用于读取或写出基本数据类型的变量或字符串。

对象流的使用

对象流

ObjectInputStream 和ObjectOutputStream

作用

  • ObjectOutputStream:内存中的对象—–>存储中的文件、通过网络传输出去

  • ObjectInputStream:存储中的文件、通过网络传输出去—–>内存中的对象

对象的序列化机制

序列化过程:对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传 输到另一个网络节点。

反序列化过程:当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象

代码实现

 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
// 序列化过程:
@Test
void testObjectOutputStream() {
  ObjectOutputStream oss = null;
  try {
    FileOutputStream fos = new FileOutputStream("object.serial");
    oss = new ObjectOutputStream(fos);
    // 将对象序列化
    oss.writeObject(new String("hello world!"));
    oss.flush();
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    if (oss != null) {
      try {
        oss.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

// 反序列化过程:
@Test
void testObjectInputStream() {
  ObjectInputStream ois = null;
  try {
    FileInputStream fis = new FileInputStream("object.serial");
    ois = new ObjectInputStream(fis);
    // 反序列化过程
    Object object = ois.readObject();
    String str = (String) object;
    System.out.println("str = " + str);
  } catch (IOException e) {
    e.printStackTrace();
  } catch (ClassNotFoundException e) {
    e.printStackTrace();
  } finally {
    if (ois != null) {
      try {
        ois.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
  }
}

RandomAccessFile的使用

随机存取文件流使用说明

  1. RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口

  2. RandomAccessFile既可以作为一个输入流,又可以作为一个输出流

  3. 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖)

  4. 可以通过相关的操作,实现RandomAccessFile“插入”数据的效果

典型代码1

 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
@Test
public void test1() {

  RandomAccessFile raf1 = null;
  RandomAccessFile raf2 = null;
  try {
    //1.
    raf1 = new RandomAccessFile(new File("爱情与友情.jpg"),"r");
    raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"),"rw");
    //2.
    byte[] buffer = new byte[1024];
    int len;
    while((len = raf1.read(buffer)) != -1){
      raf2.write(buffer,0,len);
    }
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    //3.
    if(raf1 != null){
      try {
        raf1.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
    if(raf2 != null){
      try {
        raf2.close();
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
  }
}

@Test
public void test2() throws IOException {

  RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");

  raf1.seek(3);//将指针调到角标为3的位置
  raf1.write("xyz".getBytes());//

  raf1.close();

}

典型代码2

 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
/*
    使用RandomAccessFile实现数据的插入效果
     */
@Test
public void test3() throws IOException {

  RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");

  raf1.seek(3);//将指针调到角标为3的位置
  //保存指针3后面的所有数据到StringBuilder中
  StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
  byte[] buffer = new byte[20];
  int len;
  while((len = raf1.read(buffer)) != -1){
    builder.append(new String(buffer,0,len)) ;
  }
  //调回指针,写入“xyz”
  raf1.seek(3);
  raf1.write("xyz".getBytes());

  //将StringBuilder中的数据写入到文件中
  raf1.write(builder.toString().getBytes());

  raf1.close();

  //思考:将StringBuilder替换为ByteArrayOutputStream
}

Path、Paths、Files的使用

NIO的使用说明

Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新 的IO API,可以替代标准的Java IO API。

NIO与原来的IO有同样的作用和目 的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于 通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

Path的使用–jdk7提供

Path的说明

Path替换原有的File类。

如何实例化

Paths 类提供的静态 get() 方法用来获取 Path 对象:

  • static Path get(String first, String … more) : 用于将多个字符串串连成路径

  • static Path get(URI uri): 返回指定uri对应的Path路径

常用方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
- String toString() : 返回调用 Path 对象的字符串表示形式
- boolean startsWith(String path) : 判断是否以 path 路径开始
- boolean endsWith(String path) : 判断是否以 path 路径结束
- boolean isAbsolute() : 判断是否是绝对路径
- Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
- Path getRoot() :返回调用 Path 对象的根路径
- Path getFileName() : 返回与调用 Path 对象关联的文件名
- int getNameCount() : 返回Path 根目录后面元素的数量
- Path getName(int idx) : 返回指定索引位置 idx 的路径名称
- Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
- Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
- File toFile(): 将Path转化为File类的对象

Files工具类—jdk7提供

作用

操作文件或文件目录的工具类。

常用方法

image-20210224175537046

image-20210224175549932