Java【I/O】
一、IO概述
1.1 什么是IO
Java中的I/O操作主要是指使用java.io
包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做写出数据。
1.2顶级父类们:
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
二、字节流
2.1 一切皆为字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存的,都是一个一个的字节。
2.2 字节输出流【OutputStream】
java.io.OutputStream
抽象类是表示字节输出流的所有类的父类,将指定的字节信息写出到目的地。定义了字节输出流的基本共性功能方法。
void close()
:Closes this output stream and releases any system resources associated with this stream.void flush()
:Flushes this output stream and forces any buffered output bytes to be written out.void write(byte[] b)
: Writes b.length bytes from the specified byte array to this output stream.void write(byte[] b, int off, int len)
:Writes len bytes from the specified byte array starting at offset off to this output stream.abstract void write(int b)
:Writes the specified byte to this output stream.
2.3 [FileOutputStream]
java.io.FileOutputStream
类是文件输出流,把内存中的数据写入到文件中。
构造方法
FileOutputStream(String name)
:创建一个向具有指定名称的文件中写入数据的输出文件流。FileOutputStream(File file)
:创建一个向指定File对象表示的文件中写入数据的文件输出流。
构造方法的作用:创建FileOutputStream对象,根据构造方法中传递的文件/文件路径,创建一个空的文件,把FileOutputStream对象指向创建好的文件。
// 1、创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
FileOutputStream fos = new FileOutputStream("day20-advanced/a.txt");
// 2、调用FileOutputStream对象中的方法write,把数据写入到文件中
// abstract void write(int b):将指定的字节写入
fos.write(97);
// 3、释放资源
fos.close();
追加写/续写:
-
FileOutputStream(String name, boolean append)
: 创建一个向具有指定 name 的文件中写入数据的输出文件流。 -
FileOutputStream(File file, boolean append)
:创建一个向指定file 对象表示的文件中写入数据的文件输出Luis。boolean append
:追加写开关。true
:创建对象不会覆盖源文件,继续在文件的末尾追加写数据;false
:创建一个新文件,覆盖原文件。FileOutputStream fos = new FileOutputStream("day20-advanced/c.txt",true); fos.write("您好".getBytes()); // 将字符串转换为字节数组 fos.close();
2.3字节输入流【InputStream】
java.io.InputStream
抽象类是表示字节输入流的所有类的父类,可以读取字节信息到内存中,定义了字节输入流的基本共性功能方法。
int read()
: 从输入流中读取数据的下一个字节。int read(byte[] b)
:从输入流汇总读取一定数量的字节,并将其存储在缓冲区数组b中。void close()
:关闭此输入流并释放与该流关联的所有系统资源。
2.4 [FileInputStream]
java.io.FileInputStream
类是文件输入流,把硬盘文件中的数据,读取到内存中使用。
构造方法
FileInputStream(String name)
FileInputStream(File file)
FileInputStream fis = new FileInputStream("day20-advanced/a.txt"); // abc
/*
读取文件是一个重复的过程,可以使用循环优化
不知道文件有多少个字节,使用while循环
结束条件是读到-1结束
*/
int len = 0; // 记录读到的字节
while((len = fis.read() )!=-1 ) {
System.out.print((char)len); // abc
}
fis.close();
一次读取多个字节的方法
-
int read(byte[] b)
:从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。参数:
byte[] b
起到缓冲作用,存储每次读取到的多个字节。数组的长度一般定义为1024(1kb)或者1024的整数倍。返回值:每次读取的有效字节个数。
// 创建FileInputStream 对象,构造方法中绑定要读取的数据源 FileInputStream fis = new FileInputStream("day20-advanced/a.txt"); /* 发现以上读取是一个重复的过程,可以使用循环优化 不知道文件汇总有多少字节,所以使用while循环 while循环结束的条件,读取到-1结束 */ byte[] bytes = new byte[1024];// 存储读取到的多个字节 int len = 0;// 记录每次读取的有效字节个数 while((len = fis.read(bytes)) != -1) { System.out.println(len);// 3 //System.out.println(new String(bytes));// abc //String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数 System.out.println(new String(bytes,0 ,len)); //abc } fis.close();
-
tips:String类的构造方法
String(byte[] bytes)
:把字节数组转换为字符串
String(byte[] bytes, int offset, int length)
: 把字节数组的一部分转换为字符串,offset
:数组的开始索引,length
:转换的字节个数。
2.5 练习
使用FileOutputStream
和FileInputStream
完成文件复制操作。
long s = System.currentTimeMillis();
// 1、创建字节输入流,读取数据源
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
//2、 创建字节输出流,写入读取的数据
FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");
3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
//byte[] bytes = new byte[1024];
//int len = 0;
//while((len = fis.read(bytes)) != -1) {
// // 4、将读取的数据写入到输出流中
// fos.write(bytes,0,len);
//}
int len = 0;
while ((len = fis.read()) != -1) {
fos.write(len);
}
// 5、释放资源(先关写的,如果写完了肯定读完了)
fos.close();
fis.close();
long e = System.currentTimeMillis();
System.out.println("字节输入输出流复制文件共耗时:" + (e-s) + "毫秒");
三、字符流
使用字节流读取文本文件时,遇到中文字符,可能不会显示完整的字符,原因是一个中文字符可能只能有多个字节(1个中文,GBK:占用两个字节,UTF-8:占用3个字节),Java提供了字符流类,以字符为单位读写数据,用于处理文本文件。
3.1 字符输入流【Reader】
java.io.Reader
抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。定义了字符输入流的基本共性功能
int read()
:读取单个字符并返回int read(char[] cbuf)
: 一次读取多个字符,将字符读入数组void close()
: 关闭该流并释放与之关联的所有资源。
3.2 [FileReader]
java.io.FileReader
文件字符输入流,把硬盘文件中的数据以字符的方式读取到内存中,构造时按照系统默认的字符编码(Idea中默认编码是utf-8)和默认字节缓冲区。
public static void main(String[] args) throws IOException {
// 1、创建FileReader对象,构造方法中绑定要读取的数据源
FileReader fr = new FileReader("day20-advanced/a.txt");
// 2、使用FileReader对象中的read方法
// int len = 0;
// while ((len = fr.read()) != -1) {
// System.out.println(len);
// }
char[] cs = new char[1024];// 一次读取多个字符,将字符读入数组
int len = 0;// 记录的是每次去的的有效字符个数
while((len = fr.read(cs)) != -1 ) {
/*
String类的构造方法
String(char[] value) 把字符数组转换为字符串
String(char[] value, int offset, int count) 把字符数组的一部分转换为字符串 offset数组的开始索引, count转换的个数
*/
System.out.println(new String(cs));
}
// 3、释放资源
fr.close();
}
3.3 字符输出流【Writer】
java.io.Writer
抽象类是表示用于写出字符流的所有类的父类,将内存中的字符数据写入到文件中。定义了字节输出流的基本共性功能方法。
void write(int c)
写入单个字符void write(char[] cbuf)
写入字符数组abstract void write(char[] cbuf,int off, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数。void write(String str)
写入字符串void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数。void flush()
刷新该流的缓冲void close()
关闭此流,但要先刷新它。
3.4 [FileWriter]
java.io.FileWriter
文件字符输出流,可以把内存中的字符数据写入到文件中。构造时使用系统默认字符编码和默认的字节缓冲区。
构造方法
FileWriter(File file)
根据给定的File对象构造一个FileWriter对象。FileWriter(String fileName)
根据给定的文件名构造一个FileWriter对象
// 1、创建FileWriter对象,构造方法中绑定要写入数据的目的地
FileWriter fw = new FileWriter("day20-advanced/d.txt");
// 2、使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
fw.write(97);
// 3、使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中
fw.flush();
// 4、释放资源(会先把内存缓冲区中的数据刷新到文件中)
fw.close();
flush和close的区别
flush
: 刷新缓冲区,流对象可以继续使用close
: 先刷新缓冲区,然后通知系统释放资源。 流对象不可以再被使用了。
字符输出流写数据的其他方法
void write(char[] cbuf)
写入字符数组。abstract void write(char[] cbuf, int offset, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数void write(String str)
写入字符串void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数
FileWriter fw = new FileWriter("day20-advanced/e.txt");
char[] cs = {'a','b','c','d','e'};
// void write(char[] cbuf)写入字符数组
fw.write(cs);// abcde
//void write(char[] cbuf, int offset, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
fw.write(cs, 1, 3);// bcd
fw.flush();
fw.close();
续写换行
FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
换行:换行符号win:\r\n; linux:/n; mac:/r
FileWriter fw = new FileWriter("day20-advanced/f.txt",true);
for (int i = 0; i < 10; i++) {
fw.write("HelloZX" + i + "\r");
}
fw.close();
四、IO异常处理
4.1 在jdk1.7
之前使用try-catch-finally
处理流中的异常
// 提高变量的作用域,让finally可以使用
// 变量在定义的时候,可以没有值,但是使用的时候必须有值
FileWriter fw = null;
try {
fw = new FileWriter("day20-advanced/g.txt",true);
for (int i = 0; i < 10; i++) {
fw.write("HelloZX" + i + "\r");
}
} catch (IOException e) {
// 异常的处理逻辑
System.out.println(e);
} finally {
// 一定会执行的代码
// 创建对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2 jdk1.7处理流异常
jdk1.7
的新特性,在try
后面可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try
中有效。try
中的代码执行完毕,会自动将流对象释放,不用写finally
。
try(// 1、创建字节输入流,读取数据源
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
//2、 创建字节输出流,写入读取的数据
FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");){
//3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes)) != -1) {
// 4、将读取的数据写入到输出流中
fos.write(bytes,0,len);
}
} catch (IOException e) {
// 异常的处理逻辑
System.out.println(e);
}
4.3 jdk9处理流异常
jdk9
新特性,try
的前边可以定义流对象,在try
后面的()中可以直接引入流对象的名称(变量名),在try
代码执行完毕之后,流对象亦可以释放掉,不用写finally
。
// 1、创建字节输入流,读取数据源
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
//2、 创建字节输出流,写入读取的数据
FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");
try(fis;fos) {
//3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes)) != -1) {
// 4、将读取的数据写入到输出流中
fos.write(bytes,0,len);
}
} catch (IOException e) {
System.out.println(e);
}
五、属性集
5.1 概述
java.util.Properties
继承与Hashtable
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。Properties
集合是一个唯一和IO流结合的集合。
可以使用Properties
集合中的方法store
,把集合中的临时数据,持久化写入到硬盘中存储;可以使用Properties
集合中的方法load
,把硬盘中保存的文件(键值对),读取到集合中使用。属性列表中每个键及其对应值是一个字符串。Properties
集合是一个双列集合,key
和value
默认都是字符串。
5.2 Properties类
构造方法
public Properties()
:创建一个空的属性列表
基本的存储方法
String getProperty(String key)
Searches for the property with the specified key in this property list.Object setProperty(String key, String value)
Calls the Hashtable method put.Set<String> stringPropertyNames()
返回此属性列表中的键集,其中改键及其对应值是字符串,此方法相当于Map集合中的keySet方法
Properties
集合中load
方法
void load(InputStream inStream)
void load(Reader reader)
// 1、创建Properties对象
Properties prop = new Properties();
// 2、使用Properties集合对象中的方法load读取保存键值的文件
//prop.load(new FileReader("day20-advanced/prop.txt"));
prop.load(new FileInputStream("day20-advanced/prop.txt"));
// 3、遍历Properties集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
Properties
集合中的方法store
void store(OutputStream out, String comments)
void store(Writer writer, String comments)
参数:
OutputStream out
: 字节输出流,不能写入中文
Writer writer
: 字符输出流,可以写中文
String comments
:注释,用来解释说明保存的文件是做什么用的,不能使用中文,会产生乱码,默认是Unicode
编码, 一般使用""空字符串。
// 1、创建Properties集合对象,添加数据
Properties prop = new Properties();
prop.setProperty("赵丽颖","168");
prop.setProperty("古力娜扎","178");
prop.setProperty("赵晓","165");
2、创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
//FileWriter fw = new FileWriter("day20-advanced/prop.txt");
//
3、使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
//prop.store(fw,"save data");
//
4、释放资源
//fw.close();
prop.store(new FileOutputStream("day20-advanced/prop.txt"), "");// 用字节输入流写中文会出现乱码
六、缓冲流
6.1 概述
缓冲流,是对四个基本的FileXxx
流的增强,所以数量也是四个流,按照类型分为
输入流 | 输出流 | |
---|---|---|
字节缓冲流 | BufferedInputStream 字节缓冲输入流 | BufferedOutputStream 字节缓冲输出流 |
字符缓冲流 | BufferedReader 字符缓冲输入流 | BufferedWriter 字符缓冲输出流 |
缓冲流的基本原理,是在创建流对象时,创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统的IO次数,提高读写的效率。
6.2 字节缓冲输出流【BufferedOutputStream】
java.io.BufferedOutputStream
继承自OutputStream
构造方法
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流BufferedOutputStream(OutpurStream out, int size)
创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层
参数:
OutputStream out
:字节输出流。我们可以传递FileOutputStream
, 缓冲流会给FileOutputStream
增加一个缓冲区,提高FileOutputStream
的写入效率。
int size
:指定缓冲流内部缓冲区的大小,不指定默认
继承自父类的共性成员方法
public void close()
: 关闭此输出流并释放与此相关的任何系统资源public void flush()
: 刷新此输出流并强制任何缓冲的输出字节被写出public void write(byte[] b)
:将b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
: 从指定的字节数组写入len 字节,从偏移量off 开始输出到此输出流public abstract void write(int b)
: 将指定的字节写入此输出流。
//1、创建FileOutputStream 对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream("day20-advanced/g.txt");
//2、创建BufferedOutputStream 对象,构造方法中传递FileOutputStream 对象,提高FileOutputStream对象的效率
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3、使用BufferedOutputStream 对象中的write方法,把数据写入到内部缓冲区中
bos.write("你这个年纪还睡得着觉".getBytes());
//4、使用BufferedOutputStream对象中的flush方法,把内部缓冲区中的数据,刷新到文件中
bos.flush();
//5、释放资源(会先调用flush方法刷新数据,第4步可以省略)
bos.close();
6.3 字节缓冲输入流【BufferedInputStream】
java.io.BufferedInputStream
继承自InputStream
。
构造方法
BufferedInputStream(InputStream in)
创建一个BufferedInputStream
对象并保存其参数,即输入流in,以便将来使用。BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的BufferedInputStream
并保存器参数,即输入流in。
参数:
InputStream
:字节输入流,我们可以传递FileInputStream
,缓冲流会给FileInputStream
增加一个缓冲区,提高FileInputStream
的读取效率
int size
:指定缓冲流内部缓冲区的大小,不指定默认
继承自父类的成员方法
int read()
从输入流中读取数据的下一个字节。int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。void close()
关闭此输入流并释放与该流关联的所有系统资源
//1、创建FileInputStream对象,构造方法中绑定要读取的数据
FileInputStream fis = new FileInputStream("day20-advanced/g.txt");
//2、创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
BufferedInputStream bis = new BufferedInputStream(fis);
//3、使用BufferedInputStream对象中的read方法读取文件
// int read() 是一个字节一个字节的读
//int len = 0;
//while ((len = bis.read()) != -1){
// System.out.println(len);
//}
// int read(byte[] b) 字节数组来读
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
System.out.println(new String(bytes,0,len));
}
//4、释放资源
bis.close();
6.4 练习【文件复制】
文件复制练习:一读一写 ,和之前的字节输入输出流复制文件的效率进行对比。
long s = System.currentTimeMillis();
//1、创建字节缓冲输入流对象,构造方法中传递字节输入流
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg"));
//2、创建字节缓冲输出流对象,构造方法中传递字节输出流
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("/Users/yanzhuang/Desktop/2.jpg")
);
//3、使用字节缓冲输入流中的方法read,读取文件
//int len = 0;
//while ((len = bis.read()) != -1 ) {
// //4、使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区
// bos.write(len);
//}
byte[] bytes = new byte[1024];
int len = 0;
while((len = bis.read(bytes)) != -1) {
bos.write(bytes,0, len);
}
//5、释放资源(会先把缓冲区中的数据,刷新到文件中)
bos.close();
bis.close();
long e = System.currentTimeMillis();
System.out.println("字节缓冲输入输出流复制文件共耗时:" + (e-s) + "毫秒");
6.5 字符缓冲输出流【BufferedWriter】
java.io.BufferedWriter
继承自Writer
。
构造方法
BufferedWriter(Writer out)
创建一个使用默认大小输出缓冲区的缓冲字符输出流。BufferedWriter(Writer out, int sz)
创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
参数:
Writer out
: 字符输出流,我们可以传递FileWriter
,缓冲流会给FileWriter
增加一个缓冲区,提高FileWriter
的写入效率
int sz
:指定缓冲区的大小,不写默认大小
继承自父类的共性成员方法
void write(int c)
写入单个字符void write(char[] cbuf)
写入字符数组abstract void write(char[] cbuf, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数void write(String str, int off, int len)
写入字符串的某一部分,off 字符串的开始索引,len写的字符个数。void flush()
刷新该流的缓冲。void close()
关闭此流,但要先刷新它。
特有的成员方法
void newLine()
写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
//1、创建字符缓冲输出流对象,构造方法中传递字符输出流
BufferedWriter bw = new BufferedWriter(
new FileWriter("day20-advanced/h.txt"));
//2、调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
for (int i = 0; i < 10; i++) {
//bw.write("helloZX" + i + "\r");
bw.write("helloZX" + i);
bw.newLine();
}
//3、调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
bw.flush();
//4、释放资源
bw.close();
6.6 字符缓冲输入流【BufferedReader】
java.io.BufferedReader
继承自 Reader
。
构造方法
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流BufferedReader(Reader in, int sz)
创建一个使用指定大小输入缓冲区的缓冲字符输入流
继承自父类的共性成员方法
int read()
读取单个字符并返回。int read(char[] cbuf)
一次读取多个字符,将字符读入数组void close()
关闭改流并释放与之相关的所有资源
特有的成员方法
String readLine()
读取一个文本行。读取一行数据,行的终止符号:通过下列字符之一即可认为某行终止:换行、回车、或回车后跟着换行(\r\n)。返回值:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null
。
//1、创建字符缓冲输入流对象,构造方法中传递字符输入流
BufferedReader br = new BufferedReader(new FileReader("day20-advanced/h.txt"));
//2、使用字符缓冲输入流对象中的read/readLine方法读取文本
//String line = br.readLine();
//System.out.println(line);
/*
读取是一个重复的过程,使用循环优化,不知道有多少行数据,所以使用while循环
结束条件为读到null结束
*/
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
//System.out.println(br.readLine());// null
//3、释放资源
br.close();
6.7 练习
要求:对文本的内容进行排序in.txt
,输出到文件out.txt
中。
in.txt
文件中的内容:
5.烹羊宰牛且为乐,会须一饮三百杯。
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
8.钟鼓馔玉不足贵,但愿长醉不愿醒。
7.与君歌一曲,请君为我倾耳听。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
9.古来圣贤皆寂寞,惟有饮者留其名。
11.主人何为言少钱,径须沽取对君酌。
3.人生得意须尽欢,莫使金樽空对月。
1.君不见黄河之水天上来,奔流到海不复回。
6.岑夫子,丹丘生,将进酒,杯莫停。
4.天生我材必有用,千金散尽还复来。
//1、创建字符输入缓冲流对象,读取文件in.txt
BufferedReader br = new BufferedReader(new FileReader("day20-advanced/in.txt"));
//2、创建字符缓冲输入流对象,写入文件out.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("day20-advanced/out.txt"));
//3、创建map集合,用来存储序号和内容
HashMap<Integer,String> map = new HashMap<>();
//4、创建list集合,用来存储序号并进行排序
ArrayList<Integer> list = new ArrayList<>();
//5、使用字符缓冲输入流按行读取字符串,并进行分割,将序号单独存储在list集合中,序号-内容存储在map集合中
String line;
while((line = br.readLine()) != null){
System.out.println(line);
// 转义字符要加上
String[] str = line.split("\\.");
list.add(Integer.parseInt(str[0]));
map.put(Integer.parseInt(str[0]),str[1]);
}
//6、使用Collections.sort方法进行排序
Collections.sort(list);
//7、遍历list集合,通过list集合的内容查找map集合中的值并拼接成字符串
for (Integer i : list) {
String val = map.get(i);
line = i + "." + val;
System.out.println(i+"." + val);// 需要写入的信息
//8、将拼接的字符串写入到out.txt中
bw.write(line);
bw.newLine();
}
//9、释放资源
bw.close();
br.close();
注意:
HashMap
是可以自动按照key
值排序的,不需要单独将key
值存储到list
集合中进行排序了。
七、转换流
7.1 字符编码和字符集
字符编码:自然语言的字符与二进制数之间的对应规则
按照某种规则,将字符存储到计算机中,成为编码。反之,将存储在计算机中的二进制数按照某种规则解析出来,成为解码。
字符集(Charset):也叫编码表。是一个系统支持的所有字符的集合。
7.2 编码引出的问题
在IDEA中,使用FileReader
读取文本文件,使用默认的UTF-8编码,如果文件是Windows系统创建的,默认中文的编码是gbk,这个时候使用IDEA读取就会显示为乱码。
7.3 [OutputStreamWriter]
java.io.OutputStreamWriter
继承自Writer
,是字符流通向字节流的桥梁;,可使用指定的 charset 将要写入流中的字符编码成字节。
构造方法
OutputStreamWriter(OutputStream out)
创建使用默认字符编码的OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集d的OutputStreamWriter
继承自父类的共性成员方法
void write(int c)
写入单个字符void write(char[] cbuf)
写入字符数组abstract void write(char[] cbuf, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数void write(String str, int off, int len)
写入字符串的某一部分,off 字符串的开始索引,len写的字符个数。void flush()
刷新该流的缓冲。void close()
关闭此流,但要先刷新它。
/*
使用转换流OutputStreamWriter写gbk格式的文件
*/
private static void write_gbk() throws IOException {
// 1、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day20-advanced/gbk.txt"),"gbk");
// 2、使用OutputStreamWriter对象的方法write,把字符转换为字节存储缓冲区中(编码)
osw.write("你好");
// 3、使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
osw.flush();
// 4、释放资源
osw.close();
}
/*
使用转换流OutputStreamWriter写UTF-8格式的文件
*/
private static void write_utf_8() throws IOException {
// 1、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day20-advanced/utf_8.txt"),"utf-8");
// 2、使用OutputStreamWriter对象的方法write,把字符转换为字节存储缓冲区中(编码)
osw.write("你好");
// 3、使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
osw.flush();
// 4、释放资源
osw.close();
}
7.4 [InputStreamReader]
java.io.InputStreamReader
继承自Reader
,是字节通向字符流的桥梁,它使用指定的charset
读取字节并将其解码为字符。
构造方法
InputStreamReader(InputStream in)
创建一个使用默认字符集的InputStreamReader
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的InputStreamReader
参数:
InputStream in
: 字节输入流,用来读取文件中保存的字节
String charsetName
: 指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用utf-8。
注意事项: 构造方法中指定的编码表名称要和文件的编码相同,否则会发现乱码
继承自父类的共性成员方法
int read()
读取单个字符并返回。int read(char[] cbuf)
一次读取多个字符,将字符读入数组void close()
关闭改流并释放与之相关的所有资源
private static void read_gbk() throws IOException {
//1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
//InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/gbk.txt"),"gbk");// 你好
InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/gbk.txt"));//���
//2、使用InputStreamReader对象中的方法reader读取文件
int len = 0;
while((len = isr.read()) != -1 ){
System.out.print((char) len);
}
//3、释放资源
isr.close();
}
private static void read_utf_8() throws IOException{
//1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
//InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/utf-8.txt"),"utf-8");
InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/utf-8.txt"));
//2、使用InputStreamReader对象中的方法reader读取文件
int len = 0;
while((len = isr.read()) != -1 ){
System.out.print((char) len);
}
//3、释放资源
isr.close();
}
7.5 练习【转换文件编码】
转换文件编码:将GBK编码的文本文件,转换成UTF-8编码的文本文件。
//1、创建转换输入流对象 charsetName 是gbk
InputStreamReader isr = new InputStreamReader(
new FileInputStream("day20-advanced/gbk.txt"),"gbk"
);
//2、创建转换输出流对象,默认charsetName是utf8
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("day20-advanced/gbk_2_utf8.txt")
);
//3、使用转换输入流对象通过gbk编码读取gbk格式的文件中数据
int len = 0;
while((len = isr.read()) != -1) {
//4、使用转换输出流对象通过utf编码写入到文件中
osw.write(len);
}
//5、释放资源
osw.close();
isr.close();
八、对象序列化与反序列化
8.1 概述
Java提供一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的相关信息。字节序列写出到文件之后,相当于在文件中持久保存了这个对象的信息。
反之,可以将改字节序列从文件中读取出来,重构对象,称之为反序列化。
8.2 [ObjectOutputStream]
java.io.ObjectOutputStream
继承自OutputStream
,对象的序列化流。
构造方法
ObjectOutputStream(OutputStream out)
创建写入指定OutputStream
的ObjectOutputStream
特有的成员方法
void writeObject(Object obj)
将指定的对象写入ObjectOutputStream
//1、创建ObjectOutputStream对象,构造方法中传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20-advanced/person.txt"));
//2、使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
oos.writeObject(new Person("赵晓",18));
//3、释放资源
oos.close();
8.3 [ObjectInputStream]
java.io.ObjectInputStream
继承自OutputStream
,对象的反序列化流。
构造方法
ObjectInputStream(InputStream in)
创建从指定InputStream
读取的ObjectInputStream
特有的成员方法
Object readObject()
从ObjectInputStream
中读取对象。
注意:readObject
方法声明抛出了ClassNotFoundException
(class文件找不到异常),当不存在对象的class文件时抛出此异常。
反序列化的前提:1.类必须实现Serisalizable
; 2.必须存在类对应的class
文件
//1、创建ObjectInputStream对象,构造方法中传递字节输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20-advanced/person.txt"));
//2、使用ObjectInputStream对象中的方法readObject读取保存对象的文件
Object o = ois.readObject();// 多态
//3、释放资源
ois.close();
//4、使用读取出来的对象(打印)
System.out.println(o);
8.4 练习【序列化集合】
要求:
- 将存有多个自定义对象的集合序列化操作,保存到
list.txt
文件中。 - 反序列化
list.txt
,并遍历集合,打印对象信息
/*
练习:序列化集合
分析:
1、把若干个学生对象,保存到集合中
2、序列化集合
3、反序列化堆区,再转换为集合对象
4、遍历集合,打印学生信息
*/
public class Demo03Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20-advanced/list.txt"));
List<Student> list = new ArrayList<>();
Student stu1 = new Student("张三",22);
Student stu2 = new Student("李四",22);
Student stu3 = new Student("赵晓",18);
Student stu4 = new Student("徐超",39);
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
oos.writeObject(list);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20-advanced/list.txt"));
Object o = ois.readObject();
ArrayList<Student> list2 = (ArrayList<Student>) o;
for (Student student : list2) {
System.out.println(student);
}
oos.close();
ois.close();
}
}
九、打印流
9.1 概述
平时我们在控制台打印输出,是调用print
方法和println
方法完成的,这两个方法都来自java.io.PrintStream
类,该类能够方便打印各种数据类型的值,是一种快捷的输出方式。
9.2 [PrintStream]
PrintStream
继承自OutputStream
只负责数据的输出,不负责数据的读取,与其他输出流不同,PrintStream
永远不会抛出 IOException
,有特有的方法。
构造方法
PrintStream(File file)
: 输出的目的地是一个文件PrintStream(OutputStream out)
: 输出的 目的地是一个字节输出流PrintStream(String fileName)
: 输出的目的地是一个文件路径
继承自父类的共性成员方法
void close()
:Closes this output stream and releases any system resources associated with this stream.void flush()
:Flushes this output stream and forces any buffered output bytes to be written out.void write(byte[] b)
: Writes b.length bytes from the specified byte array to this output stream.void write(byte[] b, int off, int len)
:Writes len bytes from the specified byte array starting at offset off to this output stream.abstract void write(int b)
:Writes the specified byte to this output stream.
特有的成员方法
void print(任意类型的值)
void println(任意类型的值并换行)
注意
如果使用继承自父类的write方法写数据,那么查看数据时候就会查询编码表 97->a;如果使用自己特有的方法print/println方法写数据,写的时候原样输出 97-> 97。
// 创建打印流对象,构造方法中绑定要输出的目的地
PrintStream ps = new PrintStream("day20-advanced/print.txt");
// 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表
ps.write(97); // a
//如果使用自己特有的方法print/println方法写数据,写的时候原样输出 97-> 97
ps.println(97);
ps.println("hello");
ps.println(823.23423);
//释放资源
ps.close();
9.3 System.setOut方法
使用System.setOut
方法改变输出语句的目的地为参数中传递的打印流的目的地。
static void setOut(PrintStream out)
:重新分配"标准"输出流。
System.out.println("我是在控制台输出的");
PrintStream ps = new PrintStream("day20-advanced/目的地是打印流.txt");
System.setOut(ps);// 把输出语句的目的地改为打印流的目的地
System.out.println("我是在打印流的目的地中输出");
ps.close()