JavaSE之深入浅出 IO 流:字节流、字符流与序列化流详解(含完整代码示例)
JavaSE之深入浅出 IO 流:字节流、字符流与序列化流详解(含完整代码示例)
一、字节流
1、IO流介绍以及输入输出以及流向的介绍
1.I:Input 输入O:Output 输出2.概述:从专业的角度去说:从一个设备上将数据传输到另外一个设备上的技术 -> IO流技术3.从专业角度上来说IO流流向怎么区分:相对的谁发送数据,谁就是输出谁接手数据,谁就是输入
2、IO流的流向_针对se阶段的IO
se部分的IO流流向区分,先找参照物->内存1.输出:将内存中的数据写到硬盘上保存
2.输入:将硬盘上的数据读会回到内存中来
3、IO流分类
1.字节流(万物皆字节,字节流是一个万能流(侧重的是文件复制))OutputStream:字节输出流的父类 -> 抽象类InputStream:字节输入流的父类 -> 抽象类2.字符流(专门操作文本文档的)Writer: 字符输出流的父类 -> 抽象类Reader: 字符输入流的父类 -> 抽象类
IO流四大基类: > > OutputStream > > InputStream > > Writer > > Reader
4、OutputStream中子类[FileOutputStream]的介绍以及方法的简单介绍
1.字节输出流概述:FileOutputStream是OutputStream的子类
2.作用:将数据写到文件中(保存到文件中)
3.构造:FileOutputStream(String path)FileOutputStream(File file)
4.方法:a.write(int i) -> 一次写一个字节b.write(byte[] bytes) -> 一次写一个字节数组c.write(byte[] bytes,int offset,int length)-> 一次写一个字节数组的一部分bytes:要写的数组offset:从数组的哪个索引开始写length:写多少个d.close():关流5.细节:a.如果指定的文件没有,FileOutputStream会自动创建b.默认情况下,每运行一次,都会重新创建一个新的文件,覆盖老文件
package com.code.day20;import java.io.FileOutputStream;
import java.io.IOException;public class Demo01FileOutputStream {public static void main(String[] args) throws IOException {method1();method2();method3();method4();}/*** 一次写一个字节* @throws IOException*/public static void method1() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");fileOutputStream.write(97);fileOutputStream.close();}/*** 一次写一个字节组* @throws IOException*/public static void method2() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");byte[] bytes = {97,98,99,100};fileOutputStream.write(bytes);fileOutputStream.close();}/*** 一次写一个字节数组的一部分* @throws IOException*/public static void method3() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");byte[] bytes = {97,98,99,100};fileOutputStream.write(bytes,1,3);fileOutputStream.close();}/*** 一次写一个字节的汉字* @throws IOException*/public static void method4() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");fileOutputStream.write("你好".getBytes());fileOutputStream.close();}}
续写追加以及换行
1.续写追加:FileOutputStream(String path,boolean append)
如果append为true,就是续写追加,就不会每次都产生一个新的文件覆盖老文件了
2.换行:需要将换行符写到文件中
windows: \r\n
linux: \n
mac os : \
package com.code.day20;import java.io.FileOutputStream;
import java.io.IOException;public class Demo02FileOutputStream {public static void main(String[] args) throws IOException {method1();}public static void method1() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo2.txt", true);fileOutputStream.write("床前明月光\r\n".getBytes());fileOutputStream.write("疑是地上霜\r\n".getBytes());fileOutputStream.write("举头望明月\n".getBytes());fileOutputStream.write("低头思故乡\n".getBytes());fileOutputStream.close();}}
5、InputStream子类[FileInputStream]的介绍以及方法的使用
1.概述:FileInputStream 继承于InputStream
2.作用:将数据从文件中读到内存中
3.构造:FileInputStream(String path)FileInputStream(File file)
4.方法:a.int read() 一次读一个字节,返回的是读取到的字节b.int read(byte[] bytes) 一次读一个字节数组,返回的是读取的个数(数组定多长,每次读多少个)c.int read(byte[] bytes,int offset,int length) 一次读取一个字节数组一部分,返回的是读取个数d.close() 关流 部分堆对象无法被GC垃圾回收器回收 因此要手动关闭
5.细节:a.如果指定的文件没有,FileOutputStream会自动创建b.默认情况下,每运行一次,都会重新创建一个新的文件,覆盖老文件 c.如果为读对象不会创建
6、一次读取一个字节
public class Demo03FileInputStream {public static void main(String[] args) throws IOException {method();}public static void method() throws IOException {FileInputStream fileInputStream = new FileInputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");int len = 0;while ((len = fileInputStream.read()) != -1)System.out.println((char) len);}
}
1.用一个流对象读取完之后,再读啥也读不出来的;如果还想读,重新new对象
2.流关闭之后,不能再用了 否则会报错
java > Exception in thread "main" java.io.IOException: Stream Closed > at java.base/java.io.FileInputStream.read0(Native Method) > at java.base/java.io.FileInputStream.read(FileInputStream.java:228) > at com.atguigu.b_input.Demo01FileInputStream.method01(Demo01FileInputStream.java:39) > at com.atguigu.b_input.Demo01FileInputStream.main(Demo01FileInputStream.java:8) >
7、读取-1的问题
每个文件中的末尾都有一个结束标记,如果读到结束比较之后,会直接返回-1
8、一次读取一个字节数组以及过程
public class Demo04FileInputStream {public static void main(String[] args) throws IOException {/*** int read(byte[] b) 一次读多个字节,返回的是读取到的字节个数*/FileInputStream fileInputStream = new FileInputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo1.txt");byte[] bytes = new byte[3];int len = 0; //接收的字节个数while ((len = fileInputStream.read(bytes)) != -1) {System.out.println(new String(bytes, 0, len));
// System.out.println(new String(bytes)); // 如果直接转为字符串 则可能会导致有一个数据被覆盖 而另一个没被覆盖}}
}
数组的长度一般定义长度为1024或者其倍数
不能直接使用一下方法
while ( fileInputStream.read(bytes)!= -1) { //读取第一个
System.out.println(fileInputStream.read(bytes)); //读取第二个 并输出第二个 }
以上方法会导致读取到的第一个字节未被输出
9、字节流实现图片复制分析
不能够边写边读
10、字节流实现图片复制代码实现
package com.code.day20;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo05Copy {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\img\\1726621487689.png");FileOutputStream fileOutputStream = new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\刘大胆.jpg"); //需要指定文件复制后的名称byte[] bytes = new byte[1024];int len = 0; while ((len = fileInputStream.read(bytes)) != -1) { //先读后写fileOutputStream.write(bytes, 0, len);}//先开的流 后关fileOutputStream.close();fileInputStream.close();}
}
二、字符流
1、字节流读取中文的问题
1.编码:将数据按照指定的规则保存的过程,叫做编码
2.解码:将数据按照指定的规则读取出来的过程,叫做解码3.GBK: 一个汉字占2个字节UTF-8: 一个汉字占3个字节4.注意:a.字节流称之为万能流(特指文件复制),我不管到底是什么编码规范,最终只要总字节数没有丢,文件复制过去就会正常显示,但是不要边读边看,如果边读边看的话,即使编码和解码规则一样,也有可能导致看到的是乱码b.解决:我们在读取文本文档的时候,如果把内容看成是一个一个的字符去操作,就可以了
> 说明:即使用字符流去操作文本文档,那么前提也是编码和解码规则一致 > > 如果编码解码规则不一致,字符流操作也会出现乱码情况
> 字符流操作文本文档,如果编码和解码一致,边读边看是不会乱的 > > 字节流操作文本文档,即使编码和解码一致,边读边看也有可能出现乱码
2、FileReader的介绍以及使用
1.概述:FileReader extends Reader
2.作用:读数据
3.构造:FileReader(String path)FileReader(File file)
4.方法:int read() 一次读一个字符,返回的是读取的字符int read(char[] chars) 一次读一个字符数组,返回的是读取个数int read(char[] chars,int offset,int length) 一次读取一个字符数组一部分,返回的是读取个数close() 关流
package com.code.day20;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;public class Demo06FileReader {public static void main(String[] args) throws IOException {
// method();method1();}/*** int read() 一次读一个字符,返回的是读取的字符*/public static void method() throws IOException {FileReader fileReader = new FileReader("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo3.txt");int len =0;while((len = fileReader.read())!=-1){System.out.print((char)len);}fileReader.close();}/*** int read(char[] cbuf) 一次读多个字符,返回的是读取的字符个数*/public static void method1() throws IOException {FileReader fileReader = new FileReader("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo3.txt");char[] chars = new char[1024];int len= 0;while((len=fileReader.read(chars))!=-1){System.out.print(new String(chars,0,len));}}
}
注意:如果要涉及到文件复制,不要使用字符流,要使用字节流
3、FileWriter的介绍以及使用
1.概述:FileWriter extends Writer
2.作用:写数据
3.构造:FileWriter(String path)FileWriter(File file)FileWriter(String path,boolean append)append为true就是追加续写
4.方法:a.write(int i) 一次写一个字符b.write(char[] chars) 一次写一个字符数组c.write(char[] chars,int offset,int length) 一次写一个字符数组一部分d.write(String s) 一次写一个字符串e.close()f.flush() //刷新缓冲区
5.注意:FileWriter底层有一个缓冲区,我们所写的数据,先进缓冲区,然后再从缓冲区中将数据刷到文件中
public class Demo07FileWriter {public static void main(String[] args) throws IOException {FileWriter fileWriter = new FileWriter("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo3.txt",true);fileWriter.write("我叫刘大胆\n");fileWriter.write("我叫超级无敌刘大胆\n");fileWriter.close();}
}
快速将一段代码抽取到方法中: > > 1.选中要抽取的代码 > > 2.ctrl+alt+m
4、FileWriter的刷新功能和关闭功能
1.flush:将数据从缓冲区中刷到文件中,但是流没关闭,后续还能使用这个流对象
2.close:先将数据刷到文件中,然后再关流,后续流对象无法使用
5、IO异常处理的方式
package com.code.day20;import java.io.FileWriter;
import java.io.IOException;public class Demo09IOEx {public static void main(String[] args) {FileWriter fileWriter = null;try {fileWriter = new FileWriter("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo4.txt");fileWriter.write("你好");} catch (IOException e) {e.printStackTrace();} finally {try {fileWriter.close();} catch (IOException e) {e.printStackTrace();}}}
}
6、JDK7之后io异常处理方式
1.格式:
try(IO对象;IO对象){可能出现异常的代码
}catch(异常 对象名){异常处理代码
}2.特点:自动关流
代码相对臃肿public class Demo10IOEX {public static void main(String[] args) {try( FileWriter fileWriter = new FileWriter("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo4.txt");) {fileWriter.write("你好你好");} catch (IOException e) {e.printStackTrace();}}
}
7、JDK9之后的IO异常处理方式(了解)
之前我们讲过JDK 1.7引入了trywith-resources的新特性,可以实现资源的自动关闭,此时要求:
-
该资源必须实现java.io.Closeable接口
-
在try子句中声明并初始化资源对象
-
该资源对象必须是final的
public class Demo11IOEX {public static void main(String[] args) throws IOException {FileWriter fileWriter = new FileWriter("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo4.txt");try (fileWriter) {fileWriter.write("你好你好你好");} catch (IOException e) {e.printStackTrace();}} }
JDK1.9又对trywith-resources的语法升级了
- 该资源必须实现java.io.Closeable接口
- 在try子句中声明并初始化资源对象,也可以直接使用已初始化的资源对象
- 该资源对象必须是final的
IO流对象1声明和初始化;
IO流对象2声明和初始化;try(IO流对象1;IO流对象2){可能出现异常的代码
}catch(异常类型 对象名){异常处理方案
}
public class Demo05FileWriter {public static void main(String[] args)throws Exception {FileWriter fw = new FileWriter("day20_IO\\4.txt");/*可以减轻try的压力同时可以自动关流*/try(fw) {fw.write("床前明月光11111");} catch (Exception e) {e.printStackTrace();}}
}
三、序列化流
1、序列化流和反序列化流介绍
1.序列化流:写对象
2.反序列化流:读对象
2、序列化流_ObjectOutputStream
1.概述:ObjectOutputStream extends OutputStream
2.作用:写对象
3.构造:ObjectOutputStream(OutputStream out)
4.方法:writeObject(Object obj)5.注意:如果想让一个对象序列化到文件中,此时此对象需要实现序列化接口
package com.code.day20;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class Demo12Serializable {public static void main(String[] args) throws IOException {method();}public static void method() throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo5.txt"));Person person = new Person("张三",18);objectOutputStream.writeObject(person);objectOutputStream.close();}}
package com.code.day20;import java.io.Serializable;public class Person implements Serializable {private String name;private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
3、反序列化_ObjectInputStream
1.概述:ObjectInputStream extends InputStream
2.作用:读对象
3.构造:ObjectInputStream(InputStream in)
4.方法:Object readObject()
package com.code.day20;import java.io.*;public class Demo12Serializable {public static void main(String[] args) throws IOException, ClassNotFoundException {read();//反序列化流}public static void read() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo5.txt"));Object o = objectInputStream.readObject();Person person = (Person) o;System.out.println(person);}}
4、不想被序列化操作(了解)
在成员前面加上一个关键字:transient
5、反序列化时出现的问题以及分析以及解决
1.问题描述:我们修改了源码,但是没有重新序列化,直接反序列化了,就会出现序列号冲突问题
public class Person implements Serializable {static final long serialVersionUID = 42L; //固定序列号private String name;public Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
6、经验问题
1.如果循环反序列化循环次数不对了,很容易出现EOFException(文件意外到达结尾异常)
2.问题解决:将多个对象放到一个集合中,将这个集合序列化到文件中,将来直接反序列化一个集合,然后集合拿出来了,我们遍历的次数就确定了,就不会出现这个问题了
package com.code.day20;import java.io.*;
import java.util.ArrayList;import static com.code.day20.Demo12Serializable.read;public class Demo13JingYan {public static void main(String[] args) throws IOException, ClassNotFoundException {
// write(); //序列化read(); //反序列化}public static void write() throws IOException {ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo6.txt"));ArrayList<Person> list = new ArrayList<>();Person p1 = new Person("张三", 18);Person p2 = new Person("李四", 18);Person p3 = new Person("王五", 18);list.add(p1);list.add(p2);list.add(p3);objectOutputStream.writeObject(list);objectOutputStream.close();}public static void read() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("D:\\java-code\\java-full-stack\\01-java-base\\src\\com\\code\\day20\\file\\demo6.txt"));Object o = objectInputStream.readObject();ArrayList<Person> list = (ArrayList<Person>) o;for (Person person : list) {System.out.println(person);}objectInputStream.close();}
}