创建文件对象的三种方式
文件常用方法
IO流
输入流从文件输入到程序内存,输出流从程序内存输出到文件
流的分类
字节流分为字节输入流和字节输出流
InputStream是一个抽象类
IO流分为字节流和字符流。
字节流分为字节输入流【InputStream】和字节输出流【OutputStream】。
字符流分为字符输入流【Reader】和字符输出流【Writer】并且这四个类都是抽象类,都不可以直接进行实例化,我们只可以实例化这个些抽象类的子类。。。。
举个例子:字节输出流的子类都是字节输出流。。。
流和文件的关系
类比一下:
流就相当于我们的外卖小哥,起到一个传输的作用。。。
文件就像物流中心的物品,通过流进行传输
IO流体系图-常用的类
InputStream常用的子类
FileInputStream
但是该方法
进行优化:
该read方法表示一次最多读取8个byte【字节】。如果字节数组的大小小于8时,也可以读取少于8个字节的数组。。。。
举个例子来说明:
第一次读取8个字节大小即是读取:hello,wo
第二次读取另外3个字节大小 :rld
FileOutputStream
同样也可以写入一个字节数组
注意:
表示追加:
练习
注意:从一个文件读取数据到另外一个文件的过程
readLen每一次得到的是输入流读取这个数组的长度,如果没有读取到,那么就返回-1,举个例子假设读取1039个字节,那么第一次读取数组大小:1024个字节,那么第一次readLen==1024。剩余15个字节。。。那么第二次读取15个字节,那么第二次readLen==15。
我们一定要使用fileOutputStream.write(buf,0,readLen);进行读取数据,,,,
如果我们用fileOutputStream.write(buf)进行读取的时候,举个例子:
假设读取1039个字节,那么第一次读取1024个字节,剩余15个字节。。。那么第二次依旧读取1024个字节,那么就是不正确的了,,,,,,,【即是如图】
char[ ]是字符数组,byte[ ]是字节数组
练习
一个字节一个字节的读取
一个字符数组一个字符数组的读取
FileWriter使用之后,一定要记得关闭
对于byte和char的区别及其使用
1.
2和3
4.第二点可知:char可以表示中文字符,byte不可以表示中文字符。所以字节流【即是】读取或输出的时候我们一般是使用byte,但是当字符流读取的时候,我们就不可以使用byte了,因为他不可以表示中文字符。。。。会导致乱码现象。
追一追close()源码:
底层都有一个写入的方法
节点流和处理流
节点流可能比较低级,功能相对于处理流比较弱一点
当我们想要更广大的功能的时候,我们就不能再仅仅局限于节点流了。。这时候我们就要用到包装流。。
举例子说明:
因为包装流BufferedReader中封装的有属性Reader,该Reader可以封装一个节点流,,该节点流为Reader子类即可
BufferWriter同理
节点流和处理流(即是包装流)的区别和联系
节点流是处理文件或数组是分别有对应的节点流去进行作用,是比较固定的。
但是处理流[即包装流] 不太一样,它是比较灵活的,一个类可以处理不同的类型需求
对于第三点设计模式,我们模拟一下底层BufferReader类的实现
我们的BufferedReader底层源码实现中和上面模拟的思想一致,都是搞出一个父类Reader作为属性,
之后进行封装继承这个父类抽象类的节点流的方法,然后塑造出了包装流,,我们传入的构造器的参数为这个父类流的子类对象,实际上就是指定调用哪个流的方法
包装流实际上还是调用的是节点流
进行一步更新模拟的方式:见下
包装流实际上还是调用的是节点流
文本文件和二进制文件
文本文件是用字符进行组织的,二进制文件是用字节进行组织的 。
一般二进制文件就是图片或者视频或音乐
文本文件就是文本文件
处理流【即是包装流】
1.字符处理流【不可以处理二进制文件,只可以处理文本文件】
在我们底层进行关闭这个包装流的时候,也会自动关闭我们这个包装流包装的节点流
和字节流一样,参数加上true就表示是追加的意思
总结一下:处理流的传参
我们对于BufferedReader和BufferedWriter的方法进行传参的时候
源码显示构造器:
我们可以传继承这个Writer抽象类的类的对象
同理:
抽象类Reader和Writer的子类如图所示:
2.字节处理流【可以处理二进制文件】
一般二进制文件就是图片或者视频或音乐
。。。 。。。。因为InputStream是一个抽象类,所以我们可以把InputStream类的子类的实例对象传进去
OutputStream同理:
到时候需要什么对象传进去就可以了
思考
文本文件底层就是由二进制组成的,所以可以进行这样的操作。。
对象流
这是我们实际开发中的一个新的需求。。。。把数据类型也保存下来
序列化和反序列化
总结:
1.如果从程序中只是简单的保存值到文件中,而不保存它的类型,那么我们就称之为保存值
2.如果从程序中既保存值也保存了数据类型到文件中,那么我们称这个过程为序列化。从文件中重新恢复这个反向操作称之为反序列化
哈哈哈哈哈,学英语!
第二个依然是实现第一个接口的,并且还有抽象方法需要被实现。。。。
。。。 。。。。因为InputStream是一个抽象类,所以我们可以把InputStream类的子类传进去
OutputStream同理:
如图:
总结:
Out:输出到文件,就是序列化
In:输入到程序内存,就是反序列化
使用ObjectOutputStream进行序列化
使用ObjectInputStream进行反序列化
我们把文件中的数据反序列化读取到程序中时,要按照进行序列化时的顺序进行读取
读取的时候要按照输出时的顺序
我们对于序列化的Dog类需要进行公有化进行暴露出来。。否则就没办法向下转型我们反序列化得出的dog引用,我们想要对于Dog类的方法,我们根据运行时绑定可知,运行类型也必须是Dog类型
序列化细节
序列化时写出到文件中的顺序和非序列化时读取到程序内存的顺序要一致的
第三条添加一个SerialVersionUID是为了保证当我们多添加了一个属性之后实现序列化,我们只是把它当作一个升级,而不会把它当作一个新的类。。使其变得更加灵活了,,,
1.
2.
但是当里面的属性没有实现序列化接口时,我们进行序列化时就会报错
解决方法就是让没有实现接口的属性进行实现
3.序列化是可以被传递下来的
例子:Integer没有实现Serializable,但是Number实现了,可以传递下去,所以Intger可以实现序列化
4.学英语,,,,一瞬间,那么怎么来序列化,,故被此修饰的,不可被进行序列化。。。。
标准输入输出流
System.out表示标准输出流,类型是PrintStream。设备是显示器,那么就是输出显示到我们的显示器
System.in表示标准输入流,类型是InputStream,设备是键盘。表示从我们的键盘输入到我们的程序内存
转换流
当我们直接输出,发现是正常输出的,因为我们编译器在没有特定指示时,默认是按照UTF-8的格式进行读取的
但是当我们改变读取格式之后,发现出现乱码现象。。。。。
解决办法:
我们字节流是可以进行指定编码的格式的,所以我们可以先给字节流指定一个编码格式。。之后通过转换流把字节流转换为字符流
IntputStream是字节输入流的顶级父类
Reader是字符输入流的顶级父类
IntputStreamReader 意思就是:把 IntputStream这个字节流转换为Reader这个字符流
OutputStream是字节输出流的顶级父类
Writer是字符输出流的顶级父类
OutputStreamWriter 意思就是:把 OutputStream这个字节流转换为Writer这个字符流
如果按照上面这个直接写,会出现乱码现象。。
改正方法如下:
使用转换流进行改正:
通过转换流,我们可以进行改变文件的编码格式。即是通过参数的传递。。。
打印流
输出到指定文件中去:
Properties类
1.传统方法
读取mysql.properties文件到程序内存中 我们用的是读取
第二个参数的意思是,写入到mysql的注释内容
3.
有key的时候,直接写就相对于是修改了
源码分析:
变式:
当把文件的编码改成了gbk,我们知道我们默认是utf-8的文件编码,当我们更改时,我们需要借助转换流去再重新指定文件编码
先创建一个文件
当我们进行序列化操作之后,即是序列输出程序的内容到指定路径的文件。如果说指定路径没有该文件,那么就创建一个。。如果有那么就使用,,,如果原文件有内容,那么就是相当于覆盖这个文件的内容
反序列化:
即是把路径地址为filename1处的文件中的内容进行读取到程序中