# IO

# 一、File

File类被定义为文件和目录路径名的抽象表示形式,这是因为File类既可以表示文件也可以表示目录,他们都通过对应的路径来描述。

通过构造函数创建一个File类对象,则该对象就是指定文件的引用,可以通过该对象对文件操作。

//创建一个File类对象f,可通过f对temp.txt进行操作
File f = new File("D:\\temp.txt"); 
1
2

路径分隔符:

  • Windows下路径分隔符使用反斜线\,而Java程序中的反斜线表示转义字符,如果需要在Windows路径下使用反斜线,则应使用两条反斜线,如:C:\\mytest.txt
  • 使用斜线/,Java程序支持将斜线当成平台无关的路径分隔符。

File类常见方法:

  • boolean isHidden():判断是否是隐藏文件
  • boolean canRead():是否可读
  • boolean canWrite():是否可写
  • String getAbsolutePath():获取文件的绝对路径
  • String getName():获取文件名
  • boolean isDirectory():是否是目录
  • boolean isFile():是否是文件
  • boolean mkdir():创建目录
  • long lastModified():最后修改时间
  • long length():文件长度
  • File[] listFiles():返回目录下的所有文件和目录
public class MyTest1 {
    /*
     * I---input
     * O---output
     * IO--输入输出(键盘 显示器 硬盘 网络)
     * 	|---File类---文件和目录(文件夹)的抽象表示形式---File类将文件当成一个整体,不能对文件的内容进行操作,如果对文件的内容操作,需要使用IO流
     * 				通过File类的对象和文件或目录建立关联,操作File类的对象,就是操作相应的文件或者目录
     *
     * 	|---IO流
     * */
    public static void main(String[] args) {
        //创建File类的对象  java.io....
        /*
         * 文件的路径有两种写法
         * C:\\Users\\Raymond\\Desktop\\test\\a.txt
         * C:/Users/Raymond/Desktop/test/a.txt
         *
         * \ 转义字符----和其后的第一个字符结合,表示特殊的含义    \n \t
         * 如果要表示\,必须写两条\
         *
         * */
        //File f = new File("C:\\Users\\Maxwell\\Desktop\\a.txt");
        File f = new File("C:/Users/Maxwell/Desktop/a.txt");

        //File类中常见的方法
        //判断文件是否是隐藏文件
        System.out.println("是否隐藏:" + f.isHidden());
        //大小  Byte
        System.out.println("文件的大小:" + f.length());
        //文件名
        System.out.println("文件名:" + f.getName());
        //绝对路径
        System.out.println("绝对路径:" + f.getAbsolutePath());
        //是否可读
        System.out.println("是否可读:" + f.canRead());
        //是否可写
        System.out.println("是否可写:" + f.canWrite());
        /*
         * 时间戳---->表示时间的字符串(xxxx年xx月xx日 xx:xx:xx)
         *
         * SimpleDateFormat
         * 		|
         * 	  Date()
         * 		|
         * 	       时间戳
         * 时间戳---->Date()
         * */
        //文件最后修改时间
        long lastMdate = f.lastModified();
        Date date = new Date(lastMdate);
        //Date---String
        SimpleDateFormat sft = new SimpleDateFormat("yyyy年MM月dd日  HH:mm:ss");
        String dateStr =  sft.format(date);
        System.out.println("最后修改时间:" + dateStr);

        //判断是否是文件夹
        System.out.println("是否是文件夹:" + f.isDirectory());

        File f1 = new File("C:/Users/Maxwell/Desktop/test123/");
        //判断文件或文件夹是否存在
        //System.out.println("是否存在:" + f1.exists());
        if(!f1.exists()) { //不存在创建该文件夹
            //创建文件夹  Linux mkdir
            f1.mkdir();
        }

        File[] fArr = f1.listFiles();
        for (File file : fArr) {
            System.out.println(file.getName());
        }
    }
}
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

File类中没有提供对文件的读写操作,如果要实现对文件的读写,需要通过IO流来进行操作。

案例:列出一个目录下所有的文件和目录(包括子目录中的目录和文件)

public class MyTest2 {
    /*
     * f表示需要被遍历的文件夹
     * */
    public static void listFiles(File f) {
        File[] farr = f.listFiles();
        for(File subf : farr) {
            System.out.println(subf.getAbsolutePath());
            if(subf.isDirectory()) {
                listFiles(subf);
            }
        }
    }

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

# 二、IO流概述

# 2.1、什么是IO

I/OInput/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。

IO流是一组有序的,有起点和终点的数据集合,是对数据传输的总称和抽象。

IO作用

  • 人机交互
  • 文件数据读取写入,数据持久化保存

IO流的源和目的地:

  • 内存
  • 控制台
  • 磁盘文件
  • 网络端点

关于InputOutput

  • Input读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中;
  • Output将程序(内存)数据输出到磁盘、光盘等存储设备中。

# 2.2、IO流分类

按照处理的数据单元不同

  • 字节流:操作的数据单元是8位字节InputStreamOutputStream。二进制文件(声音、图片、视频)、文本文件;
  • 字符流:操作的数据单元是16位字符ReaderWriter,通常用于处理文本文件。

按照数据流流向不同

  • 输入流:只能从中读取数据,而不能向其写入数据。InputStreamReader
  • 输出流:只能向其写入数据,而不能从中读取数据。OutputStreamWriter
  • 输入、输出都是从内存的角度进行划分,内存-->硬盘,输出流;硬盘-->内存,输入流。
字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

Java的IO流共涉及40多个类,实际上非常规则,都是从上面4个抽象基类派生的。

由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

# 2.3、IO流处理流程

  • 打开流
  • 在流中数据传输(输入/输出 读/写)
  • 关闭流

程序中打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。

# 三、字节流

操作的数据单元是8位字节,主要涉及两个抽象类:InputStreamOutputStream

# 3.1、InputStream

主要方法:

  • int read():从输入流中读取数据的下一个字节。返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1;
  • int read(byte[] b):从此输入流中将最多b.length个字节的数据读入一个byte数组中。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数;
  • int read(byte[] b, int off,int len):将输入流中最多len个数据字节读入byte数组。尝试读取len个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;
  • public void close():关闭此输入流并释放与该流关联的所有系统资源。
import java.io.FileInputStream;
import java.io.IOException;

public class MyTest3 {
    /*
     * IO流
     *
     * 操作步骤:
     * 	1.打开流
     *  2.读/写
     *  3.关闭流
     * 分类------->能够解决什么情况下选择什么样的IO流的问题
     * 	|---处理数据单元的不同
     * 			|---字节流(8) InputStream OutputStream
     * 			|---字符流 (16) Reader Writer
     * 	|---流向---方向   物理   参考系---内存
     * 		 |---输入流  InputStream Reader
     * 		 |---输出流  OutputStream Writer
     * 	1.InputStream OutputStream Reader Writer都是抽象类,我们学习的是他们的子类;
     * 	2.任何一个IO流同时属于上面两个分类体系;
     * 	3.处理的内容是二进制文件(声音,图片,视频)使用字节流进行处理
     * 	4.处理的内容是文本文件(txt)使用字符流进行处理
     * */
    public static void main(String[] args) throws IOException {
        /*
         * 使用字节流读取文件并在控制台打印--InputStream--FileInputStream
         * */
        //打开流
        FileInputStream in = new FileInputStream("C:\\Users\\Maxwell\\Desktop\\a.txt");
        /*
         * 读取文件中的内容
         * 1.为了提升读取效率,要一次尽可能读取多个字节的数据----读到数组里面
         * 2.文件有可能特别大,数组如果设置特别大会导致程序崩溃----数组的大小不能特别大,不能和被读取的文件一样大,数组要小一点----多次调用读取的方法进行读取----循环读取
         * 3.read方法返回-1表示读到文件末尾
         *
         * read(byte[] arr)
         * read(byte[] arr, int off, int len); ***
         */
        byte[] arr = new byte[1024];//1KB
        int len = 0;
        while((len = in.read(arr, 0, 1024)) != -1) {
            //内容在数组中----String
            //byte[]----String
            /*
             * 根据数组中的有效信息转换成字符串
             * */
            String str = new String(arr, 0, len);
            //打印转换的内容
            System.out.print(str);
        }

        //关闭流
        in.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
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

# 3.2、OutputStream

主要方法:

  • void write(int b):将指定的字节写入此输出流。write的常规协定是:向输出流写入一个字节。要写入的字节是参数b的八个低位。b 的24个高位将被忽略,即写入0~255范围的;
  • void write(byte[] b):将b.length个字节从指定的byte数组写入此输出流。write(b)的常规协定是:应该与调用write(b, 0, b.length)的效果完全相同;
  • void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流;
  • void flush():刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标;
  • void close():关闭此输出流并释放与该流关联的所有系统资源。
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class MyTest4 {
    public static void main(String[] args) throws IOException {
        /*
         * 从控制台获取输入,将输入的内容写入到文件当中(使用字节流)
         * OutputStream
         * */
        Scanner sc = new Scanner(System.in);
        /*
         * 日志文件
         *
         * */
        //打开流
        FileOutputStream out = new FileOutputStream("C:\\Users\\Maxwell\\Desktop\\a.txt");
        //读取键盘数据
        String str = sc.next();
        str += "\r\n";
        //String--->byte[]
        byte[] arr = str.getBytes();
        //写入数据
        out.write(arr);
        //关闭流
        out.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

在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖。

如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。

# 3.3、字节流实现文件拷贝

public class MyTest5 {
    public static void main(String[] args) throws IOException {
        //拷贝操作---复制---读取一个文件的内容,写入另外的一个文件
        //打开流
        FileInputStream in = new FileInputStream("C:\\Users\\Maxwell\\Desktop\\pic.jpg");
        FileOutputStream out = new FileOutputStream("C:\\Users\\Maxwell\\Desktop\\pic1.jpg");

        byte[] arr = new byte[1024];
        int len = 0;
        //读写操作
        while((len = in.read(arr, 0, 1024)) != -1) {
            //读取多少内容,就写入多少内容
            out.write(arr, 0, len);
        }

        //关闭
        out.close();
        in.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 四、字符流

操作的数据单元是16位字符,主要涉及两个抽象类:ReaderWriter,通常用于处理文本文件。

# 4.1、Reader

主要方法:

  • int read():读取单个字符。作为整数读取的字符,范围在0到65535之间 (2个字节的Unicode码),如果已到达流的末尾,则返回 -1;
  • int read(char[] cbuf):将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数;
  • int read(char[] cbuf,int off,int len):将字符读入数组的某一部分,存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回 -1,否则返回本次读取的字符数;
  • close():关闭此输入流并释放与该流关联的所有系统资源。
import java.io.FileReader;
import java.io.IOException;

public class MyTest6 {
    public static void main(String[] args) throws IOException {
        /*
         * 字符流(16)---Reader Writer
         * */
        //打开流
        FileReader in = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");

        char[] arr = new char[1024];
        int len = 0;
        //读取
        while((len = in.read(arr, 0, 1024)) != -1) {
            //打印String
            //char[]---->String
            String str = new String(arr, 0, len);
            System.out.println(str);
        }
        //关闭
        in.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

# 4.2、Writer

主要方法:

  • void write(int c):写入单个字符。要写入的字符包含在给定整数值的16个低位中,16高位被忽略。 即写入0到65535之间的Unicode码;
  • void write(char[] cbuf):写入字符数组;
  • void write(char[] cbuf,int off,int len):写入字符数组的某一部分。从off开始,写入len个字符;
  • void write(String str):写入字符串;
  • void write(String str,int off,int len):写入字符串的某一部分;
  • void flush():刷新该流的缓冲,则立即将它们写入预期目标;
  • void close():关闭此输出流并释放与该流关联的所有系统资源。
import java.io.FileWriter;
import java.io.IOException;

public class MyTest7 {
    public static void main(String[] args) throws IOException {
        //在文件中写入数据---Writer
        FileWriter out = new FileWriter("C:\\Users\\Maxwell\\Desktop\\a.txt");
        //写入
        String str = "abcdefg....";
        out.write(str);
        //关闭
        out.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在写入一个文件时,如果使用构造器FileWriter(file),则目录下有同名文件将被覆盖。

如果使用构造器FileWriter(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。

# 4.3、字符流实现文件拷贝

public class MyTest8 {
    public static void main(String[] args) throws IOException {
        //使用字符流进行文件的拷贝
        //打开流
        FileReader in = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");
        FileWriter out = new FileWriter("C:\\Users\\Maxwell\\Desktop\\b.txt");

        char[] arr = new char[1024];
        int len = 0;
        //读写
        while((len = in.read(arr, 0, 1024)) != -1) {
            out.write(arr, 0, len);
        }
        //关闭
        out.close();
        in.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 五、缓冲流

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,默认使用8192个字节或字符的缓冲区。缓冲流要套接在相应的节点流之上。

根据数据操作单位可以把缓冲流分为:

  • BufferedInputStreamBufferedOutputStream
  • BufferedReaderBufferedWriter

当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流。

关闭流的顺序和打开流的顺序相反只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流。

如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。

# 5.1、使用缓冲流实现文件拷贝

import java.io.*;

public class MyTest9 {
    public static void main(String[] args) throws IOException {
        BufferedReader rd = new BufferedReader(new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt"));
        BufferedWriter wr = new BufferedWriter(new FileWriter("C:\\Users\\Maxwell\\Desktop\\b.txt"));

        char[] temp = new char[1024];
        int len = 0;
        while ((len = rd.read(temp)) != -1) {
            wr.write(temp, 0, len);
        }

        wr.close();
        rd.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.2、按行读取

public class MyTest10 {
    public static void main(String[] args) throws IOException {
        //字符过滤流---缓冲区---BufferdReader
        //打开流
        FileReader rd = new FileReader("C:\\Users\\Maxwell\\Desktop\\a.txt");
        /*
         * Reader in = rd;向上转型 多态
         * BufferedReader in = new BufferedReader(in);
         * */
        BufferedReader in = new BufferedReader(rd);
        //读---控制台
        String line = null;
        while((line = in.readLine()) != null) {
            System.out.println(line);
            System.out.println("-----------------------------------------");
        }
        //关闭
        in.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上次更新: 2024/4/13