IO也就是Input/Output
,数据拿到计算机内存中的过程即为输入,反之,数据从内存输出到外部存储(可以是远程主机、磁盘、数据库等)的过程即为输出。数据传输过程类似于水流,因此称作IO流。IO流在Java中分为输出流和输入流,根据数据的处理方式又分为字节流和字符流。(这里的输入输出是以程序为中心的,输入指程序接收输入,输出指程序把数据输出到外部存储)
Java IO流
Java IO流有四个基类,分别是输入流InputStream
(字节输入流),Reader
(字符输入流),OutputStream
(字节输出流),Writer
(字符输出流),其余的IO相关类都是派生于这四个抽象基类。
字节流与字符流
字节流:
- 以字节为单位处理数据,适用于处理二进制数据
- 直接操作字节,不涉及编码转换,可以处理任何类型的数据
字符流:
- 以字符为单位处理数据,适合处理文本数据
- 自动处理字符编码和解码(将字节传为字符)
- 性能逊于字节流处理,因为还有编解码消耗
- 对于不知道编码类型的数据,使用字节流处理会带来乱码问题,而使用字符流就不会出现这样的问题
字节流
InputStream
InputStream
用于从源读取字节流到内存中,它是一个抽象类,是所有字节输入流的父类。
常用方法
-
read()
:返回输入流中下一个字节的数据,如果未读取任何字节,返回-1
,表示结束 -
read(byte b[])
:从输入流中读取一些字节放到字节数组b
中,如果数组b
的长度为0,则不读取,如果没有可以读取的字节,返回-1
。最多可以读b.length
个字节,返回读取的字节数 -
read(byte b[], int off, int len)
:off
是偏移量,len
是指定的要读取的最大字节数,其余与read(byte b[])
一致(这里的偏移量off
是针对字节数组b
的,加入偏移为2,则从b
的第3个下标开始填充) -
skip(long n)
:忽略输入流中的n
个字节,返回实际忽略的字节数 -
avaliable()
:返回输入流中可以读取的字节数 -
close()
:关闭输入流并释放资源 -
readAllBytes()
:读取输入流中的所有字节,返回字节数组 -
readNBytes(byte[] b, int off, int len)
:阻塞直到读取len
个字节 -
transferTo(OutputStream out)
:将所有字节流从一个输入流传递到一个输出流,输出流自动写入
使用的输入文件为text.txt
:
hello,world!
sjska
12345678910
qpwoeiruty
代码语言:javascript复制String filePath = "text.txt";
FileInputStream file = new FileInputStream(filePath);
System.out.println(file.available());
// 输出输入流中可以读取的字节数 44
byte[] bytes = new byte[10];
file.read(bytes);
for (byte b : bytes){
System.out.println(b);
System.out.println(Character.toChars(b));
}
// 输出bytes数组的内容,依次输出 104 h 101 e 108 l 108 l 111 o 44 , 119 w 111 o 114 r 108 l
int read = file.read();
System.out.println(Character.toChars(read));
// 读取了一个字节,输出 d
byte[] bytes1 = new byte[7];
file.read(bytes1, 2,5);
for(byte b : bytes1){
System.out.println(b);
System.out.println(Character.toChars(b));
}
// 读取5个字节,并且偏移量为2,输出bytes1数组的内容,依次输出
// 0 0 33 ! 13 10 115 s 106 j
// bytes1数组的前两个字节偏移了,为空,13和10分别表示换行符和回车符
// 跳过4个字节
file.skip(4);
file.read(bytes);
for(byte b : bytes){
System.out.println(b);
System.out.println(Character.toChars(b));
}
// 再读取10个字节存在bytes中,其内容为
// 10 49 1 50 2 51 3 52 4 53 5 54 6 55 7 56 8 57 9
// 第一个10是回车符,它之前的换行符已经被skip跳过了
// 之后的1~9为存储的文本,49~57为其对应的ASCII码
String outFilePath = "text1.txt"
OutputStream fileOut = new FileOutputStream("outFilePath");
// 将输入流file中的字节全部放入输出流fileOut
file.transferTo(fileOut);
// 如果没有执行transferTo方法,这里读取输入流中剩余全部字符放在返回的字符数组中
// 但是执行了transferTo,输入流中已经没有字节了,所以什么都没读到
byte[] bytes2 = file.readAllBytes();
for(byte b : bytes2){
System.out.println(b);
System.out.println(Character.toChars(b));
}
输出文件text1.txt
:
10
qpwoeiruty
OutputStream
字节输出流,将字节输出到指定地方(文件等地),OutputStream
是所有字节输出流的父类。
常用方法
-
write(int b)
:将特定字节写入到输出流 -
write(byte b[])
:将字节数组b
写入到输出流 -
write(byte b[], int off, int len)
:增加了off
偏移量以及len
(要写入的最大字节数),与字节输入流相同,这里的off
也是对于字节数组b
来说 -
flush()
:刷新此输出流并强制写出所有缓冲的输出字节 -
close()
:关闭输出流并释放资源
FileOutputStream
是使用最多的字节输出流对象,用于将字节写入到文件中,当调用write
方法的时候,首先将数据写入到FileOutputStream
的内存缓冲区,当缓冲区满、手动调用flush
方法、手动调用close
方法(其实也是触发了flush
方法的调用)、程序退出触发close
方法时,才会把数据写入到文件中。
String filePath = "test.txt";
FileOutputStream fis = new FileOutputStream(filePath);
// 将 Z 写入到输出流
fis.write(90);
// 往输出流写入换行符
fis.write(10);
// 网输出流写入回车
fis.write(13);
// 往输出流写入 hello,world!
byte[] bytes = new String("hello,world!").getBytes();
fis.write(bytes);
fis.write(10);
// 偏移量为3,网输出流写入字节数组
fis.write(bytes,3,9);
得到的输出文件`test.txt”:
代码语言:javascript复制Z
hello,world!
lo,world!
字符流
基于字节流的IO若不知道编码方式就容易出现乱码问题,字符流对象方便我们对字符进行流操作,对于音频、视频、图片等媒体文件建议使用字节流进行处理,而对于文本文件建议使用字符流进行处理。
字符流默认采用的编码方式是Unicode
编码。
Reader
Reader
用于从文件读取字符流到内存,它是所有字符输入流的父类。
常用方法
-
read()
:从输入流读取一个字符 -
read(char[] cbuf)
:用于从输入流读取字符到字符数组cbuf
中 -
read(char[] cbuf, int off, int len)
:用于从输入流读取字符到字符数组cbuf
中,并增加了偏移量off
以及读取的字符数量len
-
skip(long n)
:忽略输入流中的n
个字符,返回实际忽略的字符数量 -
close()
:关闭输入流并释放资源
FileReader
是使用较多的字符输入流。
// 文件使用上面的 test.txt
String filePath = "test.txt";
FileReader fr = new FileReader(filePath);
// 读取一个字符,输出90,也就是Z
System.out.println(fr.read());
char [] cBuf = new char[10];
// 读取一系列字符串到字符数组中
// 依次输出 换行符 回车符 h e l l o , w o
fr.read(cBuf);
for(char c : cBuf){
System.out.println(c);
}
// 跳过一个字符
fr.skip(1);
char [] cBuf1 = new char[6];
// 读取4个字符串到字符数组中,偏移量为2
// 输出 空 空 l d ! 换行符
fr.read(cBuf1,2,4);
for(char c : cBuf1){
System.out.println(c);
}
Writer
Writer
用于将字符输出到文件中,它是所有字符输出流的父类。
常用方法
-
write(int c)
:向输出流写入单个字符 -
write(char[] cbuf)
:向输出流写入字符数组 -
write(char[] cbuf, int off, int len)
:向输出流写入字符数组,并包含偏移量off
以及字符数量len
-
write(String str)
:向输出流写入字符串 -
write(String str, int off, int len)
:向输出流写入字符串,并且包含偏移量off
以及字符数量len
-
append(CharSequence csq)
:将指定的字符序列csp
附加到指定的Writer
对象并返回该Writer
对象 -
append(char c)
:将指定的字符附加到指定的Writer
对象并返回该Writer
对象 -
flush()
:刷新该输出流,强制输出所有缓冲的输出字符 -
close()
:关闭输出流并释放资源
String filePath = "simple.txt";
FileWriter fw = new FileWriter(filePath);
// 向输出流写入字母Z
fw.write(90);
// 写入换行符
fw.write(13);
// 写入字符串 hello,world!
fw.write("hello,world!");
// 写入换行符
fw.write(13);
char[] charArray = "the night".toCharArray();
// 写入字符数组
fw.write(charArray);
// 写入换行符
fw.write(13);
// 写入字符串,偏移量2,写入5个字符
// 因此写入的是 34567
fw.write("123456789",2,5);
// 写入换行符
fw.write(13);
// 写入字符数组,偏移量4,写入2个字符
// 因此写入的是 ni
fw.write(charArray,4,2);
// 写入换行符
fw.write(13);
CharSequence cs = new String("char sequence");
// 写入换行符
fw.write(13);
// 在输出流后追加字符序列 char sequence
Writer append = fw.append(cs);
// 刷新字符输入流
append.flush();
最终得到的simple.txt
内容如下:
Z
hello,world!
the night
34567
ni
char sequence
缓冲流
由于IO操作很耗时,所以采用缓冲流,一次写入/读出多个字节,从而避免频繁的IO操作,提高流的传输效率。
字节缓冲流
字节缓冲流采用装饰器模式来增强InputStream
和OutputStream
子类对象的功能。
Java的输入输出流有自带的内部缓冲区,为什么还需要字节缓冲流?
- 内部缓冲区的大小固定且较小,而字节缓冲流可以自定义缓冲区大小,更灵活
- 字节缓冲区性能更高
BufferedInputStream
BufferedInputStream
从源头读取数据到内存的过程不会一个字节一个字节读取,而是会先将读取到的字节存放在缓冲区,并从内部缓冲区中单独读取字节,大大减少IO次数,提高了读取效率。
未完待续……
版权声明: 如无特别声明,本文版权归 月梦の技术博客 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 Java IO知识总结 》
本文链接:https://ymiir.netlify.app//java/JavaIO.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!