一、IO流的概述
IO流:存储和读取数据的解决方案
IO流和File是息息相关的
1、IO流的分类
1.1、纯文本文件
word、Excel不是纯文本文件
而txt或者md文件是纯文本文件
2、小结
二、IO流的体系结构
三、字节流
1、FileOutputStream(字节输出流)
代码
结果
2、FileOutputStream写出数据的细节
3、FileOutputStream写数据的3种方式
3.1、一次写一个字节数据
3.2、一次写一个字节数组数据
3.3、一次写一个字节数组的部分数据
4、FileOutputStream写数据的两个小问题
4.1、换行写
4.2、续写
5、小结
6、FileInputStream(字节输入流)
示例代码
7、FileInputStream书写细节
8、FileInputStream的循环读取
细节误区:
9、文件拷贝
9.1、文件拷贝的基本代码
课堂练习:要求统计一下拷贝时间,单位毫秒
9.2、文件拷贝的弊端
9.3、文件拷贝弊端的解决方案(一次读多个字节)
示例代码:
改进方法:
9.4、文件拷贝代码改写
10、IO流中不同JDK版本捕获异常的方式
示例代码
简化方案:(了解即可)
代码实现:(但在实际开发中几乎所有的异常都是抛出处理的)
JDK7
JDK9
四、字符集
1、ASCII字符集(0-127)
1.1、计算机的存储规则(英文)
编码:
解码:
2、GBK字符集
主要掌握
2.1、计算机的存储规则(英文)(GBK)
2.2、计算机的存储规则(汉字)(GBK)
高位字节二进制一定以1开头,是为了和英文做出区分
解码:(直接转成十进制即可)
3.3、小结
核心1: GBK中,一个英文字母一个字节,二进制第一位是0
核心2: GBK中,一个中文汉字两个字节,二进制第一位是1
3、Unicode字符集
为了减少浪费,那么就有了UTF-8编码规则:
3.1、UTF-8是一个字符集吗?
不是,UTF-8只是Unicode字符集的一种编码方式
3.2、Unicode字符集练习
3.3、小结
五、为什么会有乱码?
1、如何不产生乱码?
2、扩展
因为拷贝的时候是一个字节一个字节拷贝的,数据没有出现丢失,那么只要记事本打开的字符集和编码方式也和数据源保持一致,就不会出现乱码。
3、Java中的编解码方法
六、字符流
1、字符流的体系架构
2、FileReader(字符输入流)
2.1、创建字符输入流对象
//1.创建对象并关联本地文件
FileReader fr = new FileReader("myio\\a.txt");
2.2、读取数据
示例代码:(空参的read方法)
示例代码(带参的read方法)
//read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中 //空参的read + 强转类型转换
2.3、释放资源
示例代码:
3、FileWriter(字符输出流)
3.1、构造方法
3.2、成员方法
3.3、FileWriter书写细节
4、字符输出流写出数据
4.1、write(int c):写出一个字符
示例代码:(字节流,每次只能操作一个字节)
4.2、write(String str):写出一个字符串
4.3、write(char[] cbuf):写出一个字符数组
5、字符流原理解析
5.1、字符输入流(FileReader)的原理解析
示例代码:(底层揭秘)
Ⅰ、创建字符输入流对象
Ⅱ、读取数据
扩展:
5.2、字符输出流(FileWriter)的原理解析
数据会先写到缓冲区中
示例代码:(当出现字节数组装满了、被刷新、被关闭三种任意一种时,字节数组中的数据都会被写入到文件当中)
七、综合练习
1、字节流和字符流的使用场景
2、练习一:拷贝(字节流)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) throws IOException {
//拷贝一个文件夹,考虑子文件夹
//1.创建对象表示数据源
File src = new File("D:\\aaa\\src");
//2.创建对象表示目的地
File dest = new File("D:\\aaa\\dest");
//3.调用方法开始拷贝
copydir(src,dest);
}
/*
* 作用:拷贝文件夹
* 参数一:数据源
* 参数二:目的地
*
* */
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();
//递归
//1.进入数据源
File[] files = src.listFiles();
//2.遍历数组
for (File file : files) {
if(file.isFile()){
//3.判断文件,拷贝
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else {
//4.判断文件夹,递归
copydir(file, new File(dest,file.getName()));
}
}
}
}
3、练习二:文件加密
小知识:如果一个数字它异或另外一个数字两次,那么它得到的数字就是它本身
^ : 异或 两边相同:false 两边不同:true
加密:
解密:
4、练习三:修改文件中的数据
细节1: 文件中的数据不要换行 细节2: bom头
在utf-8编码文件中BOM在文件头部,占用三个字节,用来标示该文件属于utf-8编码。其实UTF-8 的BOM对UFT-8没有作用,是为了支援UTF-16,UTF-32才加上的BOM,BOM签名的意思就是告诉编辑器当前文件采用何种编码,方便编辑器识别,但是BOM虽然在编辑器中不显示,但是会产生输出,就像多了一个空行。
示例代码:(常规写法)
//1.读取数据
FileReader fr = new FileReader("myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
String str = sb.toString();
String[] arrStr = str.split("-");//2-1-9-4-7-8
ArrayList<Integer> list = new ArrayList<>();
for (String s : arrStr) {
int i = Integer.parseInt(s);
list.add(i);
}
Collections.sort(list);
System.out.println(list);
//3.写出
FileWriter fw = new FileWriter("myio\\a.txt");
for (int i = 0; i < list.size(); i++) {
if(i == list.size() - 1){
fw.write(list.get(i) + "");
}else{
fw.write(list.get(i) + "-");
}
}
fw.close();
示例代码:(非常规写法—Stream && 方法引用)
//1.读取数据
FileReader fr = new FileReader("myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
Integer[] arr = Arrays.stream(sb.toString()
.split("-"))
.map(Integer::parseInt)
.sorted()
.toArray(Integer[]::new);
//3.写出
FileWriter fw = new FileWriter("myio\\a.txt");
String s = Arrays.toString(arr).replace(", ","-");
String result = s.substring(1, s.length() - 1);
fw.write(result);
fw.close();
八、IO流体系中的高级流
1、缓冲流
1.1、字节缓冲流
缓冲流是一个高级流,是对基本流的一个包装
利用字节缓冲流拷贝文件:(示例代码)
需求: * 利用字节缓冲流拷贝文件 * * 字节缓冲输入流的构造方法: * public BufferedInputStream(InputStream is) * * 字节缓冲输出流的构造方法: * public BufferedOutputStream(OutputStream os)
默认缓冲区都是8192,基本流的关闭在底层会自动执行
//1.创建缓冲流的对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myio\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myio\\a.txt"));
//2.循环读取并写到目的地
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
//3.释放资源
bos.close();
bis.close();
利用字节缓冲流拷贝文件:(一次读写多个字节)
//1.创建缓冲流的对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myio\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myio\\copy2.txt"));
//2.拷贝(一次读写多个字节)
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes)) != -1){
bos.write(bytes,0,len);
}
//3.释放资源
bos.close();
bis.close();
1.2、字节缓冲流提高效率的原理
倒手的过程是在内存中发生的,而内存的速度是非常快的,几乎可以忽略不计
它真正节约的是读和写的时候跟硬盘之间打交道的时间,其根本原因是因为字节流默认不使用缓冲区;切勿与字符流搞混
1.3、字符缓冲流
但是因为字符流本身就带有缓冲区,所以字符缓冲流对效率的提升就不是很明显。
构造方法:
特有方法:
package com.yaqi.mybufferedstream1;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
* 字符缓冲输入流:
* 构造方法:
* public BufferedReader(Reader r)
* 特有方法:
* public String readLine() 读一整行
*
* */
//1.创建字符缓冲输入流的对象
BufferedReader br = new BufferedReader(new FileReader("myio\\a.txt"));
//2.读取数据
//细节:
//readLine方法在读取的时候,一次读一整行,遇到回车换行结束
// 但是他不会把回车换行读到内存当中
/* String line1 = br.readLine();
System.out.println(line1);
String line2 = br.readLine();
System.out.println(line2);*/
String line;
while ((( line = br.readLine()) != null)){
System.out.println(line);
}
//3.释放资源
br.close();
}
}
1.4、小结
1.5、练习
1.5.1练习一:拷贝文件
package com.yaqi.mytest;
import java.io.*;
public class Test05 {
public static void main(String[] args) throws IOException {
/*
四种方式拷贝文件,并统计各自用时
*/
long start = System.currentTimeMillis();
//method1();
//method2(); //16.253秒
//method3(); //95.466秒
//method4(); //17.686秒
long end = System.currentTimeMillis();
System.out.println((end - start) / 1000.0 + "秒");
}
//字节流的基本流:一次读写一个字节4,588,568,576 字节
public static void method1() throws IOException {
FileInputStream fis = new FileInputStream("E:\\aaa\\CentOS-7-x86_64-DVD-1810.iso");
FileOutputStream fos = new FileOutputStream("myio\\copy.iso");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fos.close();
fis.close();
}
//字节流的基本流:一次读写一个字节数组
public static void method2() throws IOException {
FileInputStream fis = new FileInputStream("E:\\aaa\\CentOS-7-x86_64-DVD-1810.iso");
FileOutputStream fos = new FileOutputStream("myio\\copy.iso");
byte[] bytes = new byte[8192];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
}
//字节流的基本流:一次读写一个字节数组
public static void method3() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\aaa\\CentOS-7-x86_64-DVD-1810.iso"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myio\\copy.iso"));
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bos.close();
bis.close();
}
//字节流的基本流:一次读写一个字节数组
public static void method4() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\aaa\\CentOS-7-x86_64-DVD-1810.iso"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myio\\copy.iso"));
byte[] bytes = new byte[8192];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.close();
bis.close();
}
}
1.5.2练习二:修改文本顺序
示例代码:(Collections排序)
示例代码:(TreeMap排序)
1.5.3练习三:软件的运行次数
实现一个验证程序运行次数的小程序,要求如下: 1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用~ 2.程序运行演示如下: 第一次运行控制台输出: 欢迎使用本软件,第1次使用免费~ 第二次运行控制台输出: 欢迎使用本软件,第2次使用免费~ 第三次运行控制台输出: 欢迎使用本软件,第3次使用免费~ 第四次及之后运行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用~
//原则:
//IO:随用随创建
// 什么时候不用什么时候关闭
2、转换流(字符流的一种)
转换流是字符流和字节流之间的桥梁
2.1、转换流的基本用法
指定字符集读取:
package com.yaqi.myconvertstream;
import java.io.*;
import java.nio.charset.Charset;
public class ConvertStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
利用转换流按照指定字符编码读取(了解)
因为JDK11:这种方式被淘汰了。替代方案(掌握)
F:\JavaSE最新版\day29-IO(其他流)\资料\gbkfile.txt
*/
/* //1.创建对象并指定字符编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("myio\\gbkfile.txt"),"GBK");
//2.读取数据
int ch;
while ((ch = isr.read()) != -1){
System.out.print((char)ch);
}
//3.释放资源
isr.close();*/
FileReader fr = new FileReader("myio\\gbkfile.txt", Charset.forName("GBK"));
//2.读取数据
int ch;
while ((ch = fr.read()) != -1){
System.out.print((char)ch);
}
//3.释放资源
fr.close();
}
}
利用转换流按照指定字符编码写出数据:
import java.io.*;
import java.nio.charset.Charset;
public class ConvertStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
利用转换流按照指定字符编码写出
*/
/*
//1.创建转换流的对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myio\\b.txt"),"GBK");
//2.写出数据
osw.write("你好你好");
//3.释放资源
osw.close();*/
FileWriter fw = new FileWriter("myio\\c.txt", Charset.forName("GBK"));
fw.write("你好你好");
fw.close();
}
}
2,2、练习一:转换文件编码
将本地文件中的GBK文件,转成UTF-8
package com.yaqi.myconvertstream;
import java.io.*;
import java.nio.charset.Charset;
public class ConvertStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
将本地文件中的GBK文件,转成UTF-8
*/
//1.JDK11以前的方案
/* InputStreamReader isr = new InputStreamReader(new FileInputStream("myio\\b.txt"),"GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myio\\d.txt"),"UTF-8");
int b;
while((b = isr.read()) != -1){
osw.write(b);
}
osw.close();
isr.close();*/
//2.替代方案
FileReader fr = new FileReader("myio\\b.txt", Charset.forName("GBK"));
FileWriter fw = new FileWriter("myio\\e.txt",Charset.forName("UTF-8"));
int b;
while ((b = fr.read()) != -1){
fw.write(b);
}
fw.close();
fr.close();
}
}
2.3、练习二:读取文件中的数据
package com.yaqi.myconvertstream;
import java.io.*;
public class ConvertStreamDemo4 {
public static void main(String[] args) throws IOException {
/*
利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
//1.字节流在读取中文的时候,是会出现乱码的,但是字符流可以搞定
//2.字节流里面是没有读一整行的方法的,只有字符缓冲流才能搞定
*/
/* FileInputStream fis = new FileInputStream("myio\\a.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String str = br.readLine();
System.out.println(str);
br.close();*/
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("myio\\a.txt")));
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
}
2.4、小结
3、序列化流/对象操作输出流(字节流的一种)(ObjectOutputStream)
3.1、构造方法
3.2、成员方法
3.3、利用序列化流,把一个对象写到本地文件中
3.4、序列化流的小细节(NotSerializableException)
4、反序列化流/对象操作输入流(ObjectInputStream)
4.1、利用反序列化流/对象操作输入流,把文件中的对象读到程序当中
4.2、序列号不匹配
4.3、瞬态关键字(不会把当前属性序列化到本地文件中)
4.4、小结
4.5、综合练习:读写多个对象(先写再读)
5、打印流
5.1、字节打印流的构造方法
5.2、字节打印流的成员方法
5.3、占位符扩展
5.4、字符打印流的构造方法
5.5、字符打印流的成员方法
5.6、打印流的应用场景
5.7、小结
6、解压缩流/压缩流
6.1、解压缩流
6.2、压缩流(压缩单个文件)
6.3、压缩流(压缩文件夹)
7、Commons-io
7.1、Commons-io使用步骤
7.2、Commons-io常见方法
8、Hutool工具包
8.1、IO包的封装类
8.2、FileUtil类
九、综合练习
1、制造假数据
1.1、爬取姓氏
1.2、爬取名字
1.3、数据处理
1.4、数据处理并写出
1.5、利用HuTool包生成假数据
2、随机点名器
2.1、练习1:普通随机点名
2.2、练习2:加入了概率和统计
2.3、练习3:作弊功能
2.4、练习4:随机不能重复,自动开启第二轮
2.5、练习5:带权重的随机
3、登录注册
3.1、练习1:写一个登录小案例
3.2、练习2:添加锁定账号功能
3.3、练习3:补全拼图小游戏的登录注册(考虑多个用户)
4、游戏的存档和读档
4.1、存档
4.2、读档
4.3、修改bug
5、游戏配置
5.1、配置文件的优点
5.2、常见的配置文件
5.3、properties配置文件
5.4、Properties作为Map集合的操作
5.5、Properties跟IO流结合的操作(store)
5.6、读取Properties(load)
5.7、拼图游戏的配置文件
4、每日一记(IO的最终大作业)