好的,各位观众老爷们,欢迎来到“老司机带你飞:Java IO流的那些事儿”讲座现场!我是今天的主讲人,江湖人称“代码界的段子手”——老码农。今天咱们不搞那些高深莫测的理论,就用最接地气的方式,聊聊Java IO流这玩意儿。
开场白:IO流,程序与世界的桥梁
想象一下,你的程序就像一个住在深闺的小公举,而外面的世界,互联网也好,硬盘也好,就像一个充满诱惑的花花世界。小公举想出去浪,看看外面的风景,或者想把自己的美照分享到朋友圈,怎么办? 这时候,就需要一条通道,一座桥梁,连接程序和外部世界。
而IO流,就是这座桥梁,这条通道。它负责把数据从程序内部搬运到外部,或者把外部的数据搬运到程序内部。
第一幕:字节流,数据的原始搬运工
首先登场的是我们的老大哥——字节流。 它们是数据世界的原始搬运工,啥也不挑,啥都能搬,不管是图片、视频、文字、还是二进制文件,统统来者不拒。
-
什么是字节?
字节(byte)是计算机存储数据的基本单位,一个字节等于8个比特(bit)。你可以把它想象成一个最小的包裹,里面可以装8个开关,每个开关要么开(1),要么关(0)。
-
字节流的种类
字节流分为输入流(InputStream)和输出流(OutputStream)两大类。
-
InputStream(输入流): 负责从外部世界读取数据到程序内部。你可以把它想象成一个快递员,从快递站把包裹送到你家。
常用的InputStream子类:
- FileInputStream: 从文件中读取数据。
- ByteArrayInputStream: 从字节数组中读取数据。
- BufferedInputStream: 带缓冲的输入流,提高读取效率。
- ObjectInputStream: 从输入流中读取对象。
-
OutputStream(输出流): 负责把程序内部的数据写入到外部世界。 你可以把它想象成一个邮递员,把你家的包裹寄到快递站。
常用的OutputStream子类:
- FileOutputStream: 将数据写入到文件。
- ByteArrayOutputStream: 将数据写入到字节数组。
- BufferedOutputStream: 带缓冲的输出流,提高写入效率。
- ObjectOutputStream: 将对象写入到输出流。
-
-
字节流的使用姿势
使用字节流的一般步骤:
- 创建流对象: 根据你的需求,选择合适的InputStream或OutputStream子类,并指定数据源或目标。
- 读取/写入数据: 使用read()方法从输入流读取数据,或者使用write()方法将数据写入到输出流。
- 关闭流: 使用close()方法关闭流,释放资源。 这是一个非常重要的步骤,否则可能会导致资源泄露,甚至文件损坏!
举个栗子:
import java.io.*; public class ByteStreamDemo { public static void main(String[] args) { // 从文件读取数据 try (FileInputStream fis = new FileInputStream("input.txt"); FileOutputStream fos = new FileOutputStream("output.txt")) { //try-with-resources 自动关闭流 int data; while ((data = fis.read()) != -1) { // 处理读取到的数据 System.out.print((char) data); fos.write(data); //写入到文件 } } catch (IOException e) { e.printStackTrace(); } } }在这个例子中,我们使用FileInputStream从 "input.txt" 文件中读取数据,然后使用FileOutputStream将数据写入到 "output.txt" 文件中。 try-with-resources语句保证了流会被自动关闭,即使发生异常。
第二幕:字符流,文字的优雅使者
接下来登场的是我们的优雅使者——字符流。 它们专门负责处理字符数据,比如文本文件、网页等等。 字符流在字节流的基础上,增加了字符编码的支持,可以更好地处理各种语言的文字。
-
什么是字符?
字符(character)是人类可读的符号,比如字母、数字、汉字等等。 在计算机中,字符通常用Unicode编码表示。
-
字符流的种类
字符流也分为输入流(Reader)和输出流(Writer)两大类。
-
Reader(字符输入流): 负责从外部世界读取字符数据到程序内部。 可以把它想象成一个翻译官,把外文翻译成中文。
常用的Reader子类:
- FileReader: 从文件中读取字符数据。
- BufferedReader: 带缓冲的字符输入流,提高读取效率。
- InputStreamReader: 将字节输入流转换为字符输入流,可以指定字符编码。
-
Writer(字符输出流): 负责把程序内部的字符数据写入到外部世界。 可以把它想象成一个作家,把你的想法写成文字。
常用的Writer子类:
- FileWriter: 将字符数据写入到文件。
- BufferedWriter: 带缓冲的字符输出流,提高写入效率。
- OutputStreamWriter: 将字节输出流转换为字符输出流,可以指定字符编码。
- PrintWriter: 具有自动flush功能的字符输出流,方便输出各种类型的数据。
-
-
字符流的使用姿势
使用字符流的一般步骤和字节流类似:
- 创建流对象: 根据你的需求,选择合适的Reader或Writer子类,并指定数据源或目标。
- 读取/写入数据: 使用read()方法从输入流读取字符数据,或者使用write()方法将数据写入到输出流。
- 关闭流: 使用close()方法关闭流,释放资源。
举个栗子:
import java.io.*; public class CharStreamDemo { public static void main(String[] args) { // 从文件读取字符数据 try (BufferedReader br = new BufferedReader(new FileReader("input.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = br.readLine()) != null) { // 处理读取到的数据 System.out.println(line); bw.write(line); bw.newLine(); //写入一个换行符 } } catch (IOException e) { e.printStackTrace(); } } }在这个例子中,我们使用BufferedReader从 "input.txt" 文件中读取字符数据,然后使用BufferedWriter将数据写入到 "output.txt" 文件中。 BufferedReader的readLine()方法可以一次读取一行数据,BufferedWriter的newLine()方法可以写入一个换行符。
第三幕:字节流 vs 字符流,谁更胜一筹?
既然有了字节流,为什么还需要字符流呢? 它们之间到底有什么区别? 哪个更厉害?
咱们来做个对比:
| 特性 | 字节流 | 字符流 |
|---|---|---|
| 处理数据 | 字节(byte) | 字符(character) |
| 适用场景 | 所有类型的数据,包括二进制文件 | 文本文件、网页等字符数据 |
| 编码支持 | 不支持 | 支持,可以指定字符编码 |
| 效率 | 理论上更高(因为处理的数据更原始) | 某些情况下可能更高(因为有缓冲和编码优化) |
| 使用便捷性 | 相对复杂(需要自己处理字符编码) | 相对简单(自动处理字符编码) |
简单来说:
- 如果你要处理的是二进制文件,比如图片、视频、压缩包等等,那就用字节流。
- 如果你要处理的是文本文件,比如txt、html、java源代码等等,那就用字符流。
- 如果你不确定要处理什么类型的文件,那就用字节流,因为它更通用。
第四幕:缓冲流,IO的加速器
无论是字节流还是字符流,每次读取/写入数据都需要和磁盘或网络进行交互,这个过程非常耗时。 为了提高IO效率,Java提供了缓冲流。
-
什么是缓冲?
缓冲(buffer)是一块内存区域,用来临时存储数据。 缓冲流在读取/写入数据时,会先将数据存储到缓冲区中,当缓冲区满了之后,才会一次性地将数据写入到磁盘或网络,或者从磁盘或网络读取一批数据到缓冲区。 这样可以减少IO操作的次数,从而提高IO效率。
-
缓冲流的种类
- BufferedInputStream: 带缓冲的字节输入流。
- BufferedOutputStream: 带缓冲的字节输出流。
- BufferedReader: 带缓冲的字符输入流。
- BufferedWriter: 带缓冲的字符输出流。
-
缓冲流的使用姿势
使用缓冲流很简单,只需要在已有的流对象的基础上,套上一层缓冲流即可。
举个栗子:
import java.io.*; public class BufferedStreamDemo { public static void main(String[] args) { // 使用缓冲流从文件读取数据 try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) { byte[] buffer = new byte[1024]; // 缓冲区大小 int bytesRead; while ((bytesRead = bis.read(buffer)) != -1) { // 处理读取到的数据 bos.write(buffer, 0, bytesRead); } } catch (IOException e) { e.printStackTrace(); } } }在这个例子中,我们使用BufferedInputStream和BufferedOutputStream分别对FileInputStream和FileOutputStream进行了包装,从而提高了IO效率。
第五幕:转换流,编码的魔法师
有时候,我们需要在字节流和字符流之间进行转换。 比如,我们从网络上接收到的数据是字节流,但我们需要将其转换为字符流进行处理。 这时候,就需要用到转换流。
-
转换流的种类
- InputStreamReader: 将字节输入流转换为字符输入流,可以指定字符编码。
- OutputStreamWriter: 将字节输出流转换为字符输出流,可以指定字符编码。
-
转换流的使用姿势
使用转换流也很简单,只需要将字节流对象作为参数传递给转换流的构造方法即可。
举个栗子:
import java.io.*; public class ConvertStreamDemo { public static void main(String[] args) { // 使用转换流从字节流读取数据,并指定字符编码 try (FileInputStream fis = new FileInputStream("input.txt"); InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); // 指定UTF-8编码 BufferedReader br = new BufferedReader(isr)) { String line; while ((line = br.readLine()) != null) { // 处理读取到的数据 System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } }在这个例子中,我们使用InputStreamReader将FileInputStream转换为字符输入流,并指定了UTF-8编码。 这样就可以正确地读取包含中文的文本文件了。
总结:IO流,Java的基石
IO流是Java编程中非常重要的一个概念,它连接了程序和外部世界,使得程序可以读取和写入各种类型的数据。 掌握IO流的使用,是成为一名合格的Java程序员的必备技能。
- 字节流: 数据的原始搬运工,适用于所有类型的数据。
- 字符流: 文字的优雅使者,适用于文本文件等字符数据。
- 缓冲流: IO的加速器,可以提高IO效率。
- 转换流: 编码的魔法师,可以在字节流和字符流之间进行转换。
希望今天的讲座能够帮助大家更好地理解Java IO流。 记住,学好IO流,走遍天下都不怕!
彩蛋:IO流的常见坑
- 忘记关闭流: 这是一个非常常见的错误,会导致资源泄露,甚至文件损坏。 务必使用try-with-resources语句或者在finally块中关闭流。
- 字符编码问题: 如果你的程序需要处理中文或其他非ASCII字符,一定要注意字符编码的问题。 确保你的程序使用的字符编码和文件使用的字符编码一致。
- 缓冲区溢出: 如果你使用缓冲流,一定要注意缓冲区的大小。 如果缓冲区太小,可能会导致频繁的IO操作,降低效率。 如果缓冲区太大,可能会浪费内存。
- 文件路径问题: 在指定文件路径时,一定要注意相对路径和绝对路径的区别。 相对路径是相对于当前工作目录的路径,绝对路径是文件的完整路径。
好了,今天的讲座就到这里,感谢各位观众老爷的捧场! 咱们下期再见!