详细地址:https://www.ibm.com/developerworks/cn/java/j-lo-javaio/
1、概念
输入输出是相对于内存设备而言的。
输入流:把能够读取一个字节序列的对象称为输入流;
输出流:把能够写一个字节序列的对象称为输出流
输入流和输出流中间连接着内存,输入流和输出流将读写分离开来进行操作,先从外设读入内存,然后再写出内存转移到其他外设
2、分类
基于字节操作的 I/O 接口:InputStream 和 OutputStream
基于字符操作的 I/O 接口:Writer 和 Reader
基于磁盘操作的 I/O 接口:File
基于网络操作的 I/O 接口:Socket
3、字节流
用字节流处理字符数据可能会有编码问题,因为字节流是以字节为单位,没有编码,而字符流是以字符为单位传送数据,字符流即以字节流+编码
两个顶层父类 (抽象类)及实现类:
InputStream(读入内存) :所有字节输入流相关类的父类
OutputStream(写出内存):所有和输出字节流相关类的父类
4、字符流
两个顶层父抽象类及其实现类
Reader:for reading character streams
Writer:for writing to character streams (字符流的写操作基本上后面都需要进行flush()操作)
1、输入输出是相对于内存设备而言的。
输入流:把能够读取一个字节序列的对象称为输入流;
输出流:把能够写一个字节序列的对象称为输出流
输入流和输出流中间连接着内存,输入流和输出流将读写分离开来进行操作,先从外设读入内存,然后再写出内存转移到其他外设
5、字节流和字符流的转化
InputStreamReader 类是字节到字符的转化桥梁,InputStream 到 Reader 的过程要指定编码字符集,否则将采用操作系统默认字符集,很可能会出现乱码问题。
StreamDecoder 正是完成字节到字符的解码的实现类。
6、磁盘IO
将数据持久化到物理磁盘,下面将介绍如何将数据持久化到物理磁盘的过程。
当传入一个文件路径,将会根据这个路径创建一个 File 对象来标识这个文件,然后将会根据这个 File 对象创建真正读取文件的操作对象,这时将会真正创建一个关联真实存在的磁盘文件的文件描述符 FileDescriptor,通过这个对象可以直接控制这个磁盘文件。由于我们需要读取的是字符格式,所以需要 StreamDecoder 类将 byte 解码为 char 格式,至于如何从磁盘驱动器上读取一段数据,由操作系统帮我们完成。
7、Socket
主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。
8、NIO 的工作方式(非阻塞式IO)
9、IO调优
①磁盘 I/O 优化
提升磁盘 I/O 性能通常的方法有:
- 增加缓存,减少磁盘访问次数
- 优化磁盘的管理系统,设计最优的磁盘访问策略,以及磁盘的寻址策略,这里是在底层操作系统层面考虑的。
- 设计合理的磁盘存储数据块,以及访问这些数据块的策略,这里是在应用层面考虑的。如我们可以给存放的数据设计索引,通过寻址索引来加快和减少磁盘的访问,还有可以采用异步和非阻塞的方式加快磁盘的访问效率。
- 应用合理的 RAID 策略提升磁盘 IO,RAID数据被平均写到多个磁盘阵列中,写数据和读数据都是并行的。
②网络 I/O 优化
- 一个是减少网络交互的次数
- 减少网络传输数据量的大小:减少网络数据量的办法通常是将数据压缩后再传输
- 尽量减少编码:通常在网络 I/O 中数据传输都是以字节形式的,也就是通常要序列化。但是我们发送要传输的数据都是字符形式的,尽量提前将字符转化为字节,或者减少字符到字节的转化过程。
- 根据应用场景设计合适的交互方式:所谓的交互场景主要包括同步与异步阻塞与非阻塞方式
同步:
所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。
异步:
不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。我们可以用打电话和发短信来很好的比喻同步与异步操作。
同步能够保证程序的可靠性,而异步可以提升程序的性能。
阻塞与非阻塞:主要是从 CPU 的消耗上来说的。
阻塞:CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。
非阻塞:在这个慢的操作在执行时 CPU 去干其它别的事,等这个慢的操作完成时,CPU 再接着完成后续的操作,但线程切换次数增加。
面试题:简述一下将文件中的数据输入到另一个文件中的步骤
1.首先创建File对象,并且和需要操作的文件相关联,这时候需要对文件进行判断是否存在,不存在则会报错
2.既然是读取文件并且写到文件,属于纯文本,可以选择FileReader和FileWriter进行读写操作,如果出现乱码可以使用其父类指定编码方式
3.创建FileReader对象用于读取文件中的数据,这里可以使用缓冲流进行处理,提高效率,创建一个BufferedReader对象
BufferedReader buffr = new BufferedReader(new InputStreamReader(new FileReader(file)));
4.创建FileWriter,同上使用缓存
public class Demo6 {
public static void main(String[] args){
File origin = new File("d:/helloWorld.txt");
File destination = new File("d:/helloWorld6.txt");
InputStreamReader in = null;
OutputStreamWriter out = null;
BufferedReader reader = null;
BufferedWriter writer = null;
if (!origin.exists()){
try {
origin.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(origin),"ISO-8859-1"));
// reader = new BufferedReader(new FileReader(origin));
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destination),"ISO-8859-1"));
String line = null;
while ((line = reader.readLine())!=null){
writer.write(line);
writer.newLine();
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader!= null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (writer!=null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}