一. IO流 - 字符流
1.1 FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去
构造器 | 说明 |
public FileReader(File file) | 创建字符输入流管道与源文件接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件接通 |
方法名称 | 说明 |
public int read() | 每次读取一个字符返回,如果发现没有数据可读会返回-1 |
public int read(char[] buffer) | 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1 |
public class demo {
public static void main(String[] args) {
try (
//创建一个文件字符输入流管道与源文件接通
Reader fr = new FileReader("demo\\src\\wosun.txt");
){
//读取文本文件的内容
//1.每次读取一个字符(性能较差)
// int c; //记住每次读取的字符编号
// while((c = fr.read()) != -1){
// System.out.print((char)c);
// }
//2.每次读取多个字符(性能不错)
char[] buffer = new char[10];
int len; //记住每次读取多少个字符
while((len = fr.read(buffer)) != -1){
System.out.print(new String(buffer, 0, len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.2 FileWriter(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去
构造器 | 说明 |
public FileWriter(File file) | 创建字符输出流管道与源文件对象接通 |
public FileWriter(String filepath) | 创建字符输出流管道与源文件路径接通 |
public FileWriter(File file, boolean append) | 创建字符输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath, boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
public void write(int a) | 写一个字符 |
public void write(String str) | 写一个字符串 |
public void write(String str, int off, int len) | 写一个字符串的一部分 |
public void write(char[] cbuf) | 写入一个字符数组 |
public void write(char[] cbuf, int off, int len) | 写入一个字符数组的一部分 |
public class demo {
public static void main(String[] args) {
try (
//创建一个文件字符输出流关单与目标文件接通
//Writer fw = new FileWriter("demo/src/new_wosun.txt"); //覆盖管道
Writer fw = new FileWriter("demo/src/new_wosun.txt",true); //追加数据管道
){
//1.public void write(int a) 写一个字符
fw.write('a');
fw.write(97);
fw.write('中'); //写一个字符进去
fw.write("\r\n"); //换行
//2.public void write(String str) 写一个字符串
fw.write("我爱你我的国anc");
fw.write("\r\n");
//3.public void write(String str, int off, int len) 写一个字符串的一部分
fw.write("我爱你我的国anc",0,6);
fw.write("\r\n");
//public void write(char[] cbuf) 写入一个字符数组
char[] buffer = {'小','小','J','a','v','a'};
fw.write(buffer);
fw.write("\r\n");
//public void write(char[] cbuf, int off, int len) 写入一个字符数组的一部分
fw.write(buffer,2,4);
fw.write("\r\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符输出流使用时的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效
原理是:字符输出流在写出数据的时候,先将内容写入缓冲区,然后通过刷新流操作,将缓冲区中内容一次性写出(缓冲区满会自动刷新)
public class demo {
public static void main(String[] args) throws Exception {
Writer fw = new FileWriter("demo/src/new_wosun.txt"); //追加数据管道
fw.write('a');
fw.write(97);
fw.write('中'); //写一个字符进去
fw.write("\r\n"); //换行
fw.flush(); //刷新流
fw.write("你好");
fw.close(); //关闭流,关闭流包含刷新操作
}
}
二. IO流 - 缓冲流
缓冲流的作用:对原始流进行包装,以提高原始流读写数据的性能
2.1 字节缓冲流
包括字节缓冲输入流和字节缓冲输出流
作用:提高字节流读写数据的性能
原理:字节缓冲输入流自带8KB缓冲池;字节缓冲输出流也自带8KB缓冲池
构造器 | 说明 |
public BufferedInputStream(InputStream is) | 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能 |
public BufferedOutputStream(OutputStream os) | 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高读数据的性能 |
public class demo {
public static void main(String[] args) {
try (
InputStream is = new FileInputStream("demo/src/wosun.txt");
//定义一个字节缓冲输入流包装原始的字节输入流
InputStream bis = new BufferedInputStream(is,8192*2);
OutputStream os = new FileOutputStream("demo/src/wosun_bak.txt");
//定义一个字节缓冲输出流包装原始的字节输出流
OutputStream bos = new BufferedOutputStream(os,8192*2);
){
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
System.out.println("复制完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 字符缓冲流
2.2.1 BufferedReader(字符缓冲输入流)
作用:自带8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能
构造器 | 说明 |
public BufferedReader(Reader r) | 把低级的字符输入流包装成字符缓冲输入流管道,从而提高读数据的性能 |
字符缓冲输入流新增的功能:按照行读取字符
方法 | 说明 |
public String readLine() | 读取一行数据返回,如果没有数据可读了,会返回null |
public class demo {
public static void main(String[] args) {
try (
Reader fr = new FileReader("demo/src/wosun.txt");
//创建一个字符缓冲输入流包装原始的字符输入流
BufferedReader bfr = new BufferedReader(fr);
){
// char[] buffer = new char[3];
// int len;
// while ((len = bfr.read(buffer)) != -1){
// System.out.println(new String(buffer,0,len));
// }
// System.out.println(bfr.readLine());
// System.out.println(bfr.readLine());
// System.out.println(bfr.readLine());
// System.out.println(bfr.readLine());
// System.out.println(bfr.readLine()); //没有内容,输出null
String line; //记录每次读取的每一行数据
while((line = bfr.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2.2 BufferedWriter(字符缓冲输出流)
作用:自带8K(8192)的字符缓冲池,可以提高字符输出流读取字符数据的性能
构造器 | 说明 |
public BufferedWriter(Writer r) | 把低级的字符输出流包装成一个字符缓冲输出流管道,从而提高字符输出流写数据的性能 |
字符缓冲输出流新增的功能:换行
方法 | 说明 |
public void newLine() | 换行 |
public class demo {
public static void main(String[] args) {
try (
Writer fw = new FileWriter("demo/src/wosun_back.txt",true);
//创建一个字符缓冲输出流管道包装原始的字符输出流
BufferedWriter bw = new BufferedWriter(fw);
){
bw.write('a');
bw.write(97);
bw.write('中');
bw.newLine();
bw.write("你好你好你222adc好好");
bw.write("\r\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2.3 字符缓冲流综合案例
public class demo {
public static void main(String[] args) {
//恢复出师表的顺序到新文件中
//3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
//7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原;庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
//5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞亮死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
//9.今当远离,临表涕零,不知所言。
//6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事。由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣!
//1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。
//8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎。陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
//2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理;不宜偏私,使内外异法也。
//4.将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
try(
// //创建文件字符输入流
// Reader r = new FileReader("demo/src/wosun.txt");
// //创建字符缓冲输入流
// BufferedReader br = new BufferedReader(r);
BufferedReader br = new BufferedReader(new FileReader("demo/src/wosun.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("demo/src/wosun_copy.txt"))
) {
//定义一个ArrayList集合用来存储每段内容
List<String> arrayList = new ArrayList<>();
String s; //记录每一行的内容
while((s = br.readLine()) != null){
arrayList.add(s);
}
//对arrayList里面的内容进行排序
Collections.sort(arrayList); //默认按照首位排序
System.out.println(arrayList);
//遍历arrayList的内容,一次写出去到一个新文件中
for (String s1 : arrayList) {
bw.write(s1);
bw.newLine();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2.4 原始流、缓冲流的性能分析
低级的字节流按照字节数组的形式复制文件的性能不一定比缓冲字节流按照字节数组的形式复制文件的性能差,具体要看这个字节数组的容量。但字节数组的容量也不是越大一定越好,达到一定容量之后,复制的效率提升不会太大。
public class demo {
//复制的视频路径
private static final String SRC_File = "D:\\zm\\111.mp4";
//目的地路径
private static final String DEST_File ="D:\\zm\\test\\";
public static void main(String[] args) {
//复制一个42.4MB的视频文件
//低级字节流一个一个字节复制耗时:626.895s (太慢了)
//低级字节流使用字节数组(1KB)复制耗时:0.697s
//缓冲流一个一个字节复制耗时:0.93s
//缓冲流使用字节数组复制耗时:0.137s
//将低级字节流和缓冲流用的字符数组容量扩大(8KB)
//低级字节流使用字节数组复制耗时:0.128s
//缓冲流使用字节数组复制耗时:0.122s
//将低级字节流和缓冲流用的字符数组容量扩大(16KB)
//低级字节流使用字节数组复制耗时:0.068s
//缓冲流使用字节数组复制耗时:0.067s
//将低级字节流和缓冲流用的字符数组容量扩大(32KB)
//低级字节流使用字节数组复制耗时:0.051s
//缓冲流使用字节数组复制耗时:0.049s
//copy01();
copy02();
//copy03();
copy04();
}
private static void copy01(){ //一个个字节的形式复制文件
long startTime = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(SRC_File);
OutputStream os = new FileOutputStream(DEST_File + "copy1.mp4");
) {
int a; //用于记住读取的字节
while((a = is.read()) != -1){
os.write(a);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime)/1000.0 +"s");
}
private static void copy02(){ //字节数组的形式复制文件
long startTime = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(SRC_File);
OutputStream os = new FileOutputStream(DEST_File + "copy2.mp4");
) {
byte[] buffer = new byte[1024 * 32]; //用于记住读取的字节的字符数组(大小1KB 8KB 16KB 32KB)
int len; //用于记住读取的字节长度
while((len = is.read(buffer)) != -1){
os.write(buffer,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime)/1000.0 +"s");
}
private static void copy03(){ //字节缓冲流 一个个字节的形式复制文件
long startTime = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(SRC_File);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(DEST_File + "copy3.mp4");
BufferedOutputStream bos = new BufferedOutputStream(os);
) {
int a; //用于记住读取的字节
while((a = bis.read()) != -1){
bos.write(a);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime)/1000.0 +"s");
}
private static void copy04(){ //字节缓冲流 字节数组的形式复制文件
long startTime = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(SRC_File);
BufferedInputStream bis = new BufferedInputStream(is,1024 * 32);
OutputStream os = new FileOutputStream(DEST_File + "copy4.mp4");
BufferedOutputStream bos = new BufferedOutputStream(os,1024 * 32);
) {
//超过8KB需要修改缓冲流默认的缓冲池大小
byte[] buffer = new byte[1024 * 32]; //用于记住读取的字节的字符数组
int len; //用于记住读取的字节长度
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime)/1000.0 +"s");
}
}
三. IO流 - 转换流
问题引入:不同编码读取出现乱码的问题
public class demo {
public static void main(String[] args) {
try (
//创建一个文件字符输入流与源文件接通
//代码编码:UTF-8 文件的编码:UTF-8 不会乱码
//代码编码:UTF-8 文件的编码:GBK 会乱码
Reader r = new FileReader("demo/src/wosun_back.txt");
//把文件字符输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(r);
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
因此,引入字符输入/输出转换流来解决乱码问题
3.1 InputStreamReader(字符输入转换流)
作用:解决不同编码时,字符流读取文本内容乱码的问题
解决思想:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不会乱码了
构造器 | 说明 |
public InputStreamReader(InputStream is) | 把原始的字节输入流,按照代码默认编码转换成字符输入流(与直接用FileReader的效果一样) |
public InputStreamReader(InputStream is , String charset) | 把原始的字节输入流,按照指定字符集编码转成字符输入流 |
public class demo {
public static void main(String[] args) {
try (
//得到文件的原始字节流(GBK的字节流形式)
InputStream is = new FileInputStream("demo/src/wosun_back.txt");
//字符输入转换流:把原始的字节输入流按照指定的字符集编码转换成字符输入流
Reader isr = new InputStreamReader(is,"GBK");
//把字符输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2 OutputStreamWriter(字符输出转换流)
作用:可以控制写出去的字符使用什么字符集编码
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,这样写出去的字符就会用该字符集编码了
构造器 | 说明 |
public OutputStreamWriter(OutputStream os) | 可以把原始的字节输出流,按照代码默认编码转换成字符输出流 |
public OutputStreamWriter(OutputStream os , String charset) | 可以把原始的字节输出流,按照指定编码转换成字符输出流 |
public class demo {
public static void main(String[] args) {
try (
//创建一个文件字节输出流
OutputStream os = new FileOutputStream("demo/src/wosun_back.txt");
//字符输出转换流:把原始的字节输出流按照指定的字符集编码转换成字符输出转换流
Writer osw = new OutputStreamWriter(os,"GBK");
//把字符输出转换流包装成缓冲字符输出流
BufferedWriter bw = new BufferedWriter(osw);
){
bw.write("这里是我的家abc");
bw.write("acf哪里是我的家");
} catch (Exception e) {
e.printStackTrace();
}
}
}
四. IO流 - 打印流
4.1 PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印(即写)数据出去,能实现打印啥出去就是啥出去(如打印97就是97,打印a就是a)
public class demo {
public static void main(String[] args) {
try(
//创建一个打印流管道
// PrintStream ps = new PrintStream("demo/src/wosun_back.txt", String.valueOf(Charset.forName("GBK")));
PrintStream ps = new PrintStream("demo/src/wosun_back.txt");
) {
ps.println(97);
ps.println('a');
ps.println("你好你好你好");
ps.println(true);
ps.println(99.0);
ps.write(97); //'a'
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class demo {
public static void main(String[] args) {
try(
//创建一个打印流管道
// PrintStream ps = new PrintStream("demo/src/wosun_back.txt", String.valueOf(Charset.forName("GBK")));
// PrintWriter pw = new PrintWriter("demo/src/wosun_back.txt"); //高级流不能直接追加数据
//想要追加数据的话
PrintWriter pw = new PrintWriter(new FileWriter("demo/src/wosun_back.txt",true));
) {
pw.println(97);
pw.println('a');
pw.println("你好你好你好");
pw.println(true);
pw.println(99.0);
pw.write(97); //'a'
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印流的一种应用:输出语句的重定向
可以把输出语句的打印位置改到某个文件中去
public class demo {
public static void main(String[] args) {
System.out.println("你想是怎样的人");
System.out.println("你就是怎样的人");
try (
PrintStream ps = new PrintStream("demo/src/wosun.txt");
){
//把系统默认的打印流对象改成自己设置的打印流
System.setOut(ps);
System.out.println("你想成为怎样的人");
System.out.println("你就会离这个目标不会太远");
} catch (Exception e) {
e.printStackTrace();
}
}
}
五. IO流 - 数据流
5.1 DataOutputStream(数据输出流)
允许把数据和其类型一并写出去
5.2 DataInputStream(数据输入流)
用于读取数据输出流写出去的数据
//数据输出流
public class demo {
public static void main(String[] args) {
try (
DataOutputStream dos = new DataOutputStream(new FileOutputStream("demo/src/wosun.txt"));
){
dos.writeInt(97);
dos.writeDouble(99.9);
dos.writeBoolean(true);
dos.writeUTF("你好你好nihao你好哇");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//数据输入流
public class demo2 {
public static void main(String[] args) {
//使用数据输入流读取特定类型的数据
try (
DataInputStream dis = new DataInputStream(new FileInputStream("demo/src/wosun.txt"));
){
//读取的顺序要和输出的顺序一致
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());
} catch (Exception e) {
e.printStackTrace();
}
}
}
六. IO流 - 序列化流
对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来
6.1 ObjectOutputStream(对象字节输出流)
可以把Java对象进行序列化:把Java对象存入到文件中去
注意:对象如果需要序列化,必须要实现序列化接口
6.2 ObjectInputStream(对象字节输入流)
可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中去
//序列化
public class demo {
public static void main(String[] args) {
try (
//2.创建一个对象字节输出流包装原始的字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("demo/src/wosun.txt"));
){
//1.创建一个Java对象
User u = new User("小小","wosun",10);
//3.序列化对象到文件中去
oos.writeObject(u);
System.out.println("序列化对象成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//反序列化
public class demo2 {
public static void main(String[] args) {
//对象字节输入流的使用:反序列化对象
try (
//1.创建一个对象字节输入流管道,包装低级的字节输入流与源文件接通
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("demo/src/wosun.txt"));
){
User u = (User) ois.readObject(); //readObject()返回的是Object类型,要根据Java对象的类型进行强转
System.out.println(u);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//User
//对象如果需要序列化,必须要实现序列化接口
public class User implements Serializable {
private String userName;
//transient 这个成员变量将不参与序列化
private transient String password;
private int age;
public User() {
}
public User(String userName, String password, int age) {
this.userName = userName;
this.password = password;
this.age = age;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
七. IO框架
什么是框架?
· 解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的
· 好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
· 框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去
什么是IO框架?
· 封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等
7.1 Commons-io
Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率
public class demo {
public static void main(String[] args) throws Exception {
FileUtils.copyFile(new File("demo/src/wosun.txt"),new File("demo/src/wosun_copy.txt"));
FileUtils.copyDirectory(new File("demo/src/test"),new File("demo/src/test_copy"));
FileUtils.deleteDirectory(new File("D:\\zm\\ddd"));
//Java提供的原生的一行代码搞定很多事情
//System.out.println(Files.readString(Path.of("demo/src/wosun.txt")));
//Files.copy(Path.of("demo/src/wosun.txt"),Path.of("demo/src/wosun_c.txt"))
}
}