I/O
文件对象
文件和文件夹都是用File代表
// 创建一个文件对象
String path = "..\java中期\IO\a.txt";
File file = new File(path);
什么是流
什么是流(Stream
),流就是一系列的数据
文件输入流
FileInputStream fileInputStream = new FileInputStream(file);
字节流
编码
所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。
比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。
就是编码的过程。
字节流是由字节组成的,所有的 InputStream
和 OutputStream
的子类都是,主要用在处理二进制数据
字符流
字符流是由字符组成的. 实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode
来处理,也就是要进行字符集的转化。在从字节流转化为字符流时,实际上就是byte[]
转化为String
时,public String(byte bytes[], String charsetName)
有一个关键的参数字符集编码
字符输入输出流
Reader
字符输入流
Writer
字符输出流
专门用于字符的形式读取和写入数据
// FileReader是Reader实现类,FileWriter是Writer的实现类
FileReader fr = new FileReader(f));
FileWriter fr = new FileWriter(f)
encode和decode的区别
在运行的时候,有时候会报编码错误,本文就来研究一下这个问题。
为什么会出现乱码呢?因为在文件存的时候格式和读取时候格式不一致就会乱码了。
字符串在java
内部的表示是unicode
编码,也可以说现在的内存是unicode
编码格式,硬盘是utf-8。平常的数据操
作都是先把数据读取到内存中,所以内存中都是unicode
编码格式。所以我们平常在做编码转换时候,通常用
unicode
作为中间编码。先将其他编码的字符串解码(decode
)成unicode
,再从unicode
编码(encode
)成另一种编码
格式。
decode
的作用是将二进制数据解码成unicode
编码,如str1.decode('utf-8')
,表示将utf-8
的编码字符串解码成
unicode
编码。
简单的来说:decode
就是把二进制数据(bytes
)转化成人看的懂得英文或者汉字(decode
用的比较多)
encode
的作用是将unicode
编码的字符串编码成二进制数据,如str2.encode('utf-8')
,表示将unicode
编码的字符串
编码成utf-8
。
关闭流的方式
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当
量比较大的时候,会影响到业务的正常开展。
在try中关闭(×)
在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占
用隐患。 不推荐使用
在finally中关闭 (√)
这是标准的关闭流的方式
- 首先把流的引用声明在try的外面,如果声明在
try
里面,其作用域无法抵达finally
. - 在finally关闭之前,要先判断该引用是否为空
- 关闭的时候,需要再一次进行
try catch
处理
这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
中文乱码问题
常见的编码
ASCII码
0~31 非打印控制字符,32空格,48~59是阿拉伯数字,65 ~ 90大写字母,97~122是小写字母
GB2313
收录了6763个汉字
GBK
GBK
全称《汉字内码扩展规范》(GBK
即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification
)
Unicode
UTF-8
以字节为单位编码,汉字3字节,ASCII
内字符1字节UTF-16
以16位无符号整数为单位编码, 使用 2 个或者 4 个字节来存储,长度既固定又可变UTF-32
以32位无符号整数为单位编码,都是4字节
乱码
假如使用UTF-8
的IDE
环境打开GBK
源码,解码时中文注释就会乱码
缓存流
BufferedInputStream
作用:
A BufferedInputStream
为另一个输入流添加了功能,即缓冲输入并支持mark
和reset
方法的功能。 创建BufferedInputStream
将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。 mark
操作会记住输入流中的一个点,并且reset
操作会导致从最近的mark
操作读取的所有字节在从包含的输入流中取出新字节之前重新读取。
java.io
下所有缓存流
Class | 描述 |
---|---|
BufferedInputStream | 缓冲输入流 |
BufferedOutputStream] | 缓冲输出流 |
BufferedReader | 缓冲字符输入流 |
BufferedWriter | 缓冲字符输出流 |
缓存区的定义
接收输入流,给到输出流
byte[] buffer = new byte[1024];
int len = -1;
while((len = <输入流>.read(buffer))!=-1){
<输出流>.write(buffer);
};
数据流
DataInputStream
数据输入流
DataOutputStream
数据输出流
通过DataOutputStream
将特定格式数据写入文件,读取DataOutputStream
要按写入顺序读取。
//1.写入
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(fos);
dos.writeBoolean(true);
dos.writeBoolean(false);
dos.writeFloat((float) 1.235);
//2.读取
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
boolean b = dis.readBoolean();
boolean b2 = dis.readBoolean();
float v = dis.readFloat();
对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable
接口
//Hero类的创建
public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;
}
//1.创建一个Hero garen
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
//2.准备一个文件用于保存该对象
File f =new File("d:/garen.lol");
//3.创建输入输出流
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
//4.写入、读取
oos.writeObject(h);
Hero h2 = (Hero) ois.readObject();
System.out.println(h2.name);
System.out.println(h2.hp);
//5.释放资源