一文完美诠释JavaIO流,小白都能读懂的输入和输出流操作流程总结

Posted Java架构师-大仙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文完美诠释JavaIO流,小白都能读懂的输入和输出流操作流程总结相关的知识,希望对你有一定的参考价值。

IO包括输入和输出流,输入流指的是将数据以字符或字节形式从外部媒介比如文件、数据库等读取到内存中>所以也分为字符输入流和字节输入流。

输出流指的是将内存中的数据写入外部媒介,也分为了字符输出流和字节输出流。

一、File类的使用

1. File类的理解

  • File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)。
  • File类声明在 java.io 包下:文件和文件路径的抽象表示形式,与平台无关。
  • File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
  • 想要在 Java 程序中表示一个真实存在的文件或目录,那么必须有一个 File 对象,但是 Java程序中的一个 File 对象,可能没有一个真实存在的文件或目录。
  • 后续 File 类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点"。

2. File的实例化

2.1 常用构造器

  • File(String filePath)
  • File(String parentPath,String childPath)
  • File(File parentFile,String childPath)

代码示例

@Test
public void test1() {
    //构造器1
    File file1 = new File("hello.txt");
    File file2 = new File("E:\\\\workspace_idea\\\\JavaSenic\\\\IO\\\\hello.txt");
    System.out.println(file1);
    System.out.println(file2);

    //构造器2
    File file3 = new File("E:\\\\workspace_idea\\\\JavaSenior", "hello.txt");
    System.out.println(file3);

    //构造器3
    File file4 = new File(file3, "hi.txt");
    System.out.println(file4);
}

2.2 路径分类

  • 相对路径:相较于某个路径下,指明的路径。
  • 绝对路径:包含盘符在内的文件或文件目录的路径。

说明

  • IDEA中:
    • 如果使用JUnit中的单元测试方法测试,相对路径即为当前Module下。
    • 如果使用main()测试,相对路径即为当前的Project下。
  • Eclipse中:
    • 不管使用单元测试方法还是使用main()测试,相对路径都是当前的Project下。

2.3 路径分隔符

  • windows和DOS系统默认使用 \\ 来表示

  • UNIX和URL使用 / 来表示

  • Java程序支持跨平台运行,因此路径分隔符要慎用。

  • 为了解决这个隐患,File类提供了一个常量: public static final String separator。根据操作系统,动态的提供分隔符。

    举例:

    //windows和DOS系统
    File file1 = new File("E:\\\\io\\\\test.txt");
    //UNIX和URL
    File file = new File("E:/io/test.txt");
    //java提供的常量
    File file = new File("E:"+File.separator+"io"+File.separator+"test.txt");
    
    

3. File类的常用方法

3.1 File类的获取功能

  • 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数组

代码示例:

@Test
public void test2(){
    File file1 = new File("hello.txt");
    File file2 = new File("d:\\\\io\\\\hi.txt");

    System.out.println(file1.getAbsolutePath());
    System.out.println(file1.getPath());
    System.out.println(file1.getName());
    System.out.println(file1.getParent());
    System.out.println(file1.length());
    System.out.println(new Date(file1.lastModified()));

    System.out.println();

    System.out.println(file2.getAbsolutePath());
    System.out.println(file2.getPath());
    System.out.println(file2.getName());
    System.out.println(file2.getParent());
    System.out.println(file2.length());
    System.out.println(file2.lastModified());
}
@Test
public void test3(){
    File file = new File("D:\\\\workspace_idea1\\\\JavaSenior");

    String[] list = file.list();
    for(String s : list){
        System.out.println(s);
    }

    System.out.println();

    File[] files = file.listFiles();
    for(File f : files){
        System.out.println(f);
    }

}

3.2 File类的重命名功能

  • public boolean renameTo(File dest):把文件重命名为指定的文件路径
  • 注意:file1.renameTo(file2)为例:要想保证返回 true ,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。

代码示例:

@Test
public void test4(){
    File file1 = new File("hello.txt");
    File file2 = new File("D:\\\\io\\\\hi.txt");

    boolean renameTo = file2.renameTo(file1);
    System.out.println(renameTo);

}

3.3 File类的判断功能

  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile() :判断是否是文件
  • public boolean exists() :判断是否存在
  • public boolean canRead() :判断是否可读
  • public boolean canWrite() :判断是否可写
  • public boolean isHidden() :判断是否隐藏

代码示例:

@Test
public void test5(){
    File file1 = new File("hello.txt");
    file1 = new File("hello1.txt");

    System.out.println(file1.isDirectory());
    System.out.println(file1.isFile());
    System.out.println(file1.exists());
    System.out.println(file1.canRead());
    System.out.println(file1.canWrite());
    System.out.println(file1.isHidden());

    System.out.println();

    File file2 = new File("d:\\\\io");
    file2 = new File("d:\\\\io1");
    System.out.println(file2.isDirectory());
    System.out.println(file2.isFile());
    System.out.println(file2.exists());
    System.out.println(file2.canRead());
    System.out.println(file2.canWrite());
    System.out.println(file2.isHidden());

}

3.4 Flie类的创建功能

  • 创建硬盘中对应的文件或文件目录
  • public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
  • public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
  • public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建

代码示例:

@Test
public void test6() throws IOException {
    File file1 = new File("hi.txt");
    if(!file1.exists()){
        //文件的创建
        file1.createNewFile();
        System.out.println("创建成功");
    }else{//文件存在
        file1.delete();
        System.out.println("删除成功");
    }

}
@Test
public void test7(){
    //文件目录的创建
    File file1 = new File("d:\\\\io\\\\io1\\\\io3");

    boolean mkdir = file1.mkdir();
    if(mkdir){
        System.out.println("创建成功1");
    }

    File file2 = new File("d:\\\\io\\\\io1\\\\io4");

    boolean mkdir1 = file2.mkdirs();
    if(mkdir1){
        System.out.println("创建成功2");
    }
    //要想删除成功,io4文件目录下不能有子目录或文件
    File file3 = new File("D:\\\\io\\\\io1\\\\io4");
    file3 = new File("D:\\\\io\\\\io1");
    System.out.println(file3.delete());
}

3.5 File类的删除功能

  • 删除磁盘中的文件或文件目录
  • public boolean delete():删除文件或者文件夹
  • 删除注意事项:Java中的删除不走回收站。

4. 内存解析

5. 小练习

利用 File 构造器,new 一个文件目录file

1)在其中创建多个文件和目录

2)编写方法,实现删除fle中指定文件的操作

@Test
public void test1() throws IOException {
    File file = new File("E:\\\\io\\\\io1\\\\hello.txt");
    //创建一个与file同目录下的另外一个文件,文件名为:haha.txt
    File destFile = new File(file.getParent(),"haha.txt");
    boolean newFile = destFile.createNewFile();
    if(newFile){
        System.out.println("创建成功!");
    }
}

判断指定目录下是否有后缀名为 .jpg 的文件,如果有,就输出该文件名称

public class FindJPGFileTest {

    @Test
    public void test1(){
        File srcFile = new File("d:\\\\code");

        String[] fileNames = srcFile.list();
        for(String fileName : fileNames){
            if(fileName.endsWith(".jpg")){
                System.out.println(fileName);
            }
        }
    }
    @Test
    public void test2(){
        File srcFile = new File("d:\\\\code");

        File[] listFiles = srcFile.listFiles();
        for(File file : listFiles){
            if(file.getName().endsWith(".jpg")){
                System.out.println(file.getAbsolutePath());
            }
        }
    }
    /*
	 * File类提供了两个文件过滤器方法
	 * public String[] list(FilenameFilter filter)
	 * public File[] listFiles(FileFilter filter)

	 */
    @Test
    public void test3(){
        File srcFile = new File("d:\\\\code");

        File[] subFiles = srcFile.listFiles(new FilenameFilter() {

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

        for(File file : subFiles){
            System.out.println(file.getAbsolutePath());
        }
    }

}

遍历指定目录所有文件名称,包括子文件目录中的文件。

拓展1:并计算指定目录占用空间的大小

拓展2:删除指定文件目录及其下的所有文件

public class ListFileTest {

    public static void main(String[] args) {
        // 递归:文件目录
        /** 打印出指定目录所有文件名称,包括子文件目录中的文件 */

        //1.创建目录对象
        File file = new File("E:\\\\test");

        //2.打印子目录
        printSubFile(file);

    }

    /**
     * 递归方法遍历所有目录下的文件
     *
     * @param dir
     */
    public static void printSubFile(File dir) {
        //打印子目录
        File[] files = dir.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {//如果为文件目录,则递归调用自身
                printSubFile(f);
            } else {
                System.out.println(f.getAbsolutePath());//输出绝对路径
            }
        }
    }

    // 拓展1:求指定目录所在空间的大小
    // 求任意一个目录的总大小
    public long getDirectorySize(File file) {
        // file是文件,那么直接返回file.length()
        // file是目录,把它的下一级的所有大小加起来就是它的总大小
        long size = 0;
        if (file.isFile()) {
            size += file.length();
        } else {
            File[] allFiles = file.listFiles();// 获取file的下一级
            // 累加all[i]的大小
            for (File f : allFiles) {
                size += getDirectorySize(f);//f的大小
            }
        }
        return size;
    }

    /**
     * 拓展2:删除指定的目录
     */
    public void deleteDirectory(File file) {
        // 如果file是文件,直接delete
        // 如果file是目录,先把它的下一级干掉,然后删除自己
        if (file.isDirectory()) {
            File[] allFiles = file.listFiles();
            //递归调用删除file下一级
            for (File f : allFiles) {
                deleteDirectory(f);
            }
        } else {
            //删除文件
            file.delete();
        }
    }
}

二、IO流概述

1. 简述

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

  • Java程序中,对于数据的输入输出操作以 “流(stream)” 的方式进行。

  • Java.IO 包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

2. 流的分类

操作数据单位:字节流、字符流

  • 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
  • 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理

数据的流向:输入流、输出流

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

流的角色:节点流、处理流

节点流:直接从数据源或目的地读写数据。

处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

图示:

3. IO流的体系分类

3.1 总体分类

红框为抽象基类,蓝框为常用IO流

3.2 常用的几个IO流结构

抽象基类节点流(或文件流)缓冲流(处理流的一种)
InputStreamFileInputStream (read(byte[] buffer))BufferedInputStream (read(byte[] buffer))
OutputSteamFileOutputStream (write(byte[] buffer,0,len)BufferedOutputStream (write(byte[] buffer,0,len) / flush()
ReaderFileReader (read(char[] cbuf))BufferedReader (read(char[] cbuf) / readLine()
WriterFileWriter (write(char[] cbuf,0,len)BufferedWriter (write(char[] cbuf,0,len) / flush()

3.3 对抽象基类的说明:

抽象基类字节流字符流
输入流InputSteamReader
输出流OutputSteamWriter
  • 说明:Java的lO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。

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

3.3.1InputSteam & Reader

  • InputStream和Reader是所有输入流的基类。

  • InputStream(典型实现:FileInputStream)

    • int read()
    • int read(byte[] b)
    • int read(byte[] b,int off,int len)
  • Reader(典型实现:FileReader)

    • int read()
    • int read(char[] c)
    • int read(char[] c,int off,int len)
  • 程序中打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。

  • FileInputStream 从文件系统中的某个文件中获得输入字节。FileInputStream 用于读取非文本数据之类的原始字节流。要读取字符流,需要使用 FileReader。

InputSteam:

  • 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 throws IOException

    关闭此输入流并释放与该流关联的所有系统资源。

Reader:

  • int read()

    读取单个字符。作为整数读取的字符,范围在0到65535之间(0x00-0xffff)(2个字节的 Unicode码),如果已到达流的末尾,则返回-1。

  • int read(char[] cbuf)

    将字符读入数组。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。

  • int read(char[] cbuf,int off,int len)

    将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。

  • public void close throws IOException

    关闭此输入流并释放与该流关联的所有系统资源

3.3.2 OutputSteam & Writer

  • OutputStream 和 Writer也非常相似:
    • void write(int b/int c);
    • void write(byte[] b/char[] cbuf);
    • void write(byte[] b/char[] buff,int off,int len);
    • void flush();
    • void close();//需要先刷新,再关闭此流
  • 因为字符流直接以字符作为操作单位,所以 Writer可以用字符串来替换字符数组,即以 String对象作为参数
    • void write(String str);
    • void write(String str,int off,int len);
  • FileOutputStream 从文件系统中的某个文件中获得输出字节。FileOutputstream 用于写出非文本数据之类的原始字节流。要写出字符流,需要使用 FileWriter

OutputStream:

  • void write(int b)

    将指定的字节写入此输出流。 write的常规协定是:向输出流写入一个字节。要写入的字节是参数b的八个低位。b的24个高位将被忽略。即写入0~255范围的

  • void write(byte[] b)

    b.length 个字节从指定的byte数组写入此输出流。write(b)的常规协定是:应该与调用wite(b,0,b.length)的效果完全相同。

  • void write(byte[] b,int off,int len)

    将指定byte数组中从偏移量off开始的len个字节写入此输出流。

  • public void flush()throws IOException

    刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。

  • public void close throws IOException

    关闭此输岀流并释放与该流关联的所有系统资源。

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()

    刷新该流的缓冲,则立即将它们写入预期目标。

  • public void close throws IOException

    关闭此输出流并释放与该流关联的所有系统资源

4. 输入、输出标准化过程

4.1 输入过程:

① 创建 File 类的对象,指明读取的数据的来源。(要求此文件一定要存在)

② 创建相应的输入流,将 File 类的对象作为参数,传入流的构造器中

③ 具体的读入过程:创建相应的 byte[]char[]

④ 关闭流资源

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

4.2 输出过程:

① 创建 File 类的对象,指明写出的数据的位置。(不要求此文件一定要存在)

② 创建相应的输出流,将 File 类的对象作为参数,传入流的构造器中

③ 具体的写出过程:write(char[]/byte[] buffer,0,len)

④ 关闭流资源

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

三、节点流(文件流)

1. 文件字符流 FileReader 和 FileWriter 的使用

1.1文件的输入

从文件中读取到内存(程序)中

步骤:

  1. 建立一个流对象,将已存在的一个文件加载进流 FileReader fr = new FileReader(new File("Test. txt"));
  2. 创建一个临时存放数据的数组 char[] ch = new char[1024];
  3. 调用流对象的读取方法将流中的数据读入到数组中。 fr.read(ch);
  4. 关闭资源。 fr.close();

代码示例:

@Test
public void testFileReader1()  {
    FileReader fr = null;
    try {
        //1.File类的实例化
        File file = new File("hello.txt");

        //2.FileReader流的实例化
        fr = new FileReader(file);

        //3.读入的操作
        //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
        char[] cbuf = new char[5];
        int len;
        while((len = fr.sharding:谁都能读懂的分库分表分区

人人都能读懂的编译器原理

转载 | 人人都能读懂的编译器原理

小白也能读懂的接口测试,接口测试并没有那么难

小学生都能读懂的网络协议之:WebSocket

一份数学小白也能读懂的「马尔可夫链蒙特卡洛方法」入门指南