IO是Java中的一种输入和输出的功能,Java中对这种操作叫做对流的操作。
流代表的是任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象。
流的本质是数据传输,流不只是对文件可进行读写,还可以对内存、网络、程序操作。
整个Java IO包中最重要的就是5个类和一个接口。
5个类指:
* File:用于文件或者目录的描述信息,例如生成新的目录,修改文件名,删除文件,判断文件,过滤文件等
* OutputStream:抽象类,基于字节的输出操作,是所有输出流的父类。
* InputStream:抽象类,基于字节的输入操作,是所有输入流的父类。
* Writer:抽象类,基于字符的输出操作。
* Reader:抽象类,基于字符的输入操作。
一个接口指:Serializable
另外一个特殊的类:RandomAccessFile:随机文件操作,可以从文件任意位置进行存取(输入输出)操作。
IO接口和类的结构图可参考技术栈图
我们在对文件的操作过程中,除了使用字节流和字符流的方式之外,我们还可以使用RandomAcessFile这个工具类来实现。
RandomAccessFile可以实现对文件的读 和 写,但是他并不是继承于以上4中基本虚拟类。
而且在对文件的操作中,RandomAccessFile有一个巨大的优势,他可以支持文件的随机访问,程序快可以直接跳转到文件的任意地方来读写数据。所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
RandomAccessFile的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。
RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。
Java中有输入和输出两种IO流,每种输入输出流又分为字节流和字符流两大类。
- 关于字节:每个字节(byte)有8bit组成
- 关于字符:一个字符代表一个英文字母或一个汉字
Java采用unicode编码,2个字节表示1个字符
- 先进先出,最先写入输出流的数据最先被输入流读取到
- 顺序读取,不能随机访问数据(RandomAccessFile除外)
- 只读只写,每个流只能是输入流或输出流的一种
- 每次进行IO操作,要手动close,因为IO资源并不属于内存资源,并不会被GC回收
- 对于输出操作,flush()会刷新输出流,强制缓冲区中的输出字节被写出; close()关闭输出流,释放和这个流相关的系统资源,调用close()会自动flush
- 流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时
- 节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法
- 字节流与字符流区别
- 字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节
- 字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据
- 只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流
传统的 Socket 阻塞模式直接导致每个 Socket 都必须绑定一个线程来操作数据,参与通信的任意一方如果处理数据的速度较慢,则都会直接拖累另一方,导致另一方的线程不得不浪费大量的时间在 I/O 等待上,所以,每个 Socket 要绑定一个单独的线程正是传统Socket 阻塞模式的根本“缺陷”。之所以这里加了“缺陷”两个字,是因为这种模式在一些特定场合下效果是最好的,比如只有少量的 TCP 连接通信,双方都非常快速地传输数据,此时这种模式的性能最高。
现在我们可以开始分析“非阻塞”模式了,它就是要解决 I/O 线程与 Socket 解耦的问题,因此,它引入了事件机制来达到解耦的目的。我们可以认为 NIO 底层中存在一个 I/O 调度线程,它不断扫描每个 Socket 的缓冲区,当发现写入缓冲区为空(或者不满)的时候,它会产生一个Socket 可写事件,此时程序就可以把数据写入 Socket 里,如果一次写不完,则等待下次可写事件的通知;而当发现读取缓冲区里有数据的时候,它会产生一个 Socket 可读事件,程序收到这个通知事件时,就可以从 Socket 读取数据了。