文章目录

  • java中以二进制方式读写的流主要有:
  • 1. InputStream/OutputStream
  • 1.1 InputStream
  • 1.2 OutputStream
  • 2. FileInputStream/FileOutStream
  • 2.1 FileOutputStream
  • 2.2 FileInputStream
  • 3. ByteArrayInputStream、ByteArrayOutputStream
  • 3.1 ByteArrayOutputStream
  • 3.2 ByteArrayInputStream
  • 4 DataInputStream/DataOutputStream
  • 4.1 DataOutputStream&DataInputStream



java中的文件流主要分为两大类,一类按照二进制的方式处理文件;另一类按照文本的方式处理;


其中,按照二进制方式进行处理的时候没有编码的概念,也不能进行行处理

java中以二进制方式读写的流主要有:

  • InputStream、OutputStream:这是基类,他们是两个抽象类
  • FileInputStream、FileOutputStream:输入源和输出目标是文件
  • ByteArrayInputStream、ByteArrayOutputStream:输入源和输出目标是数组
  • DataInputStream、DataOutputStream:装饰类,按基本类型和字符串而非只是字节读 写 流
  • BufferInputstream、BufferOutputStream:装饰类,对输入输出流提供缓冲功能。

1. InputStream/OutputStream

1.1 InputStream

  1. InputStream的基本方法:
  1. public abstract int read() throws IOException;

该方法从流中读取下一个字节,返回类型为int,但取值为0~255,当读取到流结尾时返回-1;

  1. public int read(byte b[]) throws IOException

读入的字节会依次放入参数b[]中;
一次最多读入的字节个数为数组的长度,返回实际读取的字节数。
读到结尾时返回-1

  1. public int read(byte b[], int off, int len) throws IOException

该方法读入的第一个字节放在b[off],最多读取len个字节,read(byte b[]) 就是调用了这个方法

  1. InputStream的高级方法(基本不用的方法)
/**
* 跳过你输入流中的n个字节,因为流中剩余的字节可能不到n,所以返回实际跳过的字节数
*/
public long skip(long n) throws IOException

/**
* 返回下一次不需要阻塞就能读到的大概的字节个数
*/
public int available() throws IOException
/**
* 标记流的当前位置
* @param readLimit表示标记后还能继续读的字节个数,超过后,标记就无效了
*/
public synchronized void mark(int readlimit)
/**
*从标记的地方开始重新读取流
*/
public synchronized void reset() throws IOException
/**
* 是否支持mark操作(并不是所有的流都支持mark)
*/
public boolean markSupported()

1.2 OutputStream

  1. 基本方法:
    public abstract void write(int b) throws IOException;
    向流中写入一个字节,虽然参数是int但是只有最低的8位会用到,高的24位会被忽略

同样它也有两个批量写入的方法:

public void write(byte b[]) throws IOException
 // 将b[]从下标off开始,长度为len  写入输出流
 public void write(byte b[], int off, int len) throws IOException

这两个跟InputStream类似就不多说了。

作为输出流还有两个方法:

// 将缓冲而未实际写的数据进行实际写入
public void flush() throws IOException {
    }
    // 释放流占用的系统资源 Ps:InputStream也有
 public void close() throws IOException {
    }

2. FileInputStream/FileOutStream

FileInputStream/FileOutStream 的输入源和输出源为文件

2.1 FileOutputStream

  1. 两个主要构造:
// name 为文件路径,绝对、相对路径均可,文件不存在时会自动创建(jdk1.8)
public FileOutputStream(String name) throws FileNotFoundException
public FileOutputStream(File file) throws FileNotFoundException

干干巴巴麻麻来来的,盘它:

try (FileOutputStream fileOutputStream = new FileOutputStream("hello.txt")) {
            String txt = "hello,world,I AM OK";
            byte[] bytes = txt.getBytes(Charset.forName("UTF-8"));
            fileOutputStream.write(bytes);
        }

OutputStream只能以byte或byte数组写文件,为了写字符串,我们调用String的getBytes方法后再使用write写入

2.2 FileInputStream

// name 为文件路径,绝对、相对路径均可,文件不存在时会抛出异常
public FileInputStream(String name) throws FileNotFoundException
public FileInputStream(File file) throws FileNotFoundException

盘它:

int read;
        try (InputStream fileInputStream = new FileInputStream("hello.txt")) {
            byte[] bytes = new byte[11];
            read = fileInputStream.read(bytes);
        }
        System.out.println("读取字符数:" + read);

这段代码最多读取11位,且只读取一次,而且一开始就分配了11位长度的数组,
如果想读取所有内容,又不想分配过大的数组,可以借助ByteArrayOutputStream盘;

3. ByteArrayInputStream、ByteArrayOutputStream

他俩的输入源和输出目标是字节数组

3.1 ByteArrayOutputStream

  1. 构造
public ByteArrayOutputStream() {
        this(32);
    }
    // size:输出数组的初始化大小
     public ByteArrayOutputStream(int size)

输出的数组长度是动态扩展的,长度按照指数级扩展,每次最少增加一倍。
ByteArrayOutputStream可以方便的把数据转换为字符串或字节数组

public synchronized byte toByteArray()
public synchronized String toString() // 使用系统默认编码
public synchronized String toString(String charsetName)

使用ByteArrayOutputStream可以改进读取文件代码,使其可以将文件内容全部读取:

try (InputStream inputStream = new FileInputStream("hello.txt")) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] bytes = new byte[2];
            int bytesRead;
            while ((bytesRead = inputStream.read(bytes)) != -1) {
                byteArrayOutputStream.write(bytes, 0, bytesRead);
            }
            String txt = byteArrayOutputStream.toString();
            System.out.println(txt);
        }

3.2 ByteArrayInputStream

ByteArrayInputStream将byte数组包装成一个输入流,是一种适配器模式,主要是为了把byte数组转换为InputStream后可以方便的传入以InputStream为参数的方法中
构造:

public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)

4 DataInputStream/DataOutputStream

上面所有的读写流都只能已字节为单位读写,DataInputStream和DataOutputStream可以以int、double等类型读写。这俩都属于装饰类

4.1 DataOutputStream&DataInputStream

DataOutputStream是装饰类FilterOutputStream的子类,FilterOutputStream是OutputStream的子类,FilterOutputStream的构造方法:

public FilterOutputStream(OutputStream out) {
        this.out = out;
    }

它接受一个已有的OutputStream,基本上所有的操作都代理给它。DataOutputStream实现了DataOutput接口,可以以各种基本类型和字符串下写入数据;
such as:

public final void writeBoolean(boolean v) throws IOException
public final void writeByte(int v) throws IOException
public final void writeInt(int v) throws IOException
public final void writeDouble(double v) throws IOException
public final void writeUTF(String str) throws IOException

在写入时,DataOutputStream会将这些类型的参数转换为对应的二进制字节;
与FilterOutputStream一样,DataOutputStream的构造方法也是接受一个已有的OutputStream:

public DataOutputStream(OutputStream out) {
        super(out);
    }

光说不练假把式,我们来新建一个人类,这个人类有姓名,年龄,身高三个属性;
然后我们建一个人类的名单文件:“list.txt”,使用DataOutputStream将人类的信息写入名单中;
人类:

package com.wzk;

public class Human {
    private String name;
    private int age;
    private int height;

    public Human(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    public Human() {
    }
    // 省略get/set
}

实现类:

public static void main(String[] args) throws IOException {
        List<Human> humanList = Arrays.asList(new Human("codeFarmer", 22, 177), new Human("programmer", 30, 188));
        try (
                FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\zhenk\\IdeaProjects\\my_java_se\\src\\main\\java\\com\\wzk\\list.txt");
                DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream)
        ) {
            dataOutputStream.writeInt(humanList.size());
            for (Human human : humanList) {
                dataOutputStream.writeUTF(human.getName());
                dataOutputStream.writeInt(human.getAge());
                dataOutputStream.writeInt(human.getHeight());
            }
        }
    }

写入后我们打开list.txt发现是一堆乱码:

codeFarmer      � 
programmer      �

原因是因为DataOutputStream向文件写入时会做特殊的标记,只有DataInputStream才能进行读取
另外:DataInputStream只能用来读取DataOutputStream写入的数据,否则会报告EOFException错误。AND 读取时用到的方法要与写入时的方法对应,比如你写的时候用的writeChars,读的时候就必须使用readChars,否则会读取出错
所以啥也别说了,读取吧:

package com.wzk;

import com.alibaba.fastjson.JSON;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class HelloJava {
    public static void main(String[] args) throws IOException {
        try (
                FileInputStream fileInputStream = new FileInputStream("C:\\Users\\zhenk\\IdeaProjects\\my_java_se\\src\\main\\java\\com\\wzk\\list.txt");
                DataInputStream dataInputStream = new DataInputStream(fileInputStream)
        ) {
            int size = dataInputStream.readInt();
            List<Human> humans = new ArrayList<>(size);
            for (int i = 0; i < size; i++) {
                Human human = new Human();
                human.setName(dataInputStream.readUTF());
                human.setAge(dataInputStream.readInt());
                human.setHeight(dataInputStream.readInt());
                humans.add(human);
            }
            System.out.println(JSON.toJSONString(humans));
        }
    }
}

输出为:[{“age”:22,“height”:177,“name”:“codeFarmer”},{“age”:30,“height”:188,“name”:“programmer”}]