“老吴,Java IO 也太上头了吧?”新兵蛋子小三向头顶很凉快的老吴抱怨道,“你瞧,我就按照传输方式对 IO 进行了一个简单的分类,就能搞出来这么多的玩意!”

好久没搞过 IO 了,老吴看到这幅思维导图也是吃了一惊。 想想也是,他当初学习 Java IO 的时候头也大,乌央乌央的一片,全是类,估计是所有 Java 包里面类最多的,一会是 Input 一会是 Output,一会是 Reader 一会是 Writer,真不知道 Java 的设计者是怎么想的。

看着肺都快要气炸的小三,老吴深深地吸了一口气,耐心地对小三说:“主要是 Java 的设计者考虑得比较多吧,所以 IO 给人一种很乱的感觉,我来给你梳理一下。”

01、传输方式划分

就按照你的那副思维导图来说吧。

传输方式有两种,字节和字符,那首先得搞明白字节和字符有什么区别,对吧?

字节(byte)是计算机中用来表示存储容量的一个计量单位,通常情况下,一个字节有 8 位(bit)。

字符(char)可以是计算机中使用的字母、数字、和符号,比如说 A 1 $ 这些。

通常来说,一个字母或者一个字符占用一个字节,一个汉字占用两个字节。

具体还要看字符编码,比如说在 UTF-8 编码下,一个英文字母(不分大小写)为一个字节,一个中文汉字为三个字节;在 Unicode 编码中,一个英文字母为一个字节,一个中文汉字为两个字节。

PS:关于字符编码,可以看前面的章节:锟斤拷

明白了字节与字符的区别,再来看字节流和字符流就会轻松多了。

字节流用来处理二进制文件,比如说图片啊、MP3 啊、视频啊。

字符流用来处理文本文件,文本文件可以看作是一种特殊的二进制文件,只不过经过了编码,便于人们阅读。

换句话说就是,字节流可以处理一切文件,而字符流只能处理文本。

虽然 IO 类很多,但核心的就是 4 个抽象类:InputStream、OutputStream、Reader、Writer。

(抽象大法真好)

虽然 IO 类的方法也很多,但核心的也就 2 个:read 和 write。

InputStream 类

  • int read() :读取数据

  • int read(byte b[], int off, int len) :从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中

  • long skip(long n) :跳过指定个数的字节

  • int available() :返回可读的字节数

  • void close() :关闭流,释放资源

OutputStream 类

  • void write(int b) :写入一个字节,虽然参数是一个 int 类型,但只有低 8 位才会写入,高 24 位会舍弃(这块后面再讲)

  • void write(byte b[], int off, int len) :将数组 b 中的从 off 位置开始,长度为 len 的字节写入

  • void flush() :强制刷新,将缓冲区的数据写入

  • void close() :关闭流

Reader 类

  • int read() :读取单个字符

  • int read(char cbuf[], int off, int len) :从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中

  • long skip(long n) :跳过指定个数的字符

  • int ready() :是否可以读了

  • void close() :关闭流,释放资源

Writer 类

  • void write(int c) :写入一个字符

  • void write( char cbuf[], int off, int len) :将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入

  • void flush() :强制刷新,将缓冲区的数据写入

  • void close() :关闭流

理解了上面这些方法,基本上 IO 的灵魂也就全部掌握了。

二、操作对象划分

小二,你细想一下,IO IO,不就是输入输出(Input/Output)嘛:

  • Input:将外部的数据读入内存,比如说把文件从硬盘读取到内存,从网络读取数据到内存等等

  • Output:将内存中的数据写入到外部,比如说把数据从内存写入到文件,把数据从内存输出到网络等等。

所有的程序,在执行的时候,都是在内存上进行的,一旦关机,内存中的数据就没了,那如果想要持久化,就需要把内存中的数据输出到外部,比如说文件。

文件操作算是 IO 中最典型的操作了,也是最频繁的操作。那其实你可以换个角度来思考,比如说按照 IO 的操作对象来思考,IO 就可以分类为:文件、数组、管道、基本数据类型、缓冲、打印、对象序列化/反序列化,以及转换等。

1)文件

文件流也就是直接操作文件的流,可以细分为字节流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)。

FileInputStream 的例子:

int b;
FileInputStream fis1 = new FileInputStream("fis.txt");
// 循环读取
while ((b = fis1.read())!=-1) {System.out.println((char)b);
}
// 关闭资源
fis1.close();

FileOutputStream 的例子:

FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("小菜鸡".getBytes());
fos.close();

FileReader 的例子:

int b = 0;
FileReader fileReader = new FileReader("read.txt");
// 循环读取
while ((b = fileReader.read())!=-1) {// 自动提升类型提升为 int 类型,所以用 char 强转System.out.println((char)b);
}
// 关闭流
fileReader.close();

FileWriter 的例子:

FileWriter fileWriter = new FileWriter("fw.txt");
char[] chars = "小菜鸡".toCharArray();
fileWriter.write(chars, 0, chars.length);
fileWriter.close();

当掌握了文件的输入输出,其他的自然也就掌握了,都大差不差。

2)数组

通常来说,针对文件的读写操作,使用文件流配合缓冲流就够用了,但为了提升效率,频繁地读写文件并不是太好,那么就出现了数组流,有时候也称为内存流。

ByteArrayInputStream 的例子:

InputStream is =new BufferedInputStream(new ByteArrayInputStream("小菜鸡".getBytes(StandardCharsets.UTF_8)));
//操作
byte[] flush =new byte[1024];
int len =0;
while(-1!=(len=is.read(flush))){System.out.println(new String(flush,0,len));
}
//释放资源
is.close();

ByteArrayOutputStream 的例子:

ByteArrayOutputStream bos =new ByteArrayOutputStream();
byte[] info ="小菜鸡".getBytes();
bos.write(info, 0, info.length);
//获取数据
byte[] dest =bos.toByteArray();
//释放资源
bos.close();

3)管道

Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力。

一个线程通过 PipedOutputStream 写入的数据可以被另外一个线程通过相关联的 PipedInputStream 读取出来。

final PipedOutputStream pipedOutputStream = new PipedOutputStream();
final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
​
Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {try {pipedOutputStream.write("小菜鸡".getBytes(StandardCharsets.UTF_8));pipedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
});
​
Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {byte[] flush =new byte[1024];int len =0;while(-1!=(len=pipedInputStream.read(flush))){System.out.println(new String(flush,0,len));}
​pipedInputStream.close();} catch (IOException e) {e.printStackTrace();}
​}
});
thread1.start();
thread2.start();

4)基本数据类型

基本数据类型输入输出流是一个字节流,该流不仅可以读写字节和字符,还可以读写基本数据类型。

DataInputStream 提供了一系列可以读基本数据类型的方法:

DataInputStream dis = new DataInputStream(new FileInputStream(“das.txt”)) ;
byte b = dis.readByte() ;
short s = dis.readShort() ;
int i = dis.readInt();
long l = dis.readLong() ;
float f = dis.readFloat() ;
double d = dis.readDouble() ;
boolean bb = dis.readBoolean() ;
char ch = dis.readChar() ;

DataOutputStream 提供了一系列可以写基本数据类型的方法:

DataOutputStream das = new DataOutputStream(new FileOutputStream(“das.txt”));
das.writeByte(10);
das.writeShort(100);
das.writeInt(1000);
das.writeLong(10000L);
das.writeFloat(12.34F);
das.writeDouble(12.56);
das.writeBoolean(true);
das.writeChar('A');

5)缓冲

CPU 很快,它比内存快 100 倍,比磁盘快百万倍。那也就意味着,程序和内存交互会很快,和硬盘交互相对就很慢,这样就会导致性能问题。

为了减少程序和硬盘的交互,提升程序的效率,就引入了缓冲流,也就是类名前缀带有 Buffer 的那些,比如说 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。

缓冲流在内存中设置了一个缓冲区,只有缓冲区存储了足够多的带操作的数据后,才会和内存或者硬盘进行交互。简单来说,就是一次多读/写点,少读/写几次,这样程序的性能就会提高。

6)打印

恐怕 Java 程序员一生当中最常用的就是打印流了: System.out 其实返回的就是一个 PrintStream 对象,可以用来打印各式各样的对象。

System.out.println("小菜鸡是真的二!");

PrintStream 最终输出的是字节数据,而 PrintWriter 则是扩展了 Writer 接口,所以它的 print()/println() 方法最终输出的是字符数据。使用上几乎和 PrintStream 一模一样。

StringWriter buffer = new StringWriter();
try (PrintWriter pw = new PrintWriter(buffer)) {pw.println("小菜鸡");
}
System.out.println(buffer.toString());

7)对象序列化/反序列化

序列化本质上是将一个 Java 对象转成字节数组,然后可以将其保存到文件中,或者通过网络传输到远程。

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {output.writeUTF("小菜鸡");
}
System.out.println(Arrays.toString(buffer.toByteArray()));

与其对应的,有序列化,就有反序列化,也就是再将字节数组转成 Java 对象的过程。

try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("Person.txt")))) {String s = input.readUTF();
}

8)转换

InputStreamReader 是从字节流到字符流的桥连接,它使用指定的字符集读取字节并将它们解码为字符。

InputStreamReader isr = new InputStreamReader(new FileInputStream("demo.txt"));
char []cha = new char[1024];
int len = isr.read(cha);
System.out.println(new String(cha,0,len));
isr.close();

OutputStreamWriter 将一个字符流的输出对象变为字节流的输出对象,是字符流通向字节流的桥梁。

File f = new File("test.txt") ;
Writer out = new OutputStreamWriter(new FileOutputStream(f)) ; // 字节流变为字符流
out.write("hello world!!") ;    // 使用字符流输出
out.close() ;

“小三啊,你看,经过我的梳理,是不是感觉 IO 也没多少东西!针对不同的场景、不同的业务,选择对应的 IO 流就可以了,用法上就是读和写。”老吴一口气讲完这些,长长的舒了一口气。

此时此刻的小三,还沉浸在老吴的滔滔不绝中。不仅感觉老吴的肺活量是真的大,还感慨老吴不愧是工作了十多年的“老油条”,一下子就把自己感觉头大的 IO 给梳理得很清晰了。

《Java 程序员进阶之路》专栏,风趣幽默、通俗易懂,对 Java 初学者极度友好和舒适

卧槽,这也真的太上头了吧相关推荐

  1. 【Pygame实战】嗷大喵历险记之程序员吸猫指南:真的太上头了~

    导语 哈喽~大家好,我是木子,首先今天木子先给大家讲个小故事: 在喵界有这样一只网红--混迹于二次元.表情包界,贱萌活泼,调皮机灵,白色的大圆脸,脖子 上系了个铃铛,年龄不详,传说可于儿童.少年.青年 ...

  2. 研究生走私实验材料被海关查获!为了毕业,真的太难了...

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文募格学术撰写.参考资料:澎湃新闻.微博.募格学术此前报道等. 6 ...

  3. cad缩放工具怎么用_小米电视怎么投屏?这个投屏工具真的太好用啦!

    原标题:小米电视怎么投屏?这个投屏工具真的太好用啦! 大家最近有看什么优质的电影吗?张艺谋的<一秒钟>.李霄峰的<风平浪静>等等都挺好看的,我们可以在别人的故事里有所感悟,这或 ...

  4. 妹妹,这回哥哥听你的了,以前哥哥真的太傻

    上次半夜接到别人的电话,我还很激动,和我妹妹说了,说我等人家回来,结果被我妹妹骂了一顿,说我是个傻蛋,笨猪,实在被别人利用我的缺点在利用我.这回我决定听我妹妹的话,彻底解决了这件事. 彻底的放弃,彻底 ...

  5. 俞敏洪老师的回复真的太糟糕了!

    微信又改版了,为了方便第一时间看到我们的推送,请按照下列操作,设置"置顶":点击上方蓝色字体"程序员之家"-点击右上角"-"-点击" ...

  6. kakaotalk语音验证码,已读怎么破?KakaoTalk超实用的隐藏功能,真的太好用了!

    原标题:已读怎么破?KakaoTalk超实用的隐藏功能,真的太好用了! 韩语菌每天都会发一篇学习文,来看看今天被翻牌的是谁? 你们想看什么,我们就发什么!欢迎留言区评论~ ▼ 카카오톡KakaoTal ...

  7. 【web前端特效源码】使用 HTMLCSSJavaScript实现各种跳跃浮动慢跑翻转旋转坠落的魔幻文字动画效果~太上头了~/动画效果|前端开发|IT软件开发基础入门教程|网页制作|网站开发定制

    b站视频演示效果: [web前端特效源码]使用 HTML&CSS&JavaScript实现各种跳跃浮动慢跑翻转旋转坠落的魔幻文字动画效果~太上头了~/动画效果|前端开发|IT软件开 效 ...

  8. 将 Linux 移植到 M1 Mac 真的太难了!

    ????????关注后回复 "进群" ,拉你进程序员交流群???????? [CSDN 编者按]自去年苹果自研 M1 芯片发布之后,激发了无数用户的体验热情,与此同时,也吸引大批开 ...

  9. 计算机电源卡扣,酷冷至尊魔方NR200 ITX机箱体验,那个机箱电源开关插头上的卡扣,真的太好用了,应当普及!...

    酷冷至尊魔方NR200 ITX机箱体验,那个机箱电源开关插头上的卡扣,真的太好用了,应当普及! 2020-12-09 14:45:34 1点赞 5收藏 1评论 创作立场声明:以上为个人感受,然而每个人 ...

最新文章

  1. python入门代码示例-这可能是最好玩的python GUI入门实例!
  2. SAP发布S4/HANA 意义超过R3
  3. 【数据竞赛】Kaggle实战之特征工程篇-20大文本特征(上)
  4. History of pruning algorithm development and python implementation(finished)
  5. python gil锁存在的意义_关于python的GIL全局解释器锁的简单理解
  6. 漫画:假装内卷,才是互联网人的骚操作
  7. 介绍自己以及github注册流程
  8. 数据库主键从某个值开始自增
  9. python爬虫步骤-Python爬虫的步骤和工具
  10. python3 将列表中元素转化为字典_软件测试学Python(七):Python中的变量和标准数据类型...
  11. 检测手机屏幕是否亮屏解锁
  12. win7下使用U盘安装Ubuntu Kylin完全详解教程
  13. PySpark : Structured Streaming
  14. 数据库分页LIMIT
  15. 哈工大计算机系统实验三——二进制炸弹
  16. 网页携带参数,将参数通过网址传入到对应的页面输入框内
  17. 怎样在python安装open cv_python 安装+open cv
  18. 名帖292 张瑞图 行书《论书卷》
  19. 实时监控Mysql等数据库变化_进行数据同步_了解Maxwell_--MaxWell工作笔记001
  20. [网盘工具/百度网盘]秒传链接的使用 -2022版油猴网页脚本

热门文章

  1. 建设一个SaaS平台需要知道什么,做什么(附多图)
  2. 日语流行口语极短句2
  3. VSCode格式化XML
  4. 服贸会在京举行|淘宝直播携手佳能佳直播联合发布《电商直播高画质开播指南》让品质直播触手可及...
  5. CentOS7.5安装oracle11g
  6. 数据结构——竞选海报
  7. 智慧社区综合信息服务平台,让你的社区更智能
  8. 存储虚拟化的特性和优势
  9. 微信设置文字大小影响网页布局
  10. ORB_VI思想框架