Java—— IO流 第三期
基本流与高级流
以下四种是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-8OutputStreamWriter 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 {//设定版本号serialVersionUIDprivate static final long serialVersionUID = 1L;private String name;private int age;//不想序列化address,使用关键字transientprivate 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;}@Overridepublic 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,所以反序列化得到的是nullois.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.txtzos.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();}
}