理解Java中的IO字节流(File的输入输出理解)

Posted 李亦华的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Java中的IO字节流(File的输入输出理解)相关的知识,希望对你有一定的参考价值。

IO 概述

什么是 IO

Java中IO操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输岀也叫做作写出数据。

IO的分类

根据数据的流向分为:输入流和输出流。

输入流:把数据从其他设备上读取到内存中的流。

输出流:把数据从内存中写出到其他设备上的流。

格局数据的类型分为:字节流和字符流。

顶级父类们

img

字节流

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都是一个ー个的字节,在传输时也是一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

字节输出流——OutputStream

java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

java.io.OutputStream
public abstract void write(int b) throws IOException;
// 将指定的字节输出流。

public void write(byte b[]) throws IOException{...};
// 将b.length字节从指定的字节数组写入此输出流。

public void write(byte b[], int off, int len) throws IOException{...};
// 从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。

public void flush() throws IOException{};
// 刷新此输出流并强制任何缓冲的输出字节被写出。

public void close() throws IOException {};
// 关闭此输出流并释放与此流相关联的任何系统资源。

FileOutputStream类

  1. OutputStream抽象类,有很多的子类,其中FileOutputStream类是它其中一个简单的子类。
  2. java.io.FileOutputStream是文件输出流,用于将数据写出到文件。

写出字节数据

写出字节:write(int b)方法,每次可以写出一个字节数据

字节输出流的使用步骤:

  1. 创建一个 FileOutputStream对象,构造方法中传递写入数据的目的地。
  2. 调用FileOutputStream对象中的方法write,把数据写入到文件中。
  3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空提供程序的效率)。
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoFosWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名创建字节流对象
        FileOutputStream fos = new FileOutputStream("FOS.txt");

        // 写出数据
        // 写出第一个字节
        fos.write(97);
        // 写出第二个字节
        fos.write(98);
        // 写出第三个字节
        fos.write(99);

        // 关闭资源
        fos.close();
    }
}

运行结果:生成了一个"FOS.txt"文件

img

FOS.txt文件大小是3个字节,内容是abc

写入数据的原理(内存-->硬盘):

Java程序 --> Java虚拟机 --> 操作系统 --> 操作系统调用写数据的方法 --> 把数据写到文件中

写数据的时候,会把要写入的数据转换为二进制数。在打开文件的时候,都会查询编码表(例如:ASCII表),把字节转换为字符表示。

write(byte b[])方法

作用:将b.length字节从指定的字节数组写入此输出流。

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

public class Demo01Write {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("FOS1.txt");

        byte[] bytes = {97, 98, 99};
        // 将3个字节从bytes字节数组写入fos输出流。
        fos.write(bytes);

        fos.close();
    }
}

运行结果:生成了一个"FOS1.txt"文件

img

write(byte b[], int off, int len)方法

作用:从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。

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

public class Demo02Write {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("FOS2.txt");

        byte[] bytes = {97, 98, 99, 100, 101, 102};
        // 将3个字节从bytes字节数组写入fos输出流。
        fos.write(bytes, 1, 4);

        fos.close();
    }
}

运行结果:生成了一个"FOS2.txt"文件

img

write方法简单练习

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class Demo03Write {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("FOS3.txt");

        // 调用String类中的getBytes()方法,把一串字符转换为byte数组
        byte[] bytes = "大佬,你好!".getBytes();
        // 将该数组转换为集合输出
        System.out.println(Arrays.toString(bytes));

        // 将多个字节从bytes字节数组写入fos输出流。
        fos.write(bytes);

        fos.close();
    }
}

控制台输出

[-27, -92, -89, -28, -67, -84, -17, -68, -116, -28, -67, -96, -27, -91, -67, -17, -68, -127]

运行结果:还生成了一个"FOS3.txt"文件

img

字节输出流的续写和换行

public FileOutputStream(File file, boolean append)
// 创建文件输出流以写入由指定的File对象表示的文件。

public FileOutputStream(String name, boolean append)
// 创建文件输出流以指定的名称写入文件。

这两个构造方法,参数中都需要传入一个 boolean类型的值,true表示追加数据,false表示清空原有数据这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:

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

public class Demo01OutputAppend {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt", true);

        // 往"a.txt"文件中写入"大佬,"内容
        byte[] bytes1 = "大佬,".getBytes();
        fos.write(bytes1);

        // 往"a.txt"文件中追加写入"你好!"内容
        byte[] bytes2 = "你好!".getBytes();
        fos.write(bytes2);

        fos.close();
    }
}

运行结果:生成了一个"a.txt"文件

img

如果需要换行:

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

public class Demo02OutputAppend {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("b.txt", true);

        // 往"a.txt"文件中写入"大佬,"内容
        byte[] bytes1 = "大佬,".getBytes();
        fos.write(bytes1);

        // 换行 "\\n" 或者 "\\r"
        byte[] bytes = "\\n".getBytes();
        fos.write(bytes);

        // 往"a.txt"文件中追加写入"你好!"内容
        byte[] bytes2 = "你好!".getBytes();
        fos.write(bytes2);

        fos.close();
    }
}

运行结果:生成了一个"b.txt"文件

img

字节输入流 —— InputStream

java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

public void close()
// 关闭此输入流并释放与此流相关联的任何系统资源。

public abstract int read()
// 从输入流读取数据的下一个字节。

public int read(byte[] b)
// 从输入流中读取一些字节数,并将它们存储到字节数组b中。

FileInputStream类

java.io.FileInputStream类是文件输入流,从文件中读取字节。

构造方法

public FileInputStream(String name) throws FileNotFoundException
// 通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的路径名name命名。

public FileInputStream(File file) throws FileNotFoundException
// 通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的File对象file命名。

当我们创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException异常。

读取数据的原理(硬盘-->内存):
Java程序 --> Java虚拟机 --> 操作系统 --> 操作系统调用读取数据的方法 --> 读取文件

字节输入流的使用步骤
  1. 创建FileInputStream对象,构造方法中绑定要读取的数据源。
  2. 使用FileInputStream对象中的方法read,读取文件。
  3. 释放资源。

下面这个例子中使用的read()方法是一个字节一个字节的读取的。

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

public class DemoInputRead {
    public static void main(String[] args) throws IOException {
        // 创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("/Users/liyihua/IdeaProjects/Study/A");

        // 使用FileInputStream对象中的方法read,读取文件
        // char=65 对应 A
        int lien0 = fis.read();
        char c0 = (char) lien0;
        System.out.println(lien0 + "对应" + c0);

        // char=10 对应 /n
        int lien1 = fis.read();
        char c1 = (char) lien1;
        System.out.println(lien1 + "对应" + c1);

        // char=66 对应 B
        int lien2 = fis.read();
        char c2 = (char) lien2;
        System.out.println(lien2 + "对应" + c2);

        // char=10 对应 /n
        int lien3 = fis.read();
        char c3 = (char) lien3;
        System.out.println(lien3 + "对应" + c3);

        // 释放资源
        fis.close();
    }
}

文件A的内容:

img

如果已经将文件A的内容读取完了,那么再读取的话,会返回-1。

FileInputStream类中的部分方法

int available()
// 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。

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

int read()
// 从输入流中读取数据的下一个字节。

int read(byte b[])
// 从输入流中读取一定数量的字节,并将其存储在緩神区数组b中。

int read(byte b[], int off, int len)
// 将输入流中最多len个数据字节读入byte数组。

long skip(long n)
// 跳过和丢弃此输入流中数据的n个字节。
read(byte b[])方法
import java.io.FileInputStream;
import java.io.IOException;

public class Demo02InputRead {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("/Users/liyihua/IdeaProjects/Study/B");

        // 创建一个byte数组,存储读取的字节
        byte[] bytes = new byte[2];

        // 开始读取,每次读取两个
        // 第一次读取: bytes = {97, 98},len1 = 2
        int len1 = fis.read(bytes);
        System.out.println(
                "bytes = " + new String(bytes)
                + "\\t\\t"
                + "len = " + len1
        );

        // 开始读取,每次读取两个
        // 第二次读取: bytes = {99, 100},len2 = 2
        int len2 = fis.read(bytes);
        System.out.println(
                "bytes = " + new String(bytes)
                + "\\t\\t"
                + "len = " + len2
        );

        // 开始读取,每次读取两个
        // 第三次读取: bytes = {101, 100},len3 = 2
        int len3 = fis.read(bytes);
        System.out.println(
                "bytes = " + new String(bytes)
                + "\\t\\t"
                + "len = " + len3
        );

        // 开始读取,每次读取两个
        // 第四次读取: bytes = {101, 100},len4 = 2
        int len4 = fis.read(bytes);
        System.out.println(
                "bytes = " + new String(bytes)
                + "\\t\\t"
                + "len = " + len4
        );
    }
}

文件B内容:

img

控制台输出

bytes = ab        len = 2
bytes = cd        len = 2
bytes = ed        len = 1
bytes = ed        len = -1

解析:

第一次读取:读取第1个字节和第2个字节,将初始数组的{0, 0} 替换为 {a, b}

第二次读取:读取第3个字节和第4个字节,将第一次读取后的数组的{a, b} 替换为 {c, d}

第三次读取:读取第5个字节,发现没有可以读取的了,于是将第二次读取的数组的 {c, d}里面的c替换为e,而d没得替换,即{e, d}

第四次读取:因为第三次读取的时候,已经读取完了,没有可以读取的了,依旧还是原来的数组{e, d}

第一次读取两个字节(len1=2),第二次读取两个字节(len2=2),第三次读取1个字节(len3=2),第四次没得读取(len4=-1)。

读取完后,再读取,返回值是-1。

字节流练习:图片复制

文件复制的步骤:

  1. 创建一个字节输入流对象,构造方法中绑定要读取的数据源。
  2. 创建一个字节输出流对象,构造方法中绑定要写入的目的地。
  3. 使用字节输入流对象中的方法read读取文件。
  4. 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中。
  5. 释放资源。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoCopyFile {
    public static void main(String[] args) throws IOException {
        // 创建一个字节输入流对象,构造方法中绑定要读取的数据源。
        FileInputStream fis = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo30/666.jpg");

        // 创建一个字节输出流对象,构造方法中绑定要写入的目的地。
        FileOutputStream fos = new FileOutputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo30/999.jpg");

        // 使用字节输入流对象中的方法read读取文件。
        int len = 0;
        while ((len = fis.read()) != -1) {
            // 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中。
            fos.write(len);
        }

        // 释放资源。先关闭写的,再关闭读的。
        fis.close();
        fos.close();
    }
}

运行结果:

img

程序优化:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo02CopyFile {
    public static void main(String[] args) throws IOException {
        // 创建一个字节输入流对象,构造方法中绑定要读取的数据源。
        FileInputStream fis = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo30/666.jpg");

        // 创建一个字节输出流对象,构造方法中绑定要写入的目的地。
        FileOutputStream fos = new FileOutputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo30/999.jpg");

        // 使用字节输入流对象中的方法read读取文件。
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fis.read(bytes)) != -1) {
            // 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中。
            fos.write(bytes, 0, len);
        }

        // 释放资源。
        fis.close();
        fos.close();
    }
}

以上是关于理解Java中的IO字节流(File的输入输出理解)的主要内容,如果未能解决你的问题,请参考以下文章

IO流 字节输入输出流概念理解以及适用环境

理解JAVA的IO

Java 输入/输出——理解Java的IO流

JAVA——IO流-字符流和字节流

理解Java之IO流

Java IO