谜底就在谜面上
I : Input 输入
O :Output 输出
输入输出(IO)是指计算机同任何外部设备之间的数据传递,创建的输入输出设备有键盘,打印机,屏幕等。 举个例子,我们将存储在硬盘之中的数据读入内存之中,将内存之中的数据存储在硬盘之中这些行为都可以被叫做输入输出。只不过输入输出的主体有所变化而已。
一般来讲我们将内存作为输入输出的主体。从内存之中出来叫输出,读入内存之中叫输入。
I : 输入流
O : 输出流
我们将数据的无结构化传递抽象为流,按照流的方式将数据进行输入输出,在这其中数据被当作无结构的字符序列或字节序列,这样的话通俗点来讲IO流就是按照流的方式进行输入与输出。
按照流的方向分类
输入流
读入内存
输出流
从内存中输出
按照读取数据方式不同进行分类
字节流
忽略所读取文件的类型,一次读取一个字节,也就是一次读取八个位,这种流可以读取任意文件类型。
字符流
一次读取一个字符,这种流可以效率很高的读取普通文本文件(.txt),但也只能读取普通的文本文件。
按照流的角色分类
节点流
从/向一个特定的地方读写数据
处理流
是对一个已经存在的流的连接和封装,通过所封装的流的功能调用实现数据的读写,如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接
以下是Java的IO类的一个继承结构图(很宝贵的!!!)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t3AEBvjn-1636869529177)(/home/wfh/Pictures/选区_001.png)]
引用自https://blog.csdn.net/u011578734/article/details/108346430
令我们高兴的是Java以及将IO流写好了,省去了很多麻烦,我们可以直接面向封装过的IO流对象。了解这些对象就行了。
首先我们通过上图可以很清楚的明白java的IO流大体分为两类 字符流, 字节流 。
他们都是抽象类,
他们都实现了closeable接口,也就是说他们都是可关闭的。流就像管子用完要关闭
输出流都实现了Flushable接口,而输入流没有实现。 输出流可刷新,输出完最好刷新一下,这个操作的作用在于强制将Buffer(缓冲区位于内存)中的数据,写入文件(硬盘中)。
read()
一次从硬盘读取一个字节,以int存储这个结果并返回,当结果返回值为-1的时候说明文件已经读到末尾,为什么要以int类型作为存储结果?,因为一般来说有可能存在某个字节本身八个位全为1的情况,这样的一个字节若用byte存储,其会被识别为-1,与我们的判别是否读取完毕条件相冲突。所以我们用int来存储。(其实我还有一个小疑问为什么不用short(2字节)或char(2字节))
存在一个帧记录着读取位置,这个帧每次向后偏移一个字节。我把他叫文件位置记录帧
优点: 一次只读一个
缺点:一次只读一个,增加了访问磁盘的次数。
read(byte[] b)
将数据读入b中,最多一次读b.length;
这个方法也存在着一个文件位置记录帧,且和第一个方法的帧是同一个,只不过他的偏移量等于他的读取量。
这个方法也有一个返回值是读取到的字节数量。
字节流从字节数组的起始开始写入,会覆盖掉字节数组原本的值。当然也可以设置起始位置,和读入长度
优点:一次读的多,减少了磁盘访问次数。
int available()
返回流中剩余没有读到的字节数量
也就是说我们可以这样读取磁盘内容
public static void main(String[] args) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("src/wenben.txt"); byte[] bytes = new byte[fileInputStream.available()]; fileInputStream.read(bytes); System.out.println(new String(bytes)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
但是这样做意味着可能要开非常大的数组,用很多内存。我们要减少内存内存开销的话可以多次读取,在同一数组上进行处理就好了。
long skip()
跳过几个字节不读
path不存在的时候会新建一个文件
void write()
void write(byte[] b)
void write(byte[] b,int off,int len)
注意以下几点
若在打开文件的时候,构造方法的参数中第二个参数没有注明true,则会清空原文件。无论是否执行write()方法。若设置了第二个参数为true,则在原来的文本内容上会追加
注意写完之后一定要刷新
其他方面与read()在思想上并无不同,这里就不再赘述,要是大家有问题的话可以在评论区中指出。
read()
一次读取一个字符,可以是中文字符也可以是其他字符。
read(char[])
向char[]内读入字符。与InputStream基本无二
blank 暂时想不出什么特别的
(转换 + 包装可谓是弥补了java不能多重继承的无奈之举吧)
InputSteamReader
将InputStream转换为Reader
OutputSteamWriter
将OutputSream转换为Writer
自带缓冲不需要指定byte[]或char[]
BufferedReader
构造函数 BufferedReader(Read in)
可以将一个FileReader作为参数传入
当一个流的构造方法需要另一个流的时候这个被传进来的流叫做,节点流,外部负责包装的流叫包装流,关闭流的时候只需要关闭包装流
readLine()
读取一个文本行返回一个字符串,不从硬盘从内存(从硬盘读入的行为发生在何时)
当读到末尾时返回一个null
BufferedWrite
网上的说法是BufferedWriter相对于FileWriter来讲的话增加了一个缓冲器,即缓冲器满了以后才向硬盘内读写,但我不认为是这样,因为Writer和OutputStream这两个输出流都实际上都带有flush()方法,也就是说每一个输出流本身就含有缓冲区,所以这里我就不太清楚了,将来知道了再更新吧
BufferedInputStream
BufferedOutputStream
DataInputStream
DataOutputStream写入的文件需要DataInputStream去读
readUTF()
readInt()
DataOutputStream
数据专属流可以连同数据以及数据的类型一并写入文件。
大家有没有觉得这个特别鸡肋啊,其实在网络传输中用的比较多。就像是协议一样的
是一种永久化对象的技术,也可以实现远距离传输对象(哈哈哈,可以这么说吗?),通过序列化(Serialize)反序列化(diSerialize)实现。
ObjectInputStream
负责反序列化生成对象。
ObjectOutputStream
负责对象的序列化。
通过writeObject(Object obj);
readObject(Object obj);
可以对象的序列化与反序列化
不过只有实现标志性接口Serializable的类的对象,才能通过这两个方法进行传输否则会报错,虚拟机看到Serialiazable接口后,会根据该类为该类提供一个序列化版本号,当类发生变化时,序列化版本号也会随之发生改变。
PrintWriter
PrintStream
标准字节输出流,默认输出到控制台(print)
其实PrintStream离我们并不远,大家应该用过System.out.println吧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZxNKBer-1636869529181)(/home/wfh/Pictures/选区_005.png)]
out其实就是System类下的一个PrintStream类型的public成员
我们也可以改变System的out通过setOut方法。
可以跟一个setOut(new FileOutputStream),这样标准输出流就指向了一个文件。
用再次使用System.out
这是日志框架的一个原理
File代表的是一个文件或目录的路径名
以下是File类中的常用方法