Bootstrap

【Java基础】输入输出流(IO流)

 目录

一、流的概念

二、输入输出流的类层次结构图

三、使用 InputStream 和 OutputStream流类

四、使用 Reader 和 Writer 流类


Java语言的输入输出功能必须借助于输入输出包 java.io 来实现,Java开发环境提供了丰富的流类,完成从基本的输人输出到文件操作。利用java.io包中所提供的输入输出类程序不但可以很方便地实现多种输人输出操作,而且还可实现对复杂的文件与文件夹的管理。

一、流的概念

流(Stream)是指计算机各部件之间的数据流动。按照数据的传输方向,流可分为输入流与输出流。从流的内容上划分,分为字节流和字符流。Java语言的流中的数据既可以是未经加工的原始二进制数据,也可以是经过一定编码处理后符合某种格式规定的特定数据即流是由位(Bit)组合或字符(Character)所构成的序列,如字符流序列、数字流序列等。用户可以通过流来读写数据,甚至可以通过流连接数据源,并可以将数据以字符或位组合的形式保存。

1.输人输出流


在Java语言中,把不同类型的输入输出源(键盘、屏幕、文件、网络等)抽象为流,而其中输人或输出的数据称为数据流(Data Stream),用统一的方式来表示,从而使程序设计简单数据流分为输入流和输出流两大类,将数据从外设或外存(如键点、标、文件)传递到应用程序的流称为输入流(Input Stream);将数据从应用程序传递到外设或外存(如屏幕、打印机、文行等)的流称为输出流(Output Stream)。

流式输入输出的最大特点是数据的获取和发送是沿着数据序列顺序进行,每一个数据都必须等待排在它前面的数据读入或送出之后才能被读写,每次读写操作处理的都是序列中乘余的未读写数据中的第一个,而不能随意选择输入输出的位置。

2. 缓冲流

对数据流的每次操作若都以字节为单位进行,即可以向输出流写入一个字节,或从输人流中读取一个字节,显然这样数据传输效率很低。为了提高数据传输效率,通常使用缓冲流(Bufered Stream),即为一个流配有一个缓冲区(Buffer),这个缓冲区就是专门用于传送数据的一块内存。

二、输入输出流的类层次结构图

三、使用 InputStream 和 OutputStream流类

注意:虽然字节流可以操作文本文件,但不提倡这样做,因为用字节流操作文本文件如果文件中有汉字,可能会出现乱码。这是因为字节流不能直接操作nicode 字符所致因此Java语言不提倡使用字节流读写文本文件,而建议使用字符流操作文本文件。

1. InputStream 流类

InputStream流类中包含一套所有字节输入都需要的方法,可以完成最基本的从流读入数据的功能。常用方法如下表:

方法功能说明
public int read()从输入流中的当前位置读入一个字节(8bit)的二进制数据,然后以此数据为低位字节,配上8个全0的高位字节合成一个16位的整型量(0~255)返回给调用此方法的语句,若输人流中的当前位置没有数据,则返回 -1
 public int read(byte[ ] b)从输入流中的当前位置连续读人多个字节保存在数组b中,同时返回所读到的字节数

public int read(byte[ ] b,int off,

int len)

从输人流中的当前位置连续读人len个字节,从数组b的第off+1个元素位置处开始存放,同时返回所读到的字节数
public int available()返回输入流中可以读取的字节数
public long skip(long n)使位置指针从当前位置向后跳过n个字节
public void mark(int readlimit)在当前位置处做一个标记,并且在输人流中读取readlimit个字节数后该标记失效
public void reset( )将位置指针返回到标记的位置
public void close()关闭输入流与外设的连接并释放所占用的系统资源

当Java程序需要从外设如键盘、磁盘文件等读人数据时,应该创建一个适当类型的输人流对象来完成与该外设的连接。由于InputStream是抽象类,所以程序中创建的输入流对象一般是InputStream某个子类的对象,通过调用该对象继承的read()方法就可实现对相应外设的输入操作。

注意:流中的方法都声明抛出异常,所以程序中调用流方法时都必须处理异常,否则编译不能通过。

2. OutputStream 流类 

OutputStream类中包含一套所有字节输出都需要的方法,可以完成最基本的向输出流写人数据的功能。常用方法如下:

方法功能说明
public void write(int b)将参数b的低位字节写入到输出流
public void write(byte[ ] b)将字节数组b中的全部字节按顺序写人到输出流
public void write ( byte[ ] b, int off,int len)将字节数组b中第off+1个元素开始的len个数据,顺序地写人到输出流
public void flush( )强制清空缓冲区并执行向外设写操作
public void close()关闭输出流与外设的连接并释放所占用的系统资源

当Java程序需要向外界如屏幕,磁盘文件等输出数据时,应该创建一个适当类型的输出流的对象来完成与该外设的连接。由于OutputStream是抽象类,所以程序中创建的输出流对象一般是OutputStream 某个子类的对象,通过调用该对象继承的 write() 方法就可以实现对相应外设的输出操作。

以下是一个使用Java的InputStream和OutputStream流类的代码示例:

import java.io.*;

public class InputStreamOutputStreamExample {
    public static void main(String[] args) {
        try {
            // 创建一个文件输入流对象,用于读取文件内容
            FileInputStream fis = new FileInputStream("input.txt");

            // 创建一个文件输出流对象,用于将数据写入文件
            FileOutputStream fos = new FileOutputStream("output.txt");

            // 定义一个缓冲区,用于存储读取到的数据
            byte[] buffer = new byte[1024];
            int bytesRead;

            // 循环读取文件内容,并将其写入到另一个文件中
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }

            // 关闭输入输出流
            fis.close();
            fos.close();

            System.out.println("文件复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这个示例中,我们首先创建了一个FileInputStream对象,用于读取名为"input.txt"的文件。然后,我们创建了一个FileOutputStream对象,用于将数据写入名为"output.txt"的文件。接下来,我们定义了一个字节数组作为缓冲区,并使用循环从输入流中读取数据,将其写入到输出流中。最后,我们关闭了输入输出流。

3. 文件输入输出流

FilelnputStream FileOutputStream主要是负责完成对本地磁盘文件的顺序输人与输出操作的流。FileInputStream 类的对象表示一个文件字节输入流,从中可读取一个字或一批字节。在生成FileInputStream类的对象时,若找不到指定的文件,则抛出FileNotFoundException异常,该异常必须捕获或声明抛出。

无论哪个构造方法,在创建文件输入或输出流时都可能因给出的文件名不对或路径不对,或文件的属性不对等,不能打开文件而造成错误,此时系统会抛出FileNotFoundException异常。执行 read() 和  write() 方法时还可能因 IO 错误,系统抛出 IOException 异常, 所以创建输入输出流并调用构造方法语句以及执行读写操作的语句应该被包含在 try语句块中,并由相应的 catch语句块来处理可能产生的异常。

FileInputStreamFileOutputStream主要用来处理二进制图像文件,例如:

import java.io.*;

class demo {
    public static void main(String[] args) throws IOException {
        FileInputStream fin = new FileInputStream("1.png");
        FileOutputStream fout = new FileOutputStream("2.png");
        System.out.println("文件大小"+fin.available());//利用available()方法获取文件的大小,并输出,以字节(B)为单位。
        byte[] b = new byte[fin.available()];//新建一个byte类型的数组
        fin.read(b);//将文件读入数组
        fout.write(b);//将数组中的数据写入新文件"2.png"中。
        System.out.println("文件复制更名成功!");
    }
}

4. 顺序输入流

顺序输入流类 SequenceInputStream 是 InputStream 的直接子类,其功能是将多个输入流顺序连接在一起,形成单一的输入数据流,没有对应的输出数据流存在。再进行输入时,顺序输入流依次打开每个输入流并读取数据,也在读取完毕后将该流关闭,然后自动切换到下一个输入流。

SequenceInputStream的构造方法:

构造方法功能说明
public SequenceInputStream(Enumeration e)创建一个串行输入流,连接枚举对象e中的所有输入流
public SequenceInputStream(InputStream s1, InputStream s2)创建一个串行输入流,连接输入流s1和s2

SequenceInputStream类的常用方法

常用方法功能
public int available()返回流中可读取的字节数
public void close()关闭输入流
public int read()从输入流中读取字节,遇到EOF就转向下一输入流
public int read(byte[] b, int off, int len)将len个数据读到一个字节数组从off开始的位置

5. 过滤输入输出流

过滤输入输出流的构造方法主要是通过将其他输入或输出流作为参数传递来创建过滤器。

FilterInputStream构造方法

基础构造方法:FilterInputStream的基础构造方法接受一个InputStream类型的参数,这个参数代表了要过滤的基础输入流。

示例代码

FilterInputStream filterStream = new FilterInputStream(new FileInputStream("example.txt"));

FilterOutputStream构造方法

基础构造方法:FilterOutputStream的基础构造方法接受一个OutputStream类型的参数,这个参数代表了要过滤的基础输出流。

示例代码

FilterOutputStream filterStream = new FilterOutputStream(new FileOutputStream("example.txt"));

6. 管道输入输出流

管道输入输出流通常用于在不同线程之间传输数据。这种机制类似于现实生活中的管道系统,一端输入,另一端输出,数据在管道中流动。在Java中,PipedInputStream作为管道的接收端,而PipedOutputStream则是发送端。

管道输入流(PipedInputStream)和管道输出流(PipedOutputStream)在使用前需要建立连接。这可以通过PipedInputStream的connect方法或者直接通过PipedOutputStream的构造函数来实现。需要注意的是,一个输出流只能与一个输入流连接,且反之亦然。如果尝试将任一端与其他流重新连接,会抛出IOException异常。

在实际使用中,通常需要创建两个线程,一个负责写入数据到PipedOutputStream,另一个从PipedInputStream读取数据。这样,当写入线程向PipedOutputStream写入数据时,这些数据可以被读取线程通过PipedInputStream读取并处理。数据传输过程中,如果缓冲区满了,写入操作会阻塞直至有空间可用;如果缓冲区为空,读取操作也会相应地阻塞直到有数据可读。

7. 标准输入输出流

程序对标准输入输出设备进行操作的时候,则不需要创建输入或输出流类的对象。
对于一般的系统来说,标准输入设备通常指键盘,标准输出设备通常指屏幕显示器。为了方便程序对键盘输入和屏幕输出进行操作。Java系统事先在System类中定义静态流对象 System.inSystem.out System.err 。System.in对应着输入流,通常指键盘输入设备。System.out对应着输出流,指显示器等信息输出设备,System.err对应着标准错误输出设备,使得程序的运行错误,可以由固定的输出位置,通常来说该对象对应着显示器。

代码示例:从键盘上输入一串字符,然后在显示在屏幕上

import java.util.Scanner;

public class KeyboardInputExample {
    public static void main(String[] args) {
        // 创建一个Scanner对象来读取输入
        Scanner scanner = new Scanner(System.in);

        // 提示用户输入一串字符
        System.out.print("请输入一串字符: ");

        // 读取用户输入的一行字符串
        String input = scanner.nextLine();

        // 显示用户输入的字符串
        System.out.println("你输入的字符是: " + input);

        // 关闭Scanner对象
        scanner.close();
    }
}

四、使用 Reader 和 Writer 流类

ReaderWriter是Java中用于处理字符数据的输入输出流类。它们提供了一种方便的方式来读取和写入文本数据,包括字符、字符串和字符数组等。

Reader类的常用方法:

常用方法说明
public int read()从输入流中读一个字符
publici int read(char[ ] cbuf)从输入流中读最多cbuf.length个字符,存入字符数组cbuf
public int read(char[ ] cbuffer, int off, int len)从输入流中读最多len个字符,存入字符数组cbuffer中从off开始的位置
public long skip(long n)从输入流中最多向后跳n个字符
public boolean ready()判断流是否最好读的准备
public void mark(int readAheadLimit)标记输入流的当前位置
public boolean markSupported()测试输入流是否支持mark
public void reset()重定位输入流
public void close()关闭输入流

Writer类的常用方法:

常用方法说明
public void write(int c)将单一字符c输出到流中
public void write(String str)将字符串输出到流中
public void write(char[] cbuf)将字符数组cbuf输出到流
public void write(char[] cbuf, int off, int len)将字符数组按指定格式输出
public void flush()将缓冲区的数组写到文件
public void close()关闭输出流

使用 FileWriter 类写入文件

文件字行输出流类 FileWrite r继承自 OutputStreamWriter 类,而OutputStreamWrite 类又继季自Wrer类.因此Writer类与OutputStreamWriter类所提供的方法均可供Fiiewmter类所创建的对象使用。要使用FleWriler类将数据写人文件,必须先调用 FileWriter() 构造方法创建 FileWriter 类对象,再利用它来调用write()方法。

FileWriter 类的构造方法如下:

构造方法功能说明
public FileWriter(String filename)根据所给文件名创建一个可供写人字符数据的输出流对象,原先的文件会被覆盖
public FileWriter(String filename,boolean a)同上,但如果a设置为true,则会将数据追加在原先文件的后面

代码示例:利用 FileWriter 类将字符数组与字符串写到文件中

import java.io.FileWriter;
import java.io.IOException;

public class WriteToFile {
    public static void main(String[] args) {
        char[] charArray = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
        String str = "Hello, World!";

        try {
            FileWriter fileWriter = new FileWriter("output.txt");
            fileWriter.write(charArray);
            fileWriter.write(str);
            fileWriter.close();
            System.out.println("成功将字符数组和字符串写入文件");
        } catch (IOException e) {
            System.out.println("发生错误");
            e.printStackTrace();
        }
    }
}

使用BufferedReader 类读取文件


缓冲字符输入流类 BufferedReader 继承自Reader类,BufferedReader 类是用来读取缓冲区里的数据。使用BufferedReader类来读取缓冲区中的数据之前,必须先创建FileReader类对象,再以该对象为参数来创建BufferedReader类的对象,然后才可以利用此对象来读取缓冲区中的数据。

BufferedReader类构造方法如下:

构造方法功能说明
public BufferedReader(Reader in)创建缓冲区字符输人流
public BufferedReader(Reader in ,int size)创建缓冲区字符输入流,并设置缓冲区大小

代码示例:利用缓冲字符输入流类 BufferedReader 读取文本文件

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFromFile {
    public static void main(String[] args) {
        try {
            FileReader fileReader = new FileReader("input.txt");
            BufferedReader bufferedReader = new BufferedReader(fileReader);

            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }

            bufferedReader.close();
        } catch (IOException e) {
            System.out.println("发生错误");
            e.printStackTrace();
        }
    }
}

;