java面试io模型详解(java学习基本IO知识)
java面试io模型详解(java学习基本IO知识)ByteArrayInputStream如何使用类功能构造器
用户进程发起请求,内核接收到请求后,从I/O设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间,该用户进程获取到数据后再响应客户端。
数据输入到buffer需要时间,从buffer复制数据至进程也需要时间,根据在这两段时间内等待方式不同,I/O动作可分为五种模式
- 阻塞I/O(Blocking I/O)
- 非阻塞I/O(Non-Blocking I/O)
- I/O复用(I/O Multiplexing)
- 信号驱动I/O
- 异步I/O
java的I/O操作在类的java.IO包中
- 基于字节操作的I/O接口: InputStream和OutputStream
- 基于字符操作的I/O接口: Writer和Reader
- 基于磁盘操作的I/O接口: File
- 基于网络操作的I/O接口: Socket(网络编程,不在io包中)
- 字节流对应原生的二进制数据
- 字符流对应字符数据,会自动处理与本地字符集之间的转换
- 缓冲流可以提高性能,通过减少底层API的调用次数来优化IO
字节输入流都继承自InputStream,InputStream表示从不同数据源产生输入的类,这些数据源包括
- 字节数组
- String对象
- 文件
- 管道,从一端输入,从另一端输出
- 一个由其他种类的流组成的序列,然后把它们汇聚为一个流
- 其他数据源,如Internet连接
类 |
功能 |
构造器 |
如何使用 |
ByteArrayInputStream |
允许将内存的缓冲区当做InputStream使用 |
缓冲区,字节将从中取出 |
将其与FilterInputStream对象相连以提供有用接口 |
StringBufferInputStream(已废除) |
将String转换为InputStream |
字符串。底层实现实际使用StringBuffer |
将其与FilterInputStream对象相连以提供有用接口 |
FileInputStream |
用于从文件中读取信息 |
字符串,表示文件名、文件或FileDescriptor对象 | |
PipedInputStream |
产生用于写入相关PipedOutputStream的数据。实现管道化概念 |
PipedOutputStream |
作为多线程中的数据源 |
SequenceInputStream |
将两个或多个InputStream对象转换成一个InputStream |
两个InputStream对象或一个容纳InputStream对象的容器Enumeration | |
FilterInputStream |
抽象类,作为装饰器的接口,为其他的InputStream类提供有用的功能 |
FilterInputStream类型子类包括以下几种
类 |
功能 |
构造器 |
如何使用 |
DataInputStream |
与DataOutputStream搭配使用,按照移植方式从流读取基本数据类型 |
InputStream |
包含用于读取基本数据类型的全部接口 |
BufferedInputStream |
使用它可以防止每次读取时都得进行实际写操作 |
InputStream |
本质上不提供接口,只是向进程添加缓冲功能 |
LineNumberInputStream(已废除) |
跟踪输入流的行号,可调用getLineNumber()和setLineNumber(int) |
InputStream,可以指定缓冲区大小 |
仅增加了行号 |
PushbackInputStream |
具有能弹出一个字节的缓冲区,因此可以将读到的最后一个字符回退 |
InputStream |
通常作为编译器的扫描器 |
// 每次读取一个字节
public abstract int read() throws IOException
// 使用字节数组作为缓冲区,将读到的字节填充至缓冲区中
public int read(byte b[]) throws IOException
// 将读到的字节填充至字节数组的指定位置上
// off 起始位置
// len 长度
public int read(byte b[] int off int len) throws IOException
关闭流
使用close方法来关闭流,在1.7及之后建议使用try-with-resources语句来使用流,这样可以避免显示的调用close()方法,减少一些重复代码
public void close() throws IOException
跳过指定字节
可以使用skip方法来跳过指定数目的字节,相当于把流中当前读到的位置向后移动若干个字节。
public long skip(long n) throws IOException
注意:该方法可能在向后移动的时候,没有达到指定字节就到达了流的末尾,所以并不一定会跳过指定的字节,该方法返回值为实际跳过的字节数
标记与重置标记与重置方法一般联合使用,以实现流中部分内容可重复读取
// mark方法在当前读取位置进行标记
// readlimit表示允许重复读取的字节,只能从标记的位置再次重复向后读取所指定的字节
public synchronized void mark(int readlimit)
// reset方法将流的当前读取位置移动到上次标记的位置
public synchronized void reset() throws IOException
// 不是所有的流都支持标记功能,需要使用该方法来判断当前流是否支持标记功能
public boolean markSupported()
可读字节数
当read方法被调用时,如果当前流中没有可用的数据,该操作就会被阻塞,available()方法就是返回当前流中还有多少字节可以读取
public int available() throws IOException
输出流
字节输入流都继承自OutputStream,该类决定了输出所要去的目标,字节数组、文件或管道
类 |
功能 |
构造器 |
如何使用 |
ByteArrayOutputStream |
在内存中创建缓冲区。所有送往流的数据都要放置在此缓冲区 |
缓冲区初始大小 |
用于指定数据的目的地 |
FileOutputStream |
用于将信息写入文件 |
字符串,表示文件名、文件或FileDescriptor对象 | |
PipedOutputStream |
任何写入其中的信息都会自动作为相关PipedInputStream的输出,实现管道化概念 |
PipedInputStream |
指定用于多线程的数据的目的地 |
FilterOutputStream |
抽象类,作为装饰器的接口,为其他OutputStream提供有用的功能 |
FilterOutputStream类型子类包括
类 |
功能 |
构造器 |
如何使用 |
DataOutputStream |
与DataInputStream搭配使用,可以按照移植方式向流中写入基本数据类型 |
OutputStream |
包含用于写入基本数据类型的全部接口 |
PrintStream |
用于产生改格式化输出。其中DataOutputStream处理数据的存储,PrintStream处理显示 |
OutputStream,可以用boolean值指示是否每次换行时清空缓冲区 |
应该是对OutputStream对象的final封装 |
BufferedOutputStream |
使用它以避免每次发送数据时都进行实际的写操作,可以调用flush()清空缓冲区 |
OutputStream,可以指定缓冲区大小 |
只是向进程添加缓冲功能 |
// 每次写入一个字节
public abstract void write(int b) throws IOException
// 使用字节数组作为缓冲区,写入整个字节数组的内容
public void write(byte b[]) throws IOException
// 写入字节数组的部分内容
// off 起始位置
// len 长度
public void write(byte b[] int off int len) throws IOException
关闭流
使用close方法来关闭流,在1.7及之后建议使用try-with-resources语句来使用流,这样可以避免显示的调用close()方法,减少一些重复代码
public void close() throws IOException
刷新
flush()方法用来强制要求OutputStream对象将暂存于内部缓冲区的数据立即进行实际的写入(一般情况下不需要手动的调用该方法,在内部缓冲区填充满了之后,会自动执行实际的写入操作,在调用close()方法时也会自动调用flush()方法)
有些OutputStream类中维护内部缓冲区是为了减少实际的写入操作次数,来提升性能
public void flush() throws IOException
字符流
InputStream和OutputStream是面向字节I/O的,而Reader和Writer则提供兼容Unicode和面向字符I/O的功能,InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer
RandomAccessFile类适用于由大小已知的记录组成的文件,可以使用seek()将文件指针从一条记录移动到另一条记录,然后对记录进行读取和修改。文件中记录的大小不一定相同,只要我们能确定那些记录有多大以及它们在文件中的位置即可。
RandomAccessFile类不是InputStream或者OutputStream的子类,只是实现了DataInput和DataOutput接口,没有使用InputStream和OutputStream的任何功能,所有方法都是独立的,大部分为native方法
常用的IO操作缓冲输入文件想要打开一个文件进行字符输入,可以使用FileReader对象,然后传入一个String或者File对象作为文件名。为了提高速度,对文件进行缓冲,可以将所产生的引用传递给一个BufferedReader构造器。BufferedReader提供了lines()方法,会产生一个Stream对象
public static void read(String file) {
try(BufferedReader bufferedReader = new BufferedReader(new FileReader(file))){
String line = null;
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
throw new RuntimeException("读取失败" e);
}
}
读取字符
public static void read() throws IOException {
StringReader stringReader = new StringReader("qaw试试");
int c;
while ((c = stringReader.read()) != -1) {
System.out.println((char)c);
}
}
StringReader的read方法是以int形式返回的下一个字节,所以打印的时候类型必须转为char
格式化数据要读取格式化数据,可以使用DataInputStream,是面向字节的,所以要使用InputStream类
文件输出FileWrite对象用于向文件写入数据,通常使用BufferedWriter将其包装起来增加缓冲的功能,
try(BufferedReader in = new BufferedReader(new StringReader("1111111111111111\n2222222222222\n3333333333"));
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter("test.txt")))
){
in.lines().forEach(printWriter :: println);
} catch (IOException e) {
e.printStackTrace();
}
标准IO
标准输入流Systom.in、标准输出流System.out、标准错误流Sytem.err。
System.out和System.err是预先包装成了PrintStream对象,但是System.in没有进行包装,属于原生的InputStream,所以在读取时需要对其进行包装
标准输入通常一行一行地读取输入,将System.in包装成BufferedReader
public static void main(String[] args) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.equals("end")) {
break;
}
System.out.println(line);
}
} catch (IOException e) {
}
}
重定向标准I/O
System类提供了简单的静态方法调用,重定向标准输入流、标准输出流和标准错误流
- setIn(InputStream)
- setOut(PrintStream)
- setErr(PrintStream)
public static void main(String[] args) {
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C:\\Users\\sinosoft\\Desktop\\剩余工作.txt"));
PrintStream printStream = new PrintStream(new BufferedOutputStream(new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\剩余工作副本.txt")))
){
System.setIn(bufferedInputStream);
System.setOut(printStream);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.equals("end")) {
break;
}
System.out.println(line);
}
} catch (IOException e1) {
}
}catch (IOException e){
}
}