Java IO流简介(一)字节流

本文介绍了Java IO流的基本概念,包括字节流与字符流、输入流与输出流的区别。重点讲解了字节流中的InputStream家族,如FileInputStream、BufferedInputStream、DataInputStream等,强调了缓冲流在提高性能方面的作用。同时,文章提到了OutputStream及其子类,如FileOutputStream和PrintStream,说明了PrintStream的自动flush功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参考:时光孤岛

概念

流是一个抽象的概念,它代表一串数据的集合,当Java程序需要从数据源读取数据时,就需要开启一个到数据源的流。同样,当程序需要输出数据到目的地时,也需要开启一个流。流的创建是为了更方便地处理数据的输入和输出。

分类

处理的数据类型不同:字符流和字节流。
流的方向不同:输入流和输出流。

字符流与字节流的区别

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表,可以看作是字节流的特殊升级。
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。

输入流与输出流的区别

输入流只能进行读操作,输出流只能进行写操作。从文件读取数据输入Java程序是输入流,从Java程序输出数据写入到文件是输出流。
在这里插入图片描述

IO流类图结构

在这里插入图片描述

InputStream
常用类含义
InputStreamInputStream抽象类是字节输入流所有类的超类,它以字节为单位从数据源中读取数据
FileInputStream以字节为单位从文件中读取数据
FilterInputStream封装其它的输入流,并为它们提供额外的功能
BufferedInputStream给输入流提供缓冲功能,提高读取效率
DataInputStream装饰其它输入流,它允许应用程序以与机器无关方式从底层输入流中读取 Java基本数据类型
PushBackInputStream可以把读取进来的某些数据重新回退到输入流的缓冲区之中
ObjectInputStream从输入流读入对象,读取对象信息
PipedInputStream与其它线程共用的管道中读取数据
SequenceInputStream合并流
StringBufferInputStream以字节为单位从字符串中读取数据
ByteArrayInputStream在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。
常用方法含义
public abstract int read() throws IOExecption该方法用于从输入流中读取数据的下一个字节,返回读到的字节值,若遇到流的末尾,返回-1。
public int read(byte[] b) throws IOExecption该方法用于从输入流中读取b.length个字节的数据,并将数据存储到缓冲区数组b中,返回的是实际读到的字节数。
public int read(byte[] b,int off,int len) throws IOExecption该方法用于从输入流中读取len个字节的数据,并从数组b的off位置开始写入到这个数组中。
public void close() throws IOExecption关闭此输入流,并释放与此输入流相关联的所有系统资源。

FileInputStream简介
FileInputStream是从文件中读取数据,所以我们让它从txt文件中读取数据,然后打印。

public class FileInputStreamTest {
    public static void main(String[] args) throws Exception {
        //1.得到数据文件
        String filePath = "E:\\ExampleTest\\TestText.txt";
        File file = new File(filePath);
        //2.创建输入流
        InputStream inputStream = new FileInputStream(file);
        //3.循环读取文件内容,输入流中将最多bytes.length个字节的数据读入一个byte数组中,
        //返回的是读取到的字节数。当文件读取到结尾时返回 -1,循环结束。
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(bytes)) != -1) {
            System.out.println("Length is " + length);
            System.out.println(new String(bytes, 0, length));
        }
        //4.关闭输入流
        inputStream.close();
    }
}

如果文件有两个字节内容,则读取时将两个字节读取到bytes中,length等于2,然后再读取一次,因为到达文件末尾,则返回-1,while循环退出,关闭输入流,程序结束。

FilterInputStream简介
FilterInputStream 只是简单地重写那些将所有请求传递给所包含输入流的 InputStream 的所有方法。FilterInputStream 的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。从FilterInputStream类注释可以看出,需要关注的是FilterInputStream的子类,BufferedInputStream,DataInputStream,PushBackInputStream。
FliterInputStream不能直接创建,它的构造方法是protected

BufferedInputStream简介
BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能。
我们从InputStream中读取数据时,一般都会是磁盘io,或者网络io,这都是耗费大量时间的操作,比如我们现在从文件读取20个字节,过一会又从文件读取20个字节,这就是两次io,浪费资源,有了BufferedInputStream,就解决这个两次io的问题,输入流在read时,干脆多读一部分数据进来,放在内存里,等你每次操作流的时候,读取的数据直接从内存中就可以拿到,这就减少了io次数,提升效率。

public class BufferedInputStreamTest{
    public static void main(String[] args) throws Exception{
        //1.得到数据文件
        String filePath = "E:\\ExampleTest\\TestText.txt";
        File file = new File(filePath);
        //2.创建缓冲输入流
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
        //3.循环读取文件内容,输入流中将最多bytes.length个字节的数据读入一个byte数组中,
        //返回的是读取到的字节数。当文件读取到结尾时返回 -1,循环结束。
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = inputStream.read(bytes)) != -1) {
            System.out.println("Length is " + length);
            System.out.println(new String(bytes, 0, length));
        }
        //4.关闭输入流
        inputStream.close();
    }
}

DataInputStreamDataOutputStream简介
DataInputStream 是用来装饰其它输入流,DataOutputStream是用来修饰其他输出流,它允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用DataOutputStream(数据输出流)写入数据然后由DataInputStream(数据输入流)读取数据。

public class DataInputStreamTest {
    public static void main(String[] args) throws Exception{
        //1.得到数据文件
        String filePath = "E:\\ExampleTest\\TestText.txt";
        File file = new File(filePath);
        file.createNewFile();
        //2.创建输出流,写入Java基本类型数据
        DataOutputStream bufferedOutputStream = new DataOutputStream(new FileOutputStream(file));
        bufferedOutputStream.writeInt(1);
        bufferedOutputStream.writeDouble(3.14);
        bufferedOutputStream.writeBoolean(true);
        bufferedOutputStream.writeUTF("Focus");
        bufferedOutputStream.flush();
        bufferedOutputStream.close();
        //3.创建输入流,读取Java基本类型数据
        DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file));
        System.out.println(dataInputStream.readInt());
        System.out.println(dataInputStream.readDouble());
        System.out.println(dataInputStream.readBoolean());
        System.out.println(dataInputStream.readUTF());
        dataInputStream.close();
    }
}

DataOutputStream和DataInputStream是相互配合使用,我们可以给文件中写入Java基本数据类型,然后再读取出来,也就是允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型,但是在读取时要保证顺序一致性,否则会导致java.io.EOFException。一个int是32位,4个字节,我们是用字节流的形式保存的,在看txt文件时,可能会乱码,但英文字符串会正常显示。
在这里插入图片描述

PushBackInputStream简介
PushBackInputStream回退流,可以把读取的数据放回去再读取一遍。
public void unread(int b) throws IOException是回退流中的回退方法。
在这里插入图片描述
比如上面的文本,我想把.重新读一遍。

public class PushBackInputStreamTest {
    public static void main(String[] args) throws Exception{
        //1.得到数据文件
        String filePath = "E:\\ExampleTest\\TestText.txt";
        File file = new File(filePath);
        //2.创建缓冲输入流
        PushbackInputStream inputStream = new PushbackInputStream(new FileInputStream(file));
        int temp = 0;
        while ((temp = inputStream.read()) != -1) {
            if (temp == '.') {
                inputStream.unread((char)temp);
                System.out.println("退回" + (char)temp);
                temp = inputStream.read();
                System.out.println((char)temp);
            }else {
                System.out.println((char)temp);
            }
        }
        inputStream.close();
    }
}

产生结果如下图所示:
在这里插入图片描述
ObjectInputStreamObjectOutputStream简介
ObjectInputStream可以从输入流中读取Java对象,它和DataInputStream的区别是DataInputStream只能读取基本类型,而ObjectInutStream可以读取基本类型和对象,所读取的对象必须实例化。
这有一个实例化类Man,我们先将它的对象写入文件中,然后读取出来。

public class Man implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    public Man(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class ObjectInputStreamTest {

    public static void main(String[] args) throws Exception{
        //1.得到数据文件
        String filePath = "E:\\ExampleTest\\TestText.txt";
        File file = new File(filePath);
        file.createNewFile();
        //2.创建输出流,写入Java类
        Man man = new Man("LiYunLong", 39);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
        objectOutputStream.writeObject(man);
        objectOutputStream.writeUTF("End");
        objectOutputStream.close();
        //3.创建输入流,读取java类
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        Man secondMan = (Man) objectInputStream.readObject();
        System.out.println(secondMan.getName());
        System.out.println(secondMan.getAge());
        System.out.println(objectInputStream.readUTF());
        objectInputStream.close();
    }
}

文本内容如下所示:
在这里插入图片描述

PipedInputStreamPipedOutputStream简介
PipedInputStream可以用于两线程之间通信,在线程之间发送消息。
public void connect(PipedOutputStream src)public void connect(PipedInputStream snk)分别是PipedInputStream与PipedOutputStream的方法,两者的实质没有区别,就是将两个线程之间的管道连接起来,实例代码如下:

public class PipedInputStreamTest {

    public static void main(String[] args) throws IOException {
        SenderThread senderThread = new SenderThread();
        ReceiverThread receiverThread = new ReceiverThread();
        //将两个线程连接起来
        receiverThread.getPipedInputStream().connect(senderThread.getPipedOutputStream());
        //与上面代码实质一样
        //senderThread.getPipedOutputStream().connect(receiverThread.getPipedInputStream());
        senderThread.run();
        receiverThread.run();
    }

}
//发送线程
public class SenderThread extends Thread {

    private PipedOutputStream pipedOutputStream = new PipedOutputStream();

    public PipedOutputStream getPipedOutputStream() {
        return pipedOutputStream;
    }

    @Override
    public void run() {
        try {
            //发送一条消息
            pipedOutputStream.write("A message for you".getBytes());
            pipedOutputStream.flush();
            pipedOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//接受线程
public class ReceiverThread extends Thread{

    private PipedInputStream pipedInputStream = new PipedInputStream();

    public PipedInputStream getPipedInputStream() {
        return pipedInputStream;
    }

    @Override
    public void run() {
        byte[] bytes = new byte[1024];
        try {
            //接受一条消息
            pipedInputStream.read(bytes);
            pipedInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(new String(bytes));
    }
}

SequenceInputStream简介
合并流,可以多个流合并成一个流,从而操作起来更加方便。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾;接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
在这里插入图片描述
在这里插入图片描述

public class SequenceInputStreamTest{
    public static void main(String[] args) throws Exception {
        //1.创建第一个输入流
        String filePathOne = "E:\\ExampleTest\\One.txt";
        File fileOne = new File(filePathOne);
        FileInputStream filtInputStreamOne = new FileInputStream(fileOne);
        //2.创建第二个输入流
        String filePathTwo = "E:\\ExampleTest\\Two.txt";
        File fileTwo = new File(filePathTwo);
        FileInputStream fileInputStreamTwo = new FileInputStream(fileTwo);
        //3.将连个输入流合并在,打印出结果
        SequenceInputStream sequenceInputStream = new SequenceInputStream(filtInputStreamOne, fileInputStreamTwo);
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = sequenceInputStream.read(bytes)) != -1) {
            System.out.println(new String(bytes, 0, length));
        }
        filtInputStreamOne.close();
        fileInputStreamTwo.close();
        sequenceInputStream.close();
    }
}

在这里插入图片描述

StringBufferInputStream简介
StringBufferInputStream这个类已经打上了@Deprecated注解,所以不应该用这个类写代码,StringBufferInputStream类的本意是把字符串转换为字节流,然后进行读操作,但是这个类实现中仅仅使用了字符编码的低8位,不能把字符串中的所有字符(比如中文字符)正确转换为字节,因此这个类已经被废弃,取而代之的是StringReader类。

ByteArrayInputStream简介
字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。

public class ByteArrayInputStreamTest {

    public static void main(String[] args) throws Exception{
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("Hello".getBytes());
        byte[] bytes = new byte[1024];
        byteArrayInputStream.read(bytes);
        System.out.println(new String(bytes));
        byteArrayInputStream.close();
    }
}

至此,InputStream下的大部分类已经做了简短的概述,类很多,但可大体分成两种,直接继承InputStream的,和继承FilterInputStream的,前者主要是数据的来源,比如FileInputStream是文件,ObjectInputStream是类,PipedInputStream是线程,SequenceInputStream是输入流,StringBufferInputStream是String,ByteArrayInputStream是byte数组,而后者主要是对数据的操作,或者说方法,BufferedInputStream是缓冲,DataInputStream是实例化数据,PushBackInputStram是回退。我们可以用方法装饰数据来源,做一些优化,比如对文件输入流做缓冲,提高性能。所以整个Java IO类是装饰者模式。

OutputStream
常用类含义
OutputStreamOutputStream抽象类是表示字节输出流的所有类的超类,它以字节为单位将数据写入数据源
FileOutputStream以字节为单位将数据写入到文件
FilterOutputStream装饰其它输出流,并为它们提供额外的功能
BufferedOutputStrean装饰其它输出流,给输出流提供缓冲功能,提高写入效率
DataOutputStrean装饰其它输出流,它允许应用程序以与机器无关方式向底层写入Java基本数据类型
PrintStream装饰其它输出流,使它们能够方便地打印各种数据值表示形式
ObjectOutputStream将对象信息写入到输出流
PipedOutputStream与其它线程共用的管道中写入数据
ByteArrayOutputStream在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中
常用方法含义
public abstract void write(int b) throws IOExecption该方法用于将指定的字节写入到输出流
public int write(byte[] b) throws IOExecption该方法用于将b.length个字节从指定的byte数组写入到输出流。
public int write(byte[] b,int off,int len) throws IOExecption该方法用于将len个字节的数据,并从数组b的off位置开始写入到输出流。
public void close() throws IOExecption关闭此输出流,并释放与此输出流相关联的所有系统资源。

FileOutputStream简介
FileOutputStream是向文件中写入数据,所以我们给txt文件中写一些数据,然后看一下。

public class FileOutputStreamTest {

    public static void main(String[] args) throws IOException {
        //1.创建写入文件
        File file = new File("E:\\ExampleTest\\TestText.txt");
        file.createNewFile();
        //2.创建输出流,然后写入数据
        OutputStream outputStream = new FileOutputStream(file);
        outputStream.write("Hello World".getBytes());
        outputStream.flush();
        outputStream.close();
    }
}

在文件中的结果如下图所示:
在这里插入图片描述
FilterOutputStream简介
FilterOutputStream类似于FilterInputStream,是一个抽象类,它的子类BufferedOutputStream,DataOutputStream,PrintStream可以修饰其它输出类,添加一些额外的功能。

BufferedOutputStream简介
BufferedOutputStream类似于BufferInputStream,是个带缓冲区的输出流,可以用来修饰其它输出流,从而提高写入性能。

public class BufferedOutputStreamTest {

    public static void main(String[] args) throws Exception {
        //1.创建准备写入的文件
        File file = new File("E:\\ExampleTest\\TestText.txt");
        file.createNewFile();
        //2.创建输出流,写入数据
        OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
        outputStream.write("BufferedOutputStreamTest".getBytes());
        outputStream.flush();
        outputStream.close();
    }
}

PrintStream简介
PrintStream是FilterOutputStram的子类,可以用来修饰其它输出流。PrintStream还提供了自动flush功能,往PrintStream写入的数据会立刻调用flush()函数。

public class PrintStreamTest {

    public static void main(String[] args) throws Exception{
        //1.创建准备写入的文件
        File file = new File("E:\\ExampleTest\\TestText.txt");
        file.createNewFile();
        //2.创建输出流,写入数据
        PrintStream printStream = new PrintStream(new FileOutputStream(file));
        printStream.println("Hello World");
        printStream.println("你好世界");
        printStream.println(true);
        printStream.close();
    }
}

在这里插入图片描述
可以看到的是,不管是什么数据类型,PrintStream都会将其转变为字符串,然后输出。我们经常使用的System.out.println();中的out就是一个PrintStream对象。

ByteArrayOutputStream简介
在创建ByteArrayOutputStream实例时,会在程序内部创建一个byte型数组的缓冲区,缓冲区会随着数据的不断写入而自动增长,可以使用toByteArray()或者toString()获取数据,也可以使用ByteArrayInputStream来获取。

public class ByteArrayOutputStreamTest {

    public static void main(String[] args) throws Exception{
        //1.创建准备写入的文件
        File file = new File("E:\\ExampleTest\\TestText.txt");
        file.createNewFile();
        //2.创建输出流,写入数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byteArrayOutputStream.write(0);
        byteArrayOutputStream.write("ok".getBytes());
        byteArrayOutputStream.write(1);
        //3.使用输入流读取数据
        byte[] bytes = byteArrayOutputStream.toByteArray();
        ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(bytes);
        int length = 0;
        while ((length = arrayInputStream.read()) != -1) {
            System.out.println(length);
        }
        byteArrayOutputStream.close();
        arrayInputStream.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值