还没有接触 java io 的文件操作可以先看这篇文章

一、输入及输出的概念

1.1 什么是 I/O

  • 是指程序与外部设备或其他计算机进行交互的操作。
  • 几乎所有的程序都具有输入与输出操作。
    • 如从键盘上读取数据,从本地或网络_上的文件读取数据或写入数据等。
    • 通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
  • Java把这些输入与输出操作用流来实现,通过统一的接口来表示,从而使程序设计更为简单。

1.2 输入输出(I/0)

  • 入还是出事相对于内存
  • 把数据读到内存中,称为输入,即 input,进行数据的 read 操作
  • 往内存外部设备写数据,称为输出,即 output,进行数据的 write 操作

二、File类

2.1 File 类的相关概念

  • File类是java. io包中很重要的一个类;
  • File类的对象可以表示文件,还可以表示目录,在程序中一个File类对象可以代表一个文件或目录;
  • File对象可以对文件或目录的属性进行操作,如:文件名、最后修改日期、文件大小等;
  • File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作。

2.2 File 类的构造方法

File 类的构造方法有四种重载方式,不过通常使用如下方式

  1. File(String pathname) //指定文件(或目录)名和路径创建文件对象
  1. File file = new File("1.txt"); // 在当前目录创建文件对象
  2. File file = new File("Java"); // 在当前目录创建一个目录对象
  3. File f3 = new File("D:\\Java");//指明详细的路径以及目录名,请注意双斜线

2.3 File 类的常用方法

image.png

2.4 File 类基本操作使用

  1. import java.io.File;
  2. import java.io.IOException;
  3. /**
  4. * file 可以封装目录。也可以封装文件
  5. * */
  6. public class TesyFile {
  7. public static void main(String[] args) throws IOException {
  8. // 在当前目录创建目录对象
  9. File dir = new File("test");
  10. System.out.println(dir.getAbsolutePath());
  11. // 创建目录,判断是否存在
  12. if (!dir.exists()) {
  13. System.out.println("目录不存在,正在创建中。。。");
  14. dir.mkdir();
  15. } else {
  16. System.out.println("目录已经存在");
  17. }
  18. // 创建文件对象
  19. File file = new File("test/1.txt");
  20. // 创建文件对象
  21. if (!file.exists()) {
  22. System.out.println("文件不存在,正在创建。。。");
  23. file.createNewFile();
  24. } else {
  25. System.out.println("文件已创建");
  26. }
  27. // 删除文件
  28. file.delete();
  29. System.out.println("=========== 遍历所有名字 list() ===================");
  30. // 文件遍历
  31. File dirs = new File("G://");
  32. String[] fileNames = dirs.list();
  33. for (String name:fileNames) {
  34. System.out.println(name);
  35. }
  36. System.out.println("=========== 遍历所有对象 listFiles() ===================");
  37. // 文件遍历 (使用 listFiles 的前提是 目录必须存在)
  38. File[] files = dirs.listFiles();
  39. for (File f:files) {
  40. System.out.println(f.getAbsolutePath());
  41. }
  42. // 文件过滤
  43. System.out.println("=========== 遍历符合条件的名字 list(FilenameFilter) 接口===================");
  44. // 遍历目录下所有文件名字, 打印符合过滤条件的
  45. String[] fileNames2 = dir.list(new MyFileNamesFilter());
  46. for (String names2:fileNames2) {
  47. System.out.println(names2);
  48. }
  49. System.out.println("=========== 遍历符合条件的名字 list(FileFilter) 接口===================");
  50. File[] files2 = dir.listFiles(new MyFileFilter());
  51. for (File f:files2) {
  52. System.out.println(f.getAbsolutePath()); // 打印目录
  53. }
  54. }
  55. }

使用文件过滤

补充:

1. filenameFilter 接口
  1. import java.io.File;
  2. import java.io.FilenameFilter;
  3. public class MyFileNamesFilter implements FilenameFilter {
  4. @Override
  5. public boolean accept(File dir, String name) {
  6. if (name.endsWith(".java")) { // 返回文件名后缀为 .java 的
  7. return true;
  8. } else
  9. return false;
  10. }
  11. }

2. FileFilter 接口
import java.io.File;
import java.io.FileFilter;

public class MyFileFilter  implements FileFilter {

    @Override
    public boolean accept(File pathname) {
        if (pathname.isDirectory()) { // 只输出目录
            return true;
        } else
            return false;
    }
}

image.png

三、输入流与输出流

输入流和输出流可以操作文件的内容

3.1 基本概念

  • 流按照数据的传输方向分为:
    • 输入流:往内存中读叫输入流
    • 输出流:从内存中往外些叫输出流
  • 所有输入流都是 InputStream 类或者 Reader 类的子类
    • 类名以 inputStream 结尾的类都是 InputStream 的子类
    • 类名以 Reader 结尾的类都是 Reader类的子类
  • 所有输出流都是 OutputStream 类 或者 Writer类的子类
    • 类名以 OutputStrean结尾的类都是 OutputStream的子类
    • 类名以 Writer结尾的类都是 Writer 类的子类

四、字节流和字符流

4.1 特点

  • 从数据流的编码格式上划分
    • 字节流
    • 字符流
  • InputStream 和 OutputStream的子类都是字节流
    • 所以字节流可以读写二进制文件,主要处理音频、图片、歌曲等,处理单元为1个字节
  • Reader 和 Writer 的子类都是字符流
    • 主要处理字符或字符串,字符流处理单元为 2个字节
    • 字节流将读取到的字节数据,去指定的编码表中获取对应的文字

4.2 io流中分类

  • 字节流中常用类
    • 字节输入流:FileInputStream
    • 字节输出流:FileOutputStream
  • 字符流中常用类
    • 字符输入流 FileReader
    • 字符输出流 FileWriter

五、节点流与处理流

5.1 特点

  • 根据封装类型不同流又分为
    • 节点流
    • 处理流

5.2 节点流

如果封装流的是某种特定的数据源,如文件、字符串、字符串数组等,则称为节点流

5.3 处理流

如果封装流的是其它流对象,成为处理流。 处理流提供缓冲功能,提高读写效率

5.4 示例 (读取一个文件) FileReader

  1. 首先创建一个文件名为 1.txt,然后我们在里面随便加点内容,之后保存
  2. 然后编写读文件的代码
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class TestFileReader {
    public static void main(String[] args) {
        // 把外部设备的文件 1.txt 包装成 file 对象
        File file = new File( "test/1.txt");
        // 由于要做读的操作,并且对文件读,所以使用字符输入流,XXXReader
        // FileReader 可以直接封装 File 对象,所以使用 FileReader
        FileReader fr = null;
        try {
          fr = new FileReader(file) ;
          // 调用 read 方法,读一个字符
          int i = fr.read();
          while (i != -1) {
              System.out.print((char)i);
              i=fr.read();
          }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null) {
                try {
                    // 关闭
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

可以看到,文件的内容正常读取出来了,但是这样做效率很低,因为我们是一个字符一个字符读取的。
image.png

5.5 节点流和处理流中常用的类

5.5.1 节点流中常用类

  • 字节输入流FileInputStream
  • 字节输出流FileOutputStream
  • 字符输入流FileReader
  • 字符输出流FileWriter .

    5.5.2 处理流中常用类

  • 缓冲字节输出流BufferedOutputStream

  • 缓冲字节输入流BufferedInputStream
  • 缓冲字符输入流BufferedReader
  • 缓冲字符输出流BufferedWriter

注意: 在使用字符缓冲输出流时,一定先flush(),然后再close()。避免数据的丟失。

5.5.3 使用 bufferReader 提高读取效率

import java.io.*;

public class TestFileReader {
    public static void main(String[] args) {
        // 把外部设备的文件 1.txt 包装成 file 对象
        File file = new File("test/1.txt");
        // 由于要做读的操作,并且对文件读,所以使用字符输入流,XXXReader
        // FileReader 可以直接封装 File 对象,所以使用 FileReader
        FileReader fr = null;
        BufferedReader br = null;
        try {
            fr = new FileReader(file);
            // 使用缓冲流,可以一行一行的读取内容
            br = new BufferedReader(fr);
            // 一步到位 br = new bufferedReader(new FileReader(file));
//          int i = fr.read();
//          while (i != -1) {
//              System.out.print((char)i);
//              i=fr.read();
//          }
            String line = br.readLine();
            while (line != null) {
                System.out.println(line);
                line = br.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }  finally {
            if (fr != null) {
                try {
                    // 关闭
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5.5.4 使用 BufferedWriter 写数据

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

public class TestBufferWriter {
    public static void main(String[] args) {
        BufferedWriter bw = null;
        File file = new File("test/1.txt");

        try {
            bw = new BufferedWriter(new FileWriter(file,true)); // true 表示不覆盖
            bw.write("\n" + new Date() + " : 插入一条新的数据\n");
            bw.flush(); // 刷新,保证缓冲区的内容写进去
            bw.close();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

然后再回到上面的读的代码,运行一下,可以看到数据正常的写进去了
image.png

5.5.5 使用 FileOutputStream 写入数据

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

// 文件的写操作
public class TestFileOutputStream {
    public static void main(String[] args) throws IOException {
        File file = new File("test/1.txt");
        if (!file.exists()) {
            file.createNewFile();
        } else {
            System.out.println("文件已存在");
        }

        FileOutputStream fos = new FileOutputStream(file); // 覆盖
        FileOutputStream fos1 = new FileOutputStream(file, true); // 追加内容
        String str = "窗前明月光\n疑是地上霜\n举头望明月\n低头思故乡";
        if (file.exists()) {
            fos1.write(str.getBytes()); // 将字符串转换为字节数组写入
        } else {
            fos.write(str.getBytes());
        }
        System.out.println("写入成功");
    }
}

image.png

5.5.6 使用 FileInputStream 读取文件

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

// 使用 FileInputStream 读取文件
public class TestFileInputStream {
    public static void main(String[] args) throws IOException {
        File file = new File("test/1.txt");

        // 文件的字节输入流
        FileInputStream fls = new FileInputStream(file);
        byte[] bs = new byte[(int) file.length()];
        // 一次性读取文件内容
        fls.read(bs);
        String str = new String(bs);
        System.out.println(str);
    }
}

image.png

5.5.7 使用 BufferedOutputStream写入文件

public static void main(String[] args) throws Exception {    
   BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;

   //写数据
   bos.write("hello".getBytes());
   //释放资源
   bos.close();
}

5.5.8 使用 BufferedInputStream读取文件

public static void main(String[] args) {
    BufferedInputStream bufferedInput = null;
    byte[] buffer = new byte[1024];

    try {
            //创建BufferedInputStream 对象
            bufferedInput = new BufferedInputStream(new FileInputStream(filename));

            int bytesRead = 0;

            //从文件中按字节读取内容,到文件尾部时read方法将返回-1
            while ((bytesRead = bufferedInput.read(buffer)) != -1) {

                //将读取的字节转为字符串对象
                String chunk = new String(buffer, 0, bytesRead);
                System.out.print(chunk);
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            //关闭 BufferedInputStream
            try {
                if (bufferedInput != null)
                    bufferedInput.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
}

六、字符流与字节流的转换

6.1 转换的概念及应用

  • 转换流的由来
    • 字符流和字节流之间的桥梁
    • 方便了字符流与字节流之间的操作
  • 转换流的应用
    • 字节流中的数据都是字符时,转成字符流操作更搞笑

6.2 字节流转换成字符流的桥梁

  • InputStreamReader

    InputStreamReader (InputStream in) InputStreamReader (InputStream in, String charsetName)

  • OutpuStreamWriter

    OutputStreamWriter (OutputStream out) OutputStreamWriter (OutputStream out,String charsetName)

注意:
它读入字节,并根据指定的编码方式,将之转换为字符流。
使用的编码方式可能由名称指定,或平台可接受的缺省编码方式。

6.3 转换图解

image.png

6.4 demo1

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class TestInputStreamReader {
    public static void main(String[] args) {
        // 使用标砖输入流转换为 InputStreamReader
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            String line = br.readLine();
            while (!line.equals("exit")) {
                System.out.println(">>>:" + line);
                line = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

image.png

6.5 demo2 (FileReader、FileWriter 实现)

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

public class HomeWork {
    /**
     * 使用 FileReader 拷贝文件
     * */
    public static void main(String[] args) throws IOException {
        File file1 = new File("a.txt");
        File file2 = new File("copy"); //拷贝的路径
        FileReader fr = null;
        FileWriter fw = null;
        if (!file1.exists()) {
            file1.createNewFile();
        } else {
            System.out.println(file1.getName()+" 存在啦");
        }

        if (!file2.exists()) {
            file2.mkdir();
        } else {
            System.out.println("目录已经创建了");
        }

        // 创建
        File file3 = new File("copy/a_copied.txt");
        if (!file3.exists()) {
            file3.createNewFile();
        } else {
            System.out.println("文件已经存在");
        }

        // 先给 file1 写入一些数据
        fw = new FileWriter(file1);
        fw.flush();
        fw.write("春眠不觉晓\n处处闻啼鸟\n夜来风雨声\n花落知多少\n"+new Date());
        fw.close();

        // 再读取 file1 中的数据
        fr = new FileReader(file1);
        fw = new FileWriter(file3);

        // file1
        fw.flush();
        int i = fr.read();
        while (i != -1) {
            System.out.print((char)i);
            fw.write((char)i);
            i=fr.read();
        }

        fr.close();
        fw.close();
    }
}

image.png

七、Scanner 类

7.1 Scanner 作为扫描类

  • Scanner 类位于 java.util 包中,不在 java.io 包中,不属于 IO 流
  • Scanner 是一个工具类,主要目标是简化文本的扫描,最长用来获取控制台的输入
  • Scanner 获取控制台输入步骤
    1. 使用控制台输入创建 Scanner 对象

    Scanner in = new Scanner(System.in);

    1. 调用 Scanner 中 nextXXX 方法,获取需要的数据类型

      next, nextInt, nextDouble ….

因此使用 Scanner 比使用 Reader 简单很多很多

7.2 Scanner 示例

import java.util.Scanner;

public class TestScanner {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入字符串:");
        while (true) {
            String line = s.nextLine();
            if (line.equals("exit")) break;
            System.out.println(">>> "+line);
        }
    }
}


image.png

八、IO 编程总结

8.1 IO 流一览表:

  • JavaSE 所提供的的所有流都位于 java.io 包内
  • 分别继承一下四种抽象流类型
    • 字节输入流

image.png

  • 字节输出流

image.png

  • 字符输入流:

image.png

  • 字符输出流

image.png

8.2 流操作的基本规律

image.png

8.3 文件拷贝练习

import java.io.*;

public class TestCopyFile {
    public static void main(String[] args) throws IOException {
        File file1 = new File("test/1.txt");
        File file2 = new File("test/1_copied.txt");

        // 提高效率
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            br = new BufferedReader(new FileReader(file1));
            bw = new BufferedWriter(new FileWriter(file2));

            // 读出源文件
            String line = br.readLine();
            while (line != null) {
                // 存数据
                bw.write(line+"\n");
                System.out.println(line);
                line = br.readLine();
            }
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            br.close();
        }
    }
}