菜鸟之路Day16一一IO流(二)
作者:blue
时间:2025.2.17
0.概述
文章内容学习自黑马程序员BV1yW4y1Y7Ms

1.缓冲流
1.缓冲流有几种?
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOuputStream
字符缓冲输入流:BufferedReader
字符缓冲输出流:BufferedWriter
2.缓冲流为什么能提高性能?
缓冲流自带长度为8192的缓冲区
可以显著提高字节流的读写性能
对于字符流提升不明显,对于字符缓冲流而言关键点是两个特有的方法
3.字符缓冲流两个特有的方法是什么?
字符缓冲输入流BufferedReader:readLine()
字符缓冲输出流BufferedWriter:newLine()

1.1字节缓冲流
一次拷贝一个字节
public class BufferedIODemo1 {
public static void main(String[] args) throws IOException {
/*
* 需求:利用字节缓冲流拷贝文件
*
* 字节缓冲输入流的构造方法:public BufferedInputStream(InputStream is)
*
* 字节缓冲输出流的构造方法:public BufferedOutputStream(OutputStream os)
* */
//1.创建缓冲流的对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy.txt"));
//拷贝(一次读取一个字节)
int b;
while ((b=bis.read())!=-1) {
bos.write(b);
}
bos.close();
bis.close();
}
}
一次拷贝多个字节
public class BufferedIODemo2 {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy2.txt"));
//一次拷贝多个字节
byte[] bytes = new byte[1024];
int len;
while((len= bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
1.2字符缓冲流
字符缓冲输入流
public class BufferedIODemo3 {
public static void main(String[] args) throws IOException {
/*
* 字符缓冲输入流:
* 构造方法:public BufferedReader(Reader r)
* 特有方法:
* public String readLine() 读一整行
* */
BufferedReader br = new BufferedReader(new FileReader("src\\a.txt"));
String line;
while((line= br.readLine())!=null){
System.out.println(line);
}
br.close();
}
}
字符缓冲输出流
public class BufferedIODemo4 {
public static void main(String[] args) throws IOException {
/*
* 字符缓冲输出流
* 构造方法:
* public BufferedWriter(Writer r)
* 特有方法:
* public void newLine() 跨平台换行
* */
BufferedWriter bw = new BufferedWriter(new FileWriter("src//a.txt",true));
bw.write("你的微笑,百度搜索不到");
bw.newLine();//换行
bw.write("我结婚的时候你一定要来哦,没有新娘,我很尴尬");
bw.close();
}
}
1.3综合练习
练习一:拷贝文件,四种方式拷贝文件,并统计各自用时
字节流的基本流:一次读写一个字节
字节流的基本流:一次读取一个字节数组
字节缓冲流:一次读写一个字节
字节缓冲流:一次读写一个字节数组
public class TestOne {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//method1();//0.051秒
//method2();//0.001秒
//method3();//0.006秒
method4();//0.0秒
long end = System.currentTimeMillis();
System.out.println((end-start)/1000.0+"秒");
}
//1.字节流的基本流:一次读写一个字节
private static void method1() throws IOException {
FileInputStream fis = new FileInputStream("src\\a.txt");
FileOutputStream fos = new FileOutputStream("src\\copy1.txt");
int b;
while((b= fis.read())!=-1){
fos.write(b);
}
fos.close();
fis.close();
}
//2.字节流的基本流:一次读取一个字节数组
private static void method2() throws IOException {
FileInputStream fis = new FileInputStream("src\\a.txt");
FileOutputStream fos = new FileOutputStream("src\\copy2.txt");
byte[] bytes = new byte[1024];
int len;
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}
//3.字节缓冲流:一次读写一个字节
private static void method3() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy3.txt"));
int b;
while((b=bis.read())!=-1){
bos.write(b);
}
bos.close();
bis.close();
}
//4.字节缓冲流:一次读写一个字节数组
private static void method4() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\copy4.txt"));
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
练习二:
需求:《滕王阁序》的顺序被打乱了,请恢复他的顺序并写到新的文件中
public class TestTwo {
public static void main(String[] args) throws IOException {
//为了读取一整行的数据,我们再这里利用字符缓冲流,为的是readLine方法
BufferedReader br = new BufferedReader(new FileReader("src\\a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("src\\copy5.txt"));
ArrayList<String> list = new ArrayList<>();
String line;
while((line=br.readLine())!=null){
list.add(line);
}
//排序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int i1 = Integer.parseInt(o1.split("\\.")[0]);
int i2 = Integer.parseInt(o2.split("\\.")[0]);
return i1-i2;
}
});
for (String s : list) {
bw.write(s);
bw.newLine();
}
bw.close();
br.close();
}
}
练习三:
实现一个验证程序运行次数的小程序,要求:当程序运行超过三次提示,本软件只能免费使用3次
思想:创建一个txt文件来记录使用的次数,注意此处创建BufferedWriter的位置是有考究的,因为如果文件已经存在,则会清空文件,所以如果直接在开头创建的话会导致读不到数据。
public class TestThree {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("src\\count.txt"));
String str = br.readLine();//读取文件中的数字
int x = Integer.parseInt(str);//转为整数
if(x<3){
System.out.println("第"+(x+1)+"次免费使用");
}
else{
System.out.println("3次免费次数已经用完,请充值后使用");
}
BufferedWriter bw = new BufferedWriter(new FileWriter("src\\count.txt"));
bw.write(x+1+"");//写回,以字符串形式
bw.close();
br.close();
}
}
2.转换流
转换流是字符流和字节流之间的桥梁,他可以包装字节流,使原本的字节流可以根据字符集一次读取多个字节,读取数据不会乱码。
1.转换流的名字是什么?
字符转换输入流:InputStreamReader
字符转换输出流:OutputStreamWriter
2.转换流的作用是什么?
指定字符集读写数据(JDK11之后已淘汰)
字节流想要使用字符流中的方法
Demo1:按照指定编码读取文件
gbktest.txt是一个GBK编码的文件,由于IDEA默认UTF-8编码所以我们可以创建一个FileReader对象,来指定读取的字符集
public class ConvertDemo1 {
public static void main(String[] args) throws IOException {
/*
* 按照指定编码读取文件
* */
FileReader fr = new FileReader("src\\gbktest.txt", Charset.forName("GBK"));
int ch;
while((ch=fr.read())!=-1){
System.out.print((char)ch);
}
fr.close();
}
}
Demo2:按指定编码写出文件
public class ConvertDemo2 {
public static void main(String[] args) throws IOException {
//按指定编码写出文件
FileWriter fw = new FileWriter("src\\gbkouttest.txt", Charset.forName("GBK"));
fw.write("你好你好");
fw.close();
}
}
Demo3:将本地中的GKB文件,以UTF-8的形式写出
public class ConvertDemo3 {
public static void main(String[] args) throws IOException {
//将本地中的GKB文件,以UTF-8的形式写出
FileReader fr = new FileReader("src\\gbktest.txt", Charset.forName("GBk"));
FileWriter fw = new FileWriter("src\\c.txt",Charset.forName("UTF-8"));
int ch;
while((ch=fr.read())!=-1){
fw.write((char)ch);
}
fw.close();
fr.close();
}
}
转换流练习:
public class ConvertDemo4 {
public static void main(String[] args) throws IOException {
/*
* 利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
* 1.字节流读中文数据必然会有乱码-->我们可以利用转换流,将字节流转为字符流
* 2.字符流没有办法读取一整行数据-->我们可以将字符流包装为字符缓冲流
* */
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src\\copy1.txt")));
String str;
while((str=br.readLine())!=null){
System.out.println(str);
}
br.close();
}
}
3.序列化流/对象操作输出流

序列化流的作用:把java中的对象写到本地文件中
序列化流的小细节:使用对象输出流将对象保存文件时会出现NotSerializableException异常
解决方案:让Javabean类实现Serializable接口,Serializable接口里面是没有抽象方法的,它属于一个标记型接口,
一旦实现了这个接口就表示当前的Student类可以被序列化。
public class myObjectStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
* 需求:利用序列化流/对象操作输出流,把一个对象写到本地文件中
* 构造方法:public ObjectOutputStream(OutputStream out) 把基本流变成高级流
* 成员方法:public final void writeObject(Object obj) 把对象序列化(写出)到文件中去
* */
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\a.txt"));
Student stu = new Student(13,"zhangsan");
oos.writeObject(stu);
oos.close();
}
}
4.反序列化流/对象操作输入流
public class myObjectStreamDemo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/*
* 需求:利用反序列化流/对象操作输入流,把文件中的对象读到程序当中
* 构造方法:public ObjectInputStream(InputStream out) 把基本流变成高级流
* 成员方法:public Object readObject() 把序列化到本地文件中的对象,读取到程序中来
* */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\a.txt"));
Student stu = (Student)ois.readObject();
System.out.println(stu);
ois.close();
}
}
序列化流/反序列化流的细节汇总
①使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。否则会出现NotSerializableException异常
②序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了
③序列化对象后,修改了javabean类,再次反序列化,会不会有问题?
会出问题,会抛出InvalidClassException异常
解决方案:给Javabean类添加serialVersionUID(序列号,版本号)
private static final long serialVersionUID
④如果一个对象中某个成员变量的值不想被序列化,又该如何实现呢?
解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
private transient String 变量名
综合练习
需求:将多个自定义对象序列化到文件中,但是对象个数不确定
public class myObjectStreamDemo3 {
public static void main(String[] args) throws IOException {
//需求:将多个自定义对象序列化到文件中,但是对象个数不确定
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\a.txt"));
Student s1 = new Student(13,"zhangsan");
Student s2 = new Student(23,"lisi");
Student s3 = new Student(24,"wangwu");
// 由于对象个数是不确定的,这造成了读对象时的麻烦
// 所以我们可以将所有的对象都放进一个集合中
// 这样方便读取
ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
oos.writeObject(list);//序列化
oos.close();
}
}
public class myObjectStreamDemo4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\a.txt"));
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();//直接接收集合对象,这样就不用理会个数了
for (Student student : list) {
System.out.println(student);
}
ois.close();
}
}