JAVA IO流
IO流图解
一、什么是IO流
I/O流是Java中用于执行输入和输出操作的抽象。它们被设计成类似于流水,可以在程序和外部源(如文件、网络套接字、键盘、显示器等)之间传输数据。
按处理数据单位分为:
1字符 = 2字节 、 1字节(byte) = 8位(bit)
- 字节流:单位是字节,处理二进制数据,可以处理任何文件类型的数据,如图片、视频、文本等。
InputStream
:InputStream
是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。FileInputSream
:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。PipedInputStream
:管道字节输入流,能实现多线程间的管道通信。ByteArrayInputStream
:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。FilterInputStream
:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。DataInputStream
:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。BufferedInputStream
:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。ObjectInputStream
:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream
的实例对象。
- 字符流:单位是字符,只能对文本类型的数据进行处理。
InputStreamReader
:从字节流到字符流的桥梁(InputStreamReader
构造器入参是FileInputStream
的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。BufferedReader
:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader
是对InputStreamReader
的封装,前者构造器的入参就是后者的一个实例对象。FileReader
:用于读取字符文件的便利类,new FileReader(File file)
等同于new InputStreamReader(new FileInputStream(file, true),"UTF-8")
,但FileReader
不能指定字符编码和默认字节缓冲区大小。PipedReader
:管道字符输入流。实现多线程间的管道通信。CharArrayReader
:从Char
数组中读取数据的介质流。StringReader
:从String
中读取数据的介质流。
Writer
与Reader
结构类似,方向相反 。
按数据流方向分为:
- 输入流:从外部读取数据到内存中为我所有,为输入流,如读取磁盘的数据、网络的数据。
- 输出流:从内存中将数据输出到外部,如输出到磁盘,输出到网络通信的数据。
按功能划分为:
- 节点流:用来包装数据源,直接与数据源连接的流。
- 处理流:用来包装节点流,利用节点流来连接,jdk利用的是装饰器模式进行编写。
二、基础方法
2.1、字节流方法
字节输入流InputStream
主要方法:
read()
:从此输入流中读取一个数据字节,返回-1表示读取完毕。read(byte[] b)
:从此输入流中将最多b.length
个字节的数据读入一个 b 数组中,等同于下面的read(byte[] b, 0, b.length)
read(byte[] b, int off, int len)
:从此输入流中将最多 len 个字节的数据读入一个 b 数组中。close()
:关闭此输入流并释放与该流关联的所有系统资源。
字节输出流OutputStream
主要方法:
write(byte[] b)
:将 b.length 个字节从指定 byte 数组写入此文件输出流中。write(byte[] b, int off, int len)
:将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。write(int b)
:将指定字节写入此文件输出流。close()
:关闭此输入流并释放与该流关联的所有系统资源。
2.2、字符流方法
字符输入流Reader
主要方法:
read()
:读取单个字符。read(char[] cbuf)
:将字符读入数组。read(char[] cbuf, int off, int len)
: 将字符读入数组的某一部分。read(CharBuffer target)
:试图将字符读入指定的字符缓冲区。close()
:关闭此流,但要先刷新它。
字符输出流Writer
主要方法:
write(char[] cbuf)
:写入字符数组。write(char[] cbuf, int off, int len)
:写入字符数组的某一部分。write(int c)
:写入单个字符。write(String str)
:写入字符串。write(String str, int off, int len)
:写入字符串的某一部分。flush()
:刷新该流的缓冲。close()
:关闭此流,会先刷新缓冲区它。
三、使用示例
字节流
3.1、FileInputStream / FileOutputStream
文件字节流:实现文件复制的功能
public static String inputPicturePath = "C:\\Users\\Administrator\\Desktop\\1.png";
public static String outPicturePath = "C:\\Users\\Administrator\\Desktop\\2.png";// 实现文件复制功能,将inputPicturePath文件复制到outPicturePath路径。
public void fileStream() throws IOException {// 读取文件,这儿读取的是本地的图片FileInputStream fis = new FileInputStream(inputPicturePath);FileOutputStream fos = new FileOutputStream(outPicturePath);int readCount = 0;byte[] bytes = new byte[4];// while循环直到等于-1为止,表明读取结束。while ((readCount = fis.read(bytes)) != -1) {fos.write(bytes, 0, readCount);}fis.close();fos.close();}
3.2、DataOutputStream / DataInputStream / ByteArrayOutputStream / ByteArrayInputStream
数据流,是一个处理流,封装节点流操作
数组流常用场景:
-
内存中读取数据:
ByteArrayInputStream
允许从内存中的字节数组读取数据,而无需借助磁盘或网络等外部存储设备。这在某些情况下可以提高读取速度和效率,特别是当数据已经存在于内存中时。 -
方便数据传输: 使用
ByteArrayInputStream
,可以方便地将字节数组传递给需要输入流的方法或组件,而无需将字节数组写入磁盘或进行网络传输。这样可以简化代码,并避免不必要的数据复制和存储开销。 -
测试和调试: 在测试和调试过程中,可以使用
ByteArrayInputStream
来模拟输入流的行为,以便更容易对代码进行单元测试和调试。可以使用预定义的字节数组作为输入数据,并通过ByteArrayInputStream
提供给待测试的方法。 -
数据解析和处理: 有时,需要对二进制数据进行解析和处理。使用
ByteArrayInputStream
可以将字节数组转换为输入流,然后使用相应的读取方法从中读取数据。这对于处理二进制协议、解析图像或音频数据等场景非常有用。
/*** 数据处理流* <p>* ByteArrayStream 字节数组流** @return* @throws IOException*/public void dataStream() throws IOException {// 字节数组流ByteArrayOutputStream bos = new ByteArrayOutputStream();DataOutputStream dos = new DataOutputStream(bos);// 真正调用的是 ByteArrayOutputStream的 write写入到数组中去。dos.writeBoolean(true);dos.writeInt(99);dos.writeShort(111);dos.writeDouble(3.14);// 读取数据流ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());DataInputStream dis = new DataInputStream(bis);// 读取数据时,数据类型的顺序要与写入时一样。。。System.out.println(dis.readBoolean());System.out.println(dis.readInt());System.out.println(dis.readShort());System.out.println(dis.readDouble());}
3.3、ObjectOutputStream / ObjectInputStream
对象处理流,常用在JAVA对象的序列化与反序列化。
public void objectStream() throws IOException, ClassNotFoundException {Person person = new Person();person.setId("1");person.setName("哇哈哈");person.setAge(32);ByteArrayOutputStream bos = new ByteArrayOutputStream();// 创建对象流,这儿用的是字节数组流进行装饰。ObjectOutputStream oos = new ObjectOutputStream(bos);// 写入对象。oos.writeObject(person);oos.writeBoolean(true);oos.flush();oos.close();ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);// 获取对象。Person person1 = (Person) ois.readObject();boolean b = ois.readBoolean();System.out.println(b);System.out.println(JSONUtil.toJsonStr(person1));}@Datapublic static class Person implements Serializable {private String id;private String name;private Integer age;}
3.4、BufferedInputStream / BufferedOutputStream
缓冲处理流。内部设置了一个缓冲区,进行存储到内存中,减少直接读取的次数,以此来提高效率。
public void bufferedStream() throws IOException {// 读取文件FileInputStream fis = new FileInputStream(filePath);// 新建,用文件流来进行装饰。BufferedInputStream bis = new BufferedInputStream(fis);FileOutputStream fos = new FileOutputStream(bufferOutputFilePath);// 用缓冲流复制文件,输出到 bufferOutputFilePathBufferedOutputStream bos = new BufferedOutputStream(fos);int readCount = 0;byte[] buffer = new byte[4];while ((readCount = bis.read(buffer)) != -1) {bos.write(buffer, 0, readCount);}fis.close();bis.close();bos.flush();bos.close();fos.close();}
字符流
3.1、FileReader / FileWriter
文件字符流
public void fileStream() throws IOException {// 文件字符流读取FileReader fileReader = new FileReader(filePath);FileWriter fileWriter = new FileWriter(fileWriterPath);int readCount = 0;// 复制文件到 fileWriterPathwhile ((readCount = fileReader.read()) != -1) {fileWriter.append((char) readCount);}fileReader.close();fileWriter.close();}
3.2、BufferedReader / BufferedWriter
字符缓冲流,是个处理流,内部用具体的流进行装饰。
public void bufferStream() throws IOException {FileReader fr = new FileReader(filePath);// 用文件流进行装饰。BufferedReader br = new BufferedReader(fr);FileWriter fw = new FileWriter(bufferWriterPath);BufferedWriter bw = new BufferedWriter(fw);int readCount = 0;char[] buff = new char[4];// 复制文件while ((readCount = br.read(buff)) != -1) {bw.write(buff, 0, readCount);}bw.close();fw.close();br.close();fr.close();}
3.3、InputStreamReader / OutputStreamWriter
转换流,用于字节流转换为字符流。
public void fileInputStream() throws IOException {// 创建文件字节输入流FileInputStream fis = new FileInputStream(filePath);// 装饰InputStreamReader isr = new InputStreamReader(fis);char[] chars = new char[4];int readCount = 0;// 创建文件字符输出流FileOutputStream fos = new FileOutputStream(inputStreamWriterPath);OutputStreamWriter osw = new OutputStreamWriter(fos);while ((readCount = isr.read(chars)) != -1) {// 输出到文件,复制osw.write(chars, 0, readCount);}isr.close();fis.close();osw.close();fos.close();}
3.4、CharArrayReader / CharArrayWriter
字符数组流
public void charArrayStream() throws IOException {// 写入到数组中CharArrayWriter caw = new CharArrayWriter();caw.write("一");caw.write("二");caw.write("三");char[] charArray = caw.toCharArray();// 创建读取字符数组。CharArrayReader car = new CharArrayReader(charArray);int readCount = 0;while ((readCount = car.read()) != -1) {System.out.println((char) readCount);}car.close();caw.close();}
四、总结
1、字节流是原生的操作,字符流是处理之后的操作。
输入流都是:InputStream
和Reader
的子类。
输出流都是:OutputStream
和 Writer
的子类。