观看提醒:本文档中代码中的try…catch…finally语句均不符合规范
IO流的概述
1.1、什么是IO流
I:input
O:output
流:像流水一样传输数据
1.2、IO流的作用
用于读写数据(本地文件、网络)
1.3、IO流按照流向可以分类哪两种流
输出流: 内存 -> 文件
输入流: 文件 -> 内存
1.4、IO流按照操作文件的类型可以分类哪两种流
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件
IO流的体系和字节输出流基本用法
2.1 IO流的体系
1、FileOutputStream
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
步骤:
1、创建字节输出流对象
参数是File对象或者字符串表示的路径
如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
如果文件已经存在,会清空文件
2、写数据
write方法的参数是整数,但实际上写到本地上文件中的是整数再ASCII上对应的字符
3、释放资源
解除了资源的占用
public static void main(String[] args) {
try {
//让程序和文件之间产生一个数据通道
FileOutputStream fos = new FileOutputStream("E:/a.txt");
try {
//将程序中的数据存入文件
fos.write(91);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
//关闭通道
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
FileOutputStream写数据的三种方式
void write(int b)
- 一次写一个字节数据
void write[byte[] b]
- 一次写一个字节数组数据
void write(byte[] b, int off, int len)
- 一次写一个字节数组的部分数据
- 参数1:数组
- 参数2:起始索引
- 参数3:个数
FileOutputStream写数据的两个小问题:换行和续写
-
换行:再次写入一个换行符即可
-
Windows:\r\n
\r 为回车符,将光标移动到最前面,\n为换行符
-
Linux: \n
-
Mac: \r
细节:在Windows操作系统中,Java对回车换行符进行了优化,写其中一个\r或者\n即可,java在底层会补全(建议写全)
FileOutputStream fos = new FileOutputStream("D:/a.txt");
String str = "\r\n";
byte bytes = str.getBytes();
fos.write(bytes);
fos.close;
- 续写(创建对象的时候,调用双参构造,第二个参数为续写开关)
FileOutputStream fos = new FileOutputStream("D:/a.txt",true);
2、FileInputStream
操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来
步骤:
1、创建字节输入流的对象
如果文件不存在,就直接报错
2、读数据
一次读一个字节,读出来的是数据在ASCII上对应的数字
读到文件末尾了,read方法返回-1
3、释放资源
每次使用完流之后必须释放资源
FileInputStream的循环读取
while((read1 = fis.read())!=-1){
System.out.println((char)read1);
}
练习:文件拷贝
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/123.png");
FileOutputStream fos = new FileOutputStream("E:/111.png");
int read1;
while((read1 = fis.read())!= -1){
fos.write(read1);
}
fos.close();
fis.close();
}
文件拷贝的弊端和解决方案
弊端
当文件非常大的时候,拷贝速度会非常慢
FileInputStream一次读一个字节
FileInputStream一次读多个字节
- public int read(byte[] buffer)
一次读一个字节数组的数据,每次读取会尽可能把数组填满
返回值是本次读取到了多少个数据
注意:虽然字节数组越大,拷贝速度越快,但是数组本身也会占用空间
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:/a.txt");
byte[] b = new byte[2];
int len = fis.read(b);
System.out.println(len); //2
String string = new String(b);
System.out.println(string); //he
fis.close;
}
因为单参方法要尽可能地把字节数字填满,多次读取,读取数据不满数组长度时,数组某些位置不会被覆盖
因此可以使用重构的三参方法
- public int read(byte[] buffer, int off,int len)
字节流读取中文会出现乱码
原因如下:
字符集
- ASCII
ASCII的编码规则:前面补0,补齐8位
- GB2312字符集
1980年发布,1981年5月1日实施的简体中文汉字编码国家标准。
收录7445个图形字符,其中包括6763个简体汉字
- BIG5字符集
台湾地区繁体中文标准字符集,共收录13053个中文字,1984年实施
- GBK
2000年3月17日发布,收录21003个汉字。
包含国家标准GB13000-1中全部中日韩汉字,和BIG5编码中的所有汉字
Windows系统默认使用的GBK,但是系统显示ANSI
- GBK对汉字的存储
因此,汉字是以1开头的,字母是以0开头的
- Unicode
国际标准字符集
UTF - 8编码规则:用1~4个字节保存(英文一个字节,中文三个字节)
至此,已经能够解释为啥会出现乱码了
原因1:读取数据时未读完整个汉字
用字节流读取中文,因为中文不是通过一个字节来编码的,所以会乱码
原因2:编码和解码时的方式不统一
字符流
字符流 = 字节流 + 字符集
输入流:一次读一个字节,遇到中文时,一次读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
FileReader
1、创建字符输入流对象
2、读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1
3、释放资源
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("E:/a.txt");
int ch;
while((ch = fr.read())!= -1){
System.out.print((char) ch);
}
fr.close();
}
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("E:/a.txt");
int ch;
char[] chars = new char[2];
while((ch = fr.read(chars))!= -1){
String str = new String(chars,0,ch);
System.out.print(str);
}
fr.close();
}
FileWriter
1、创建字符输出流对象
如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关
2、写数据
3、释放资源
void write(int c) 写出一个字符
void write(String str) 写出一个字符串
void write(String str,int off,int len) 写出一个字符串的一部分
高级流
缓冲流
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
字节缓冲流:
原理:底层自带了长度为8192的缓冲区提高性能
方法名称
- public BufferedInputStream(InputStream is) 把基本流包装成高级流,提高读取数据的性能
- public BufferedOutputStream(OutputStream os) 把基本流包装成高级流,提高书写数据的性能
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(newFileOutputStream("E:/b.txt"));
int b;
while((b = bis.read())!= -1){
bos.write(b);
}
bos.close();
bis.close();
}
字符缓冲流
- public BufferedReader(Reader r)
- public BufferedWriter(Writer r)
特殊方法:
输入流的方法
- public String readLine() 读取一行数据,如果没用数据可读了,会返回null
输出流的方法
- public void newLine() 跨平台的换行
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:/a.txt"));
String line;
while(((line = br.readLine())!= null)){
System.out.println(line);
}
}
转换流
字符流和字节流之间的桥梁
作用:字节流想要使用字符流中的方法
-
InputStreamReader
-
OutputStreamWriter
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:/a.txt"),"GBK");
int ch;
while((ch = isr.read())!= -1){
System.out.print((char) ch);
}
} //指定字符编码读取数据
练习题:利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:/a.txt"),"GBK");
BufferedReader br = new BufferedReader(isr);
String ch;
while((ch = br.readLine())!= null){
System.out.println(ch);
}
}
序列化流
可以把Java中的对象写到本地文件中
构造方法
public ObjectOutputStream(OutputStream out) 把基本流包装成高级流
成员方法
public final writeObject(Object obj) 把对象序列化到文件中去
使用对象输出流将对象保存到文件时会出现NotSerializableException异常
解决方案:需要让对象类实现Serializable接口
Serializable接口里面没有抽象方法,标记类接口
一旦实现了这个接口,就表示当前类可以被序列化
Student student = new Student();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/b.txt"));
oos.writeObject(student);
反序列化流
可以把序列化到本地的对象,读取到程序中来
public ObjectInputStream(InputStream out) 把基本流包装成高级流
成员方法
public Object readObject()
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/b.txt"));
Student s = (Student) ois.readObject();
System.out.println(s);