基本流与高级流
以下四种是IO流中最基本的流,称为基本流
字节流的FileInputStream和FileOutputStream
字符流的FileReader和FileWriter
高级流就是在基本流的基础上,额外又封装了一些功能,也是字节流或字符流的实现类
包括:缓冲流,转换流,序列化流与反序列化流,打印流,压缩流与解压缩流
缓冲流
缓冲流是属于字节流和字符流的高级流
分类
字节缓冲流
说明
底层自带了长度为8192的缓冲区提高性能
创建字节缓冲流对象
方法名称 | 说明 |
public BufferedInputStream (InputStream is) | 把基本流包装成高级流, 参数是基本流 |
public BufferedOutputStream (OutputStream os) | 把基本流包装成高级流, 参数是基本流 |
代码演示
import java.io.*;
public class Test5 {
public static void main(String[] args) throws IOException {
//将本模块下a.txt中的数据拷贝到本模块b.txt文件中
/*//创建基本流
FileInputStream fis = new FileInputStream("day04\\a.txt");
FileOutputStream fos = new FileOutputStream("day04\\b.txt");
//将基本流包装为缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);*/
//可以简化为
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day04\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day04\\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 |
注意:readLine方法在读取的时候,一次读一整行,遇到回车换行结束,但是不会把回车换行读到内存当中
字符缓冲输出流特有方法 | 说明 |
public void newLine() | 跨平台的换行 |
注意:每个平台的换行符不同, 但使用newLine方法可以在任何平台进行换行
代码演示
import java.io.*;
public class Test6 {
public static void main(String[] args) throws IOException {
//将本模块下a.txt中的数据一行一行的拷贝到本模块b.txt文件中
BufferedReader br = new BufferedReader(new FileReader("day04\\a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("day04\\b.txt"));
String line;
while ((line = br.readLine()) != null) {
//读一整行写一整行
bw.write(line);
//读不到换行符,要自己换行
bw.newLine();
}
bw.close();
br.close();
}
}

转换流
转换流是属于字符流的高级流,是字符流和字节流之间的桥梁
分类
创建转换流对象
转换输入流
方法名称 | 说明 |
public InputStreamReader(InputStream in) | 将字节流转换为字符流 |
public InputStreamReader(InputStream in, String charsetName) |
将字节流转换为字符流,且指定字符集读 |
转换输出流
方法名称 | 说明 |
public OutputStreamWriter(OutputStream out) | 将字节流转换为字符流 |
public OutputStreamWriter(OutputStream out, String charsetName) | 将字节流转换为字符流,且指定字符集写 |
作用
让字节流使用字符流中的方法
代码演示
import java.io.*;
public class Test7 {
public static void main(String[] args) throws IOException {
//利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
//只有字符缓冲流可以读一整行
//创建字节流对象
FileInputStream fis = new FileInputStream("day04\\a.txt");
//将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(fis);
//将字符流包装成字符缓冲流
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//对酒当歌,人生几何!
//譬如朝露,去日苦多。
//慨当以慷,忧思难忘。
//何以解忧?唯有杜康。
br.close();
}
}
指定字符集读写
代码演示
import java.io.*;
public class Test8 {
public static void main(String[] args) throws IOException {
//将本地GBK文件转成UTF-8文件
//将GBK文件读到程序中
//要指定使用GBK字符集进行读取,否则会出现乱码
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\a.txt"),"GBK");
//再将读到程序中的数据以UTF-8写出
//要指定使用UTF-8字符集进行写出,不指定字符集时idea默认使用UTF-8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day04\\a.txt"));
int b;
while((b = isr.read())!=-1){
osw.write(b);
}
osw.close();
isr.close();
}
}
----->
但是JDK11后出现了新的方式,以后主要使用新方式
代码演示
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
public class Test9 {
public static void main(String[] args) throws IOException {
//将GBK文件读到程序中
//使用Charset.forName指定GBK字符集进行读取,否则会出现乱码
FileReader fr = new FileReader("D:\\a.txt", Charset.forName("GBK"));
//再将读到程序中的数据以UTF-8写出
//指定UTF-8字符集进行写出,不指定字符集时idea默认使用UTF-8
//FileWriter fw = new FileWriter("day04\\z.txt",Charset.forName("UTF-8"));
FileWriter fw = new FileWriter("day04\\a.txt");
int b;
while ((b = fr.read()) != -1) {
fw.write(b);
}
fw.close();
fr.close();
}
}
序列化流与反序列化流
序列化流与反序列化流是属于字节流的高级流,用来操作Java中的对象
分类
序列化流
序列化流也称为对象操作输出流,可以把Java中的对象写到本地文件中
构造方法 | 说明 |
public ObjectOutputStream(OutputStream out) | 把基本流包装成高级流 |
成员方法 | 说明 |
public final void writeObject(Object obj) | 把对象序列化(写出)到文件中去 |
反序列化流
反序列化流也称为对象操作输入流,可以把序列化到本地文件中的对象,读取到程序中来
构造方法 | 说明 |
public ObjectInputStream(InputStream out) | 把基本流包装成高级流 |
成员方法 | 说明 |
public object readObject() | 把序列化到本地文件中的对象,读取到程序中来 |
序列化流/反序列化流细节
使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。否则,会出现NotSerializableException异常。
Serializable接口里面是没有抽象方法,是标记型接口
一旦实现了这个接口,那么就表示当前的类可以被序列化
如果一个对象中的某个成员变量的值不想被序列化,可以给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
序列化流写到文件中的数据是不能修改的,一旦修改破坏数据后就无法再次读回来了
序列化对象后,修改了Javabean类,再次反序列化,会出现InvalidClassException异常
是因为Javabean类底层会根据成员变量和方法计算出一个版本号,修改后版本号会改变
两次版本号不统一就会出现异常
可以给Javabean类添加serialVersionUID(序列号、版本号),使版本号不会被改变
这样序列化对象后,修改Javabean类,也不会出现异常
代码演示
自定义Student对象的javabeen类
import java.io.Serializable;
//实现接口Serializable,表示该Student类可以被序列化
public class Student implements Serializable {
//设定版本号serialVersionUID
private static final long serialVersionUID = 1L;
private String name;
private int age;
//不想序列化address,使用关键字transient
private transient String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
进行序列化, 将Student对象写到本地文件中
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutput{
public static void main(String[] args) throws IOException {
Student stu = new Student("张三",23,"北京");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day04\\a.txt"));
oos.writeObject(stu);
oos.close();
}
}
进行反序列化,将序列化到本地文件中的对象,读取到程序中来
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInput {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day04\\a.txt"));
Student o = (Student) ois.readObject();
System.out.println(o);
//Student{name='张三', age=23, address='null'}
//因为address不想序列化,使用了关键字transient,所以反序列化得到的是null
ois.close();
}
}
序列化多个自定义对象的方法
先将多个自定义对象添加到一个集合中,再将集合序列化(集合也实现了Serializable接口)
这样不管有多少个自定义对象,在反序列化时只需使用一次readObject方法,得到装有对象的集合遍历就行。
代码演示
进行序列化, 将装有多个Student对象的集合写到本地文件中
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
public class ObjectOutput {
public static void main(String[] args) throws IOException {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 23, "北京");
Student s2 = new Student("李四", 24, "天津");
Student s3 = new Student("王五", 25, "上海");
Collections.addAll(list, s1, s2, s3);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day04\\a.txt"));
oos.writeObject(list);
oos.close();
}
}
进行反序列化,将序列化到本地文件中的集合,读取到程序中来
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class ObjectInput {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day04\\a.txt"));
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
for (Student s : list) {
System.out.println(s);
}
//Student{name='张三', age=23, address='北京'}
//Student{name='李四', age=24, address='天津'}
//Student{name='王五', age=25, address='上海'}
ois.close();
}
}
打印流
打印流是属于输出流的高级流
分类
特点
打印流只操作文件目的地,不操作数据源
特有的写出方法可以实现数据原样写出(写97就会写出97,不会转变为a)
特有的写出方法,可以实现自动刷新,自动换行,打印一次数据 = 写出 + 换行 + 刷新
字节打印流
构造方法 | 说明 |
public PrintStream(OutputStream/File/String) | 关联字节输出流/文件/文件路径 |
public PrintStream(OutputStream out,boolean autoFlush) | 自动刷新 |
public PrintStream(OutputStream out,boolean autoFlush, String encoding) | 指定字符编码且自动刷新 |
public PrintStream(File/String,Charset charset) | 指定字符编码 |
public PrintStream(File/String,String csn) |
字节流底层没有缓冲区,开不开自动刷新都一样
idea默认字符编码为UTF-8
成员方法 | 说明 |
public void write(int b) | 常规方法:规则跟字节输出流基本流一样,将指定的字节写出 |
public void println(Xxx xx) | 特有方法:打印任意数据,自动刷新,自动换行 |
public void print(Xxx xx) | 特有方法:打印任意数据,不换行 |
public void printf(String format, 0bject... args) | 特有方法:带有占位符的打印语句,不换行 |
特有方法可以实现数据原样写出
代码演示
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class Test1 {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps = new PrintStream(new FileOutputStream("day04\\a.txt"),true);
ps.println(97);
ps.print(true);
ps.printf("你是%s","张三");
ps.close();
}
}
字符打印流
构造方法 | 说明 |
public PrintWriter(OutputStream/Write/File/String) | 关联字节输出流/字符输出流/文件/文件路径 |
public PrintWriter(OutputStream/Write,boolean autoFlush) | 自动刷新 |
public PrintWriter(OutputStream out,boolean autoFlush,Charset charset) | 指定字符编码且自动刷新 |
public PrintWriter(File/String,Charset charset) | 指定字符编码 |
public PrintWriter(File/String,String csn) |
字符流底层有缓冲区,想要自动刷新需要开启
idea默认字符编码为UTF-8
成员方法 | 说明 |
public void write(...) | 常规方法:规则跟之前一样,写出字节或者字符串 |
public void println(Xxx xx) | 特有方法:打印任意类型的数据并且换行 |
public void print(Xxx xx) | 特有方法:打印任意类型的数据,不换行 |
public void printf(String format, Object... args) | 特有方法:带有占位符的打印语句 |
特有方法可以实现数据原样写出
代码演示
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Test2 {
public static void main(String[] args) throws IOException {
PrintWriter pw = new PrintWriter(new FileWriter("day04\\a.txt"), true);
pw.println(12345);
pw.print(false);
pw.printf("1+1=%d",2);
pw.close();
}
}
打印流的应用场景
打印输出语句底层计算打印流
public class Test3 {
public static void main(String[] args) {
//获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台
//这是特殊的打印流,也叫系统中的标准输出流,不能关闭,在系统中是唯一的。
PrintStream ps = System.out;
//调用打印流中的方法println
//写出数据,自动换行,自动刷新
ps.println("123");
//123
//关闭后,下面的输出语句不会输出数据
ps.close();
System.out.println("456");
}
}
压缩流与解压缩流
压缩流与解压缩流是属于字节流的高级流
分类
压缩流
public class Test4 {
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);
//读取文件中的数据,写到压缩包中的该ZipEntry对象中
FileInputStream fis = new FileInputStream(file);
int b;
while ((b = fis.read()) != -1) {
zos.write(b);
}
fis.close();
//关闭该ZipEntry对象
zos.closeEntry();
} else {
//4.判断-文件夹,递归
toZip(file, zos, name + "\\" + file.getName());
}
}
}
}
解压缩流
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 Test5 {
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对象表示当前在压缩包中获取到的文件或者文件夹
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();
}
}