Java_字节流

一切皆为字节

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

字节输出流【OutputStream】&【FileOutPutStream】

java.io.OutputStream,字节输出流。这个抽象类是表示字节输出流的所有类的超类。
定义了一些子类共性的成员方法:

  • public void close() 关闭此输出流并释放与此流相关联的任何系统资源。
  • public void flush() 刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
  • public abstract void write(int b) 将指定的字节写入此输出流。

其中常用的子类:

  • public class FileOutputStream extends OutputStream

FileOutputStream:文件字节输出流
作用:把内存中的数据写到硬盘文件中
构造方法:

  • FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
  • FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。

参数:写入数据的目的地
(File file):目的地是一个文件
(String name):目的地是一个文件的路径
构造方法的作用:

  1. 创建一个FileOutputStream对象
  2. 会根据构造方法中传递的文件/文件路径,创建一个空的文件
  3. 会把FileOutputStream对象指向创建好的文件

字节输出流写入数据到文件

写入数据的原理(内存->硬盘):
Java程序->JVM(Java虚拟机)->OS(操作系统)->OS调用写数据方法->把数据写到文件中
字节输出流的使用步骤:

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

代码示例:

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

public class Demo01OutputStream {
  public static void main(String[] args) throws IOException {
    // 1. 创建一个FileOutputStream对象,构造方法中传递写入数据目的地
    FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
    // 2. 调用FileOutputStream中的write,把数据写到文件中
    fileOutputStream.write(111);
    // 3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序效率)
    fileOutputStream.close();
  }
}

字节输出流写多个字节的方法

  • public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流

代码实例:

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

public class Demo02OutputStream {
  public static void main(String[] args) throws IOException {
    // 1. 创建一个FileOutputStream对象,构造方法中传递写入数据目的地
    FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
    // 2. 调用FileOutputStream中的write,把数据写到文件中
    byte[] bytes = {66, 67, 68};
    fileOutputStream.write(bytes);
    // 3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序效率)
    fileOutputStream.close();
  }
}

如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表



如果写的第一个字节是负数,那么第一个字节会和第二个字节组成一个中文显示,查询系统默认码表(GBK)。那我们将数组中的第一个元素改为负数

byte[] bytes = {-66, 67, 68};

重新执行下方法,可以见先两个字母变成了一个汉字。注意:这不是乱码


  • public void write(byte[], int off, int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
    该方法可以把一部分数据写入都文件中。比如说,我只想让txt文件中显示CD两个字母,那么我们可以把off起始长度设为1,len结束长度设为2
    代码示例:
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo02OutputStream {
  public static void main(String[] args) throws IOException {
    // 1. 创建一个FileOutputStream对象,构造方法中传递写入数据目的地
    FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
    // 2. 调用FileOutputStream中的write,把数据写到文件中
    byte[] bytes = {66, 67, 68, 69, 70};
    fileOutputStream.write(bytes, 1, 2);
    // 3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序效率)
    fileOutputStream.close();
  }
}

可以看见文件中只有CD两个字母



写入字符串的第二种方法,可以使用String类中的方法,把字符串转换为数组

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

public class Demo02OutputStream {
 public static void main(String[] args) throws IOException {
   // 1. 创建一个FileOutputStream对象,构造方法中传递写入数据目的地
   FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
   // 2. 调用FileOutputStream中的write,把数据写到文件中
   byte[] bytes = "你好".getBytes();
   System.out.println(Arrays.toString(bytes));
   fileOutputStream.write(bytes);
   // 3. 释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序效率)
   fileOutputStream.close();
 }
}

可见文件中显示了你好两个字


字节输出流的续写与换行

在刚在的不断地执行方法和创建文件中,我其实并没有删掉原来的文件再执行方法,这就说明了重新执行一次方法会新建一个文件,如果本来就有这个文件就会把原来的文件覆盖掉。那么如何才能不让他替换,而是继续往后写,Java给我们提供了两个方法:

追加写/续写:使用两个参数的构造方法

  • public FileOutputStream(File file, boolean append) throws FileNotFoundException:创建文件输出流以写入由指定的File对象表示的文件。如果第二个参数是true,则字节将被写入文件的末尾而不是开头。

file - 要打开的文件写入。
append - 如果是 true ,那么字节将被写入文件的末尾,而不是开头

  • public FileOutputStream(String name, boolean append) throws FileNotFoundException:创建文件输出流以指定的名称写入文件。如果第二个参数是true,则字节将写入文件的末尾而不是开头。

name - 与系统相关的文件名
append - 如果是 true ,那么字节将被写入文件的末尾,而不是开头
代码实例:

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

public class Demo03OutputStream {
  public static void main(String[] args) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt", true);
    fileOutputStream.write("你好".getBytes());
    fileOutputStream.close();
  }
}

连续执行两次方法,可以看见文件中出现了两个你好。



如果想要换行,需要写入换行符号,也是String类型的字符串,但是系统不一样字符串也不一样

  • windows:\n\r
  • linux/unix:/n
  • mac:/r
    代码示例:
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo03OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt", true);
        for (int i = 0; i < 10; i++) {
            fileOutputStream.write("你好".getBytes());
            fileOutputStream.write("\r\n".getBytes());
        }
        fileOutputStream.close();
    }
}

接着刚才的两个你好继续


字节输入流【InputStrem】&【FileInputStream】

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

  • public void close() throws IOException:关闭此输入流并释放与流相关联的任何系统资源。
  • public abstract int read() throws IOException:从输入流读取数据的下一个字节。
  • public int read(byte[] b) throws IOException:从输入流读取一些字节数,并将它们存储到缓冲区。

小贴士:当完成流的操作时,必须调用close()方法,释放系统资源。

java.io.FileInputStream:从文件系统中的文件获取输入字节。
构造方法:

  • FileInputStream(File file):通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
  • FileInputStream (String name):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

读取数据的原理(硬盘->内存):
Java程序->JVM->OS->OS读取数据的方法->读取数据
字节输入流的使用步骤:

  1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
  2. 使用FileInputStream对象中的read()方法读取文件
  3. 释放资源

代码示例:

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

public class Demo01FileInputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("D:\\a.txt");//文件中只有123三个数字
        int read = fileInputStream.read();
        System.out.println(read);
        int read1 = fileInputStream.read();
        System.out.println(read1);
        int read2 = fileInputStream.read();
        System.out.println(read2);
        int read3 = fileInputStream.read();
        System.out.println(read3);
        fileInputStream.close();
    }
}
控制台打印:
49
50
51
-1

可以看到,控制台依次打印出了三个数字在ASCII表上对应的序号。因为文件中只有三个数字,所以到了第四次读取数据的时候,结果就会变成-1。
以上读取文件是一个重复的过程,所以可以使用循环优化,假如说文件中有100个数字,或者不知道有多少数据,这个时候就要用while循环来进行读取,当结果为-1的时候,说明读取完了,结束循环。先在文件中随便写入一批数据
代码示例:

public class Demo01FileInputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("D:\\a.txt");
        // 记录读取到的字节
        int read = 0;
        while ((read = fileInputStream.read()) != -1) {
            System.out.print((char)read);
        }
        fileInputStream.close();
    }
}
控制台打印:
567812345567134678367878178278234782345789234578934783245

当读取返回的结果返回-1的时候,非常完美的打印出了文件的数字。

字节输入流一次输入多个字节

上面的字节输入,哪怕用的while循环,read()方法也是一个字节一个字节的输入,效率特别低。
read()方法有个有参构造

  • int read(byte[] b):从该输入流读取最多 b.length个字节的数据为字节数组。

该方法中的byte[]有什么作用?返回值int是什么?带着问题一块看下面的代码:

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容