【Java】输入输出流(I/O流)的全面总结+图解

本文深入解析Java中的I/O流概念,包括输入输出流的基本原理、流的分类、节点流与处理流的区别,以及字节流、字符流、缓冲流、转换流、对象流的具体应用。同时,文章提供了丰富的代码示例,帮助读者理解如何在实际编程中运用这些流。

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

 

输入与输出简述

 
输入流(Inout Stream)与输出流(Output Stream)合称为数据流(Data Stream)

输入输出流的来源和接收者可以是文件、内存、网络连接等
 
写入数据的原理:Java程序→JVM→OS→OS调用写入数据的方法→写入成功→手动释放OS资源

读取数据的原理:Java程序→JVM→OS→OS调用读取数据的方法→读取成功→手动释放OS资源
在这里插入图片描述
 
 

Java流顶层父类

它们都是抽象类,是所有相关类的超类。
定义了输入流的一些共性方法:read(), close();以及输出流的一些共性方法:write(), flush(), close()

输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

 
 
 
 


io包中的类可分为两种:字节流(Byte Stream)和字符流(Character Stream)
字节流是最底层的,因而可以处理任何类型的文件。

文件字节流

一切皆为字节(文本、图片、视频),即皆为二进制数据。
 
字节输出流

// FileOutputStream有两种构造方法(第二个参数设为true,则不会【覆盖】而是【续写】)
FileOutputStream fos = new FileOutputStream("D:\\Demo\\loli.txt");
FileOutputStream fos = new FileOutputStream(new File("D:\\Demo\\loli.txt"));


// write有三种重载:
write(int b)							 ——— 如果0-127,则查询ASCII表;否则,查询GBK表
write(byte[] b)							 ——— 数组的值被byte类型限制在了-128-127.如果遇到负数,则与接下来的字节组成两个字节,并查询GBK中文显示
write(byte[] b, int off, int len)		 ——— 指定数组的某个范围

// 写入字符串								 
可以利用getBytes()方法将字符串转化为字节数组


// demo:

fos.write(97);												// a
byte[] bytes = {65, 66, 67};	fos.write(bytes);			// ABC
byte[] bytes = {-65, 66, 67};	fos.write(bytes);			// 緽C
byte[] bytes = {65, -66, 67};	fos.write(bytes);			// A綜
fos.write("萝莉".getBytes());								// 萝莉
fos.write("\r\n".getBytes());								// 换行(linux是/n, mac是/r)

 
字节输入流

// 同样的两种构造方法
FileInputStream fis = new FileInputStream("D:\\Demo\\loli.txt");
FileInputStream fis = new FileInputStream(new File("D:\\Demo\\loli.txt"));

// 一次读一个字节
int content = 0;
while((content = fis.read()) != -1) {
	System.out.println(char(content));					// 将 A, B, C, D, E 依次读取出来
}

// 一次读取多个字节
byte[] bytes = new byte[2];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
    System.out.println(new String(bytes, 0, len));		// 打印 AB, CD, E
}



// 注
1.读取字节时(每次read一次),指针会自动向后移动
2.read()方法的返回值就是读取的字节内容;read(byte[])方法的返回值是"有效字节的长度",读取的内容在byte[]数组中
3.byte[]数组的作用是"缓存"————我们可以控制每次读取字节的数量,并将读取的字节缓存在数组中。并根据"有效字节的长度"读取出来
  比如上面的ABCDE,如果一直读下去,这个缓冲数组的内容是:[A, B], [C, D], [E, D], [E, D]...对应的"有效字节的长度"2, 2, 1, -1...
  其实,随着指针的移动,每次新读取的字节都会"不完全覆盖"之前的内容————[C, D]->[E, D]时的D就是未被覆盖掉的。
4.对于一个字节,用char()转换类型就好;如果是个byte[]数组,可以用利用String()构造方法

在这里插入图片描述

 
练习——复制文件

FileInputStream fis = new FileInputStream(new File("D:\\Demo\\loli.jpg"));      // 读
FileOutputStream fos = new FileOutputStream(new File("D:\\Demo\\suki.jpg"));    // 写

int len = 0;
byte[] bytes = new byte[1024];					// 使用缓冲数组
while((len = fis.read(bytes)) != -1){			// 经典写法,必须背会
    fos.write(bytes, 0, len);					// write()方法正好可以用到读出的字节数组与有效长度len
}

fos.close();       // 先关闭写
fis.close();       // 再关闭读

 

 


文件字符流

字节流更适合底层的输入输出,字符流则更适合处理一个文本文件
 
字符输出流

// 同样(第二个参数设为true,则不会【覆盖】而是【续写】)
FileWriter fw = new FileWriter("D:\\Demo\\suki.txt");
FileWriter fw = new FileWriter(new File("D:\\Demo\\suki.txt"));

fw.write(int c)
fw.write(char[] cbuf)						// 字符串还需要toCharArray()
fw.write(char[] cbuf, int off, int len)
fw.write(String str)						// 字符串也可以直接写!
fw.write(String str, int off, int len)

fw.flush();
fw.close();


flush与close的区别:
首先要明白,FileWriter和OutputStream的write()方法是不同的———后者是直接写到文件中(因为是字节);后者是写到内存缓冲区,之后再刷新到文件中(因为是字符)
而flush和close都有立即把缓冲区的数据刷新到文件中的作用————而close还会释放资源,流对象无法再使用了

 
字符输入流

// 还是一样的两种构造方法
FileReader fr = new FileReader("D:\\Demo\\loli.txt");
FileReader fr = new FileReader(new File("D:\\Demo\\loli.txt"));

// 一次读一个字符
int content = 0;
while((content = fr.read()) != -1) {
	System.out.println(char(content));						// 萝 莉 s u k i
}

// 一次读取多个字符
char[] chars = new char[2];
int len = 0;
while ((len = fr.read(chars)) != -1) {
    System.out.println(new String(chars, 0, len));		   // 萝莉s uki
}

 

 


缓冲流

利用缓冲区实现。上面的四个文件节点流,就分别对应四个缓冲处理流。

---------------------- Demo: 字符输出缓冲流 -------------------------

BufferedWriter bw = new BufferedWriter(new FileWriter(new File("D:\\Demo\\loli.txt")));
bw.write("萝莉赛高");
bw.close();


---------------------- Demo: 字符输入缓冲流 -------------------------

BufferedReader br = new BufferedReader(new FileReader(new File("D:\\Demo\\loli.txt")));
String line = null;
while((line = br.readLine()) != null){					// 多了一个好用的readLine()方法
    System.out.println(line);
}
br.close();
不再赘述 "字节缓冲流" 完整demo,只给出连接流的过程:

BufferedInputStream bi = new BufferedInputStream(new FileInputStream(new File("D:\\Demo\\loli.txt")));

BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(new File("D:\\Demo\\loli.txt")));



注:write(),read()方法的多种重载,与上面所讲的完全一致

 

 


转换流

转换流是字节流流向字符流单向桥梁。是Reader的子类。

------------------ Demo: System.in是输入字节流,下面把它转化成字符流再用BufferedReader包装 -----------------------

BufferedReader bufferReader = new BufferedReader(new InputStreamReader(System.in));

String line = null;
while((line = bReader.readLine()) != null){
    if(line.equals("exit")){						// 如果输入"exit"就退出
        System.exit(1);
    }
	System.out.println("输入内容:" + line);			// 每次"回车"后,读取一行内容
}

bufferReader.close();

 

 


对象流

即序列化,作用是对象实例变量的状态保持

---------------------------------------- 序列化对象 ----------------------------------------------

Person person = new Person("Chino", 10);		// 这个对象必须是实现Serializable接口的

ObjectOutputStream objWriter = new ObjectOutputStream(new FileOutputStream(new File("D:\\Demo\\person.txt")));
objWriter.writeObject(person);
objWriter.close();

---------------------------------------- 反序列化对象 ----------------------------------------------

ObjectInputStream objReader = new ObjectInputStream(new FileInputStream(new File("D:\\Demo\\person.txt")));
Person person = (Object)objReader.readObject();
objReader.close();

在这里插入图片描述

 
 
 

I/O流大家庭

 

分类 字节输入流 字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
过滤流FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
数据流DataInputStreamDataOutputStream

注:

  1. 紫色 的是抽象基类,是无法new出对象的
  2. 粗体的流称为节点流,必须直接连接真实的物理节点;其他的称为处理流,作用是连接另一个流对其进行包装

 
 
 
 
 
 

 
 

 
 
 


 

拾遗

  1. 只知道"面相字节的流在处理汉字文本时很容易出现乱码问题,因此引入了字符流"是不够的,我们需要对【编码】有一个大致的了解: 戳这里:《【图文】有趣、通俗、严谨地解释ASCII、DBK、unicode、utf-8的区别(建议收藏)》
  2. Java中的字符使用utf-16(unicode的一种实现方式)编码,在第0平面内,所有字符都是严格的2字节,即码元为2字节
  3. 网络流Socketjava.net虽然不是java.io包中的,但也是字节流。这篇文章简单有趣的介绍了Socket流:《Head First Java》读书笔记(肆)
  4. 本文只是简单介绍了对象流,并未展开来说对象的序列化与反序列化。这篇文章同样是图解,并罗列了序列化的相关知识点:《《Head First Java》读书笔记(叁)》

 

 

 

 

 

 

 

 

 

 
End

By a Lolicon

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值