能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流
一、缓冲流
缓冲流,也叫高效流,是对4个基本的FileXxx
流的增强,所以也是4个流,按照数据类型分类:
-
字节缓冲流:
BufferedInputStream
,BufferedOutputStream
-
字符缓冲流:
BufferedReader
,BufferedW
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
1. 字节缓冲流
构造方法
-
public BufferedInputStream(InputStream in)
:创建一个 新的缓冲输入流。 -
public BufferedOutputStream(OutputStream out)
: 创建一个新的缓冲输出流。
构造举例,代码如下:
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
import java.io.*;
import java.nio.Buffer;
public class test1 {
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("day29\\a.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("day29\\b.txt"));
byte[] bytes=new byte[1024];
int len;
while ((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
2.字符缓冲流
构造方法
-
public BufferedReader(Reader in)
:创建一个 新的缓冲输入流。 -
public BufferedWriter(Writer out)
: 创建一个新的缓冲输出流。
构造举例,代码如下:
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
特有方法
字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
BufferedReader:
public String readLine()
: 读一行数据,如果没有数据可读就返回null,但是不会把回车换行读到内存当中BufferedWriter:
public void newLine()
: 跨平台的换行。
缓冲流为什么能提高性能?
缓冲流自带长度为8192的缓冲区
可以显著提高字节流的读写性能
对于字符流提升不明显,对于字符缓冲流而言关键点是两个特有的方法
二、转换流
是字节流和字符流之间的桥梁
可以根据字符集一次读取多个字节,读数据不会乱码了。
作用1∶指定字符集读写(JDK11淘汰了)
作用2:字节流想要使用字符流中的方法
package com.itheima.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();
}
}
三、序列化流/对象操作输出流
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。看图理解序列化:
构造方法:
public ObjectOutputStream(OutputStream out) 把基本流变成高级流
成员方法:
public final void writeObject(Object obj) 把对象序列化(写出)到文件中去
一个对象要想序列化,必须满足两个条件:
-
该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 -
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。 -
package com.itheima.myobjectstream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class ObjectStreamDemo1 { public static void main(String[] args) throws IOException { /* 需求: 利用序列化流/对象操作输出流,把一个对象写到本地文件中 构造方法: public ObjectOutputStream(OutputStream out) 把基本流变成高级流 成员方法: public final void writeObject(Object obj) 把对象序列化(写出)到文件中去 */ //1.创建对象 Student stu = new Student("zhangsan",23,"南京"); //2.创建序列化流的对象/对象操作输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myio\\a.txt")); //3.写出数据 oos.writeObject(stu); //4.释放资源 oos.close(); } }
四、反序列流/对象操作输入流
- 可以把序列化到本地中的对象读取到程序中来
ObjectInputStream类
构造方法:
public ObjectInputStream(InputStream out) 把基本流变成高级流
成员方法:
public Object readObject() 把序列化到本地文件中的对象,读取到程序中来
package com.itheima.myobjectstream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectStreamDemo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建反序列化流的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myio\\a.txt"));
//2.读取数据
Student o = (Student) ois.readObject();
//3.打印对象
System.out.println(o);
//4.释放资源
ois.close();//Student{name = zhangsan, age = 23, address = null}
}
}
序列化流/反序列化流的细节汇总
1.使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。
否则,会出现NotSerializableException异常
2.序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了
3.序列化对象后,修改了Javabean类,再次反序列化,会不会有问题?
会出问题,会抛出InvalidclassException异常
解决方案:给Javabean类添加serialversionUID(序列号、版本号)
4.如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
五、打印流
平时我们在控制台打印输出,是调用print
方法和println
方法完成的,这两个方法都来自于java.io.PrintStream
类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
打印流
分类:打印流一般是指:PrintStream,PrintWriter两个类
特点1:打印流只操作文件目的地,不操作数据源
特点2:特有的写出方法可以实现,数据原样写出
例如:打印:97 文件中:97
打印: true 文件中: true
特点3:特有的写出方法,可以实现自动刷新,自动换行
打印一次数据=写出+换行+刷新
字节打印流:
构造方法
public PrintStream(OutputStream/File/String) 关联字节输出流/文件/文件路径
public PrintStream(String fileName, Charset charset) 指定字符编码
public PrintStream(OutputStreamout, boolean autoFlush) 自动刷新
public PrintStream(OutputStream out, boolean autoFlush, String encoding) 指定字符编码且自动刷新
成员方法:
public void write(int b) 常规方法:规则跟之前一样,将指定的字节写出
public void println(Xxx xx) 特有方法:打印任意数据,自动刷新,自动换行
public void print(Xxx xx) 特有方法:打印任意数据,不换行
public void printf(String format, Object... args) 特有方法:带有占位符的打印语句,不换行
字节流底层没有缓冲区,开不开自动刷新都一样
字符打印流:
构造方法
public PrintWriter(Write/File/String) 关联字节输出流/文件/文件路径
public PrintWriter(String fileName, Charset charset) 指定字符编码
public PrintWriter(Write, boolean autoFlush) 自动刷新
public PrintWriter(Write out, boolean autoFlush, String encoding) 指定字符编码且自动刷新
成员方法:
public void write(int b) 常规方法:规则跟之前一样,将指定的字节写出
public void println(Xxx xx) 特有方法:打印任意数据,自动刷新,自动换行
public void print(Xxx xx) 特有方法:打印任意数据,不换行
public void printf(String format, Object... args) 特有方法:带有占位符的打印语句,不换行
字符流底层有缓冲区,想要自动刷新需要开启。
小结:打印流有几种?各有什么特点?
有字节打印流和字符打印流两种
打印流不操作数据源,只能操作目的地
字节打印流:默认自动刷新,特有的println自动换行
字符打印流:自动刷新需要开启,特有的println自动换行
六、解压缩流
解压本质:把每一个ZipEntry按照层级拷贝到本地另一个文件夹中
package com.itheima.myzipstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/*
* 解压缩流
*
* */
public class ZipStreamDemo1 {
public static void main(String[] args) throws IOException {
//1.创建一个File表示要解压的压缩包
File src = new File("D:\\aaa.zip");
//2.创建一个File表示解压的目的地
File dest = new File("D:\\");
//调用方法
unzip(src,dest);
}
//定义一个方法用来解压
public static void unzip(File src,File dest) throws IOException {
//解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
//创建一个解压缩流用来读取压缩包中的数据
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
//要先获取到压缩包里面的每一个zipentry对象
//表示当前在压缩包中获取到的文件或者文件夹
ZipEntry entry;
while((entry = zip.getNextEntry()) != null){
System.out.println(entry);
if(entry.isDirectory()){
//文件夹:需要在目的地dest处创建一个同样的文件夹
File file = new File(dest,entry.toString());
file.mkdirs();
}else{
//文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));
int b;
while((b = zip.read()) != -1){
//写到目的地
fos.write(b);
}
fos.close();
//表示在压缩包中的一个文件处理完毕了。
zip.closeEntry();
}
}
zip.close();
}
}
七、压缩流
压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中
package com.itheima.myzipstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
* 压缩流
* 需求:
* 把D:\\aaa文件夹压缩成一个压缩包
* */
//1.创建File对象表示要压缩的文件夹
File src = new File("D:\\aaa");
//2.创建File对象表示压缩包放在哪里(压缩包的父级路径)
File destParent = src.getParentFile();//D:\\
//3.创建File对象表示压缩包的路径
File dest = new File(destParent,src.getName() + ".zip");
//4.创建压缩流关联压缩包
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
//5.获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
toZip(src,zos,src.getName());//aaa
//6.释放资源
zos.close();
}
/*
* 作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
* 参数一:数据源
* 参数二:压缩流
* 参数三:压缩包内部的路径
* */
public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {
//1.进入src文件夹
File[] files = src.listFiles();
//2.遍历数组
for (File file : files) {
if(file.isFile()){
//3.判断-文件,变成ZipEntry对象,放入到压缩包当中
ZipEntry entry = new ZipEntry(name + "\\" + file.getName());//aaa\\no1\\a.txt
zos.putNextEntry(entry);
//读取文件中的数据,写到压缩包
FileInputStream fis = new FileInputStream(file);
int b;
while((b = fis.read()) != -1){
zos.write(b);
}
fis.close();
zos.closeEntry();
}else{
//4.判断-文件夹,递归
toZip(file,zos,name + "\\" + file.getName());
// no1 aaa \\ no1
}
}
}
}
八、常用工具包(Commons-io)
Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包。
作用:提高IO流的开发效率。
Commons-io使用步骤
- 在项目中创建一个文件夹:lib
- 将jar包复制粘贴到lib文件夹
- 右键点击jar包,选择Add as Library ->点击OK
- 在类中导包使用
FileUtils类
static void copyFile(File srcFile, File destFile) 复制文件
static void copyDirectory(File srcDir, File destDir) 复制文件夹
static void copyDirectoryToDirectory(File srcDir, File destDir) 复制文件夹
static void deleteDirectory(File directory) 删除文件夹
static void cleanDirectory(File directory) 清空文件夹
static String readFileToString(File file, Charset encoding) 读取文件中的数据变成成字符串
static void write(File file, CharSequence data, String encoding) 写出数据IOUtils类
public static int copy(InputStream input, OutputStream output) 复制文件
public static int copyLarge(Reader input, Writer output) 复制大文件
public static String readLines(Reader input) 读取数据
public static void write(String data, OutputStream output) 写出数据
/* File src = new File("myio\\a.txt");
File dest = new File("myio\\copy.txt");
FileUtils.copyFile(src,dest);*/
/*File src = new File("D:\\aaa");
File dest = new File("D:\\bbb");
FileUtils.copyDirectoryToDirectory(src,dest);*/
/*File src = new File("D:\\bbb");
FileUtils.cleanDirectory(src);*/
九、常用工具包(hutool)
介绍:
Commons是国人开发的开源工具包,里面有很多帮助我们提高开发效率的API
比如:
DateUtil 日期时间工具类
TimeInterval 计时器工具类
StrUtil 字符串工具类
HexUtil 16进制工具类
HashUtil Hash算法类
ObjectUtil 对象工具类
ReflectUtil 反射工具类
TypeUtil 泛型类型工具类
PageUtil 分页工具类
NumberUtil 数字工具类
FileUtil类:
file:根据参数创建一个file对象
touch:根据参数创建文件writeLines:把集合中的数据写出到文件中,覆盖模式。
appendLines:把集合中的数据写出到文件中,续写模式。
readLines:指定字符编码,把文件中的数据,读到集合中。
readUtf8Lines:按照UTF-8的形式,把文件中的数据,读到集合中copy:拷贝文件或者文件夹
/* File file1 = FileUtil.file("D:\\", "aaa", "bbb", "a.txt");
System.out.println(file1);//D:\aaa\bbb\a.txt
File touch = FileUtil.touch(file1);
System.out.println(touch);
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("aaa");
File file2 = FileUtil.writeLines(list, "D:\\a.txt", "UTF-8");
System.out.println(file2);*/
/* ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("aaa");
File file3 = FileUtil.appendLines(list, "D:\\a.txt", "UTF-8");
System.out.println(file3);*/
List<String> list = FileUtil.readLines("D:\\a.txt", "UTF-8");
System.out.println(list);