目录
BufferInputStream/BufferOutputStream
初步了解
IO流就是对文件进行读写的操作。
IO流分类
根据数据的流向来分
将硬盘里的数据读取到内存也就是输入到内存为输入流
将内存中的数据输出到硬盘也就是写入硬盘为输出流
根据所操纵的文件格式不同来分
字节流:一个字节一个字节的输入输出 可以操作计算机中所有的二进制文件 如图片 音频 文本
字符流:一个字符一个字符的输入输出 只能操作文本文件
以上两个不同的分类方式组合对应java.io包下的四个基类
InputStream 字节输入流 Writer 字符输入流
OutputStream 字节输出流 Reader 字符输出流
ps1:这四个基类都是抽象类 所以不能用来实例化
ps2:他们都实现了Closeable接口 重写了里面的close方法 便于关流也就是释放资源
下图为四个基类的对应的常用子类分布
文件流
FileInputStream(文件字节输入流)
从文件中读取数据
在读取数据时,InputStream提供了read()方法,用于读取单个字节数据,并在读取完所有数据后返回-1。通常,读取的字节数据可以根据实际需要转换为字符、整数等其他类型数据进行处理。
需要注意的是,在使用InputStream读取数据时,一定要稳妥的处理掉异常 并释放资源
public void test() {
File file = new File("abc.txt");
FileInputStream fis = null;
try {
fis = new FileInputStream(file);//选流
byte arr[] = new byte[1024];
int len;
while ((len=fis.read(arr))!=-1){
System.out.println(new String(arr, 0, len));
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream (文件字节输出流)
向文件中写入数据向文件中写入数据 如果指定的文件路径不存在,则FileOutputStream会自动创建该文件。如果文件已经存在,FileOutputStream会将新写入的内容覆盖原有内容。如果要追加内容而不是覆盖原有内容,可以使用FileOutputStream的另一个构造函数:new FileOutputStream("hello.txt", true);
@Test
public void test2() {
File file = new File("abc.txt");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
String str = "早上好,长春";
fos.write(str.getBytes());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
经典案例-文件复制
@Test
public void test3() {
File srcFile = new File("dufu.webp");
File destFile = new File("dufu_copy.webp");
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
byte [] buffer = new byte[1024];
int len;
while((len = fis.read(buffer))!= -1) {
fos.write(buffer,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行效果
FileReader/FileWriter
大部分操作相似于文件字节输入输出流 只是在存储数组的类型不同 字节文件将以字节型的数组进行读写操作 字符流将以字符进行存储 因此展示下字符文件的复制案例即可 后面都将以复制案例进行代码演示
@Test
public void test1() {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("abc.txt");
fw = new FileWriter("abc_copy.txt");
char arr[] = new char[10];
int len;
while ((len = fr.read(arr)) != -1) {
fw.write(arr, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
先新增一个概念 节点流与处理流
节点流:直接从数据源或目的地读写数据 上面的文件流可以看作节点流
处理流:不直接连接到数据源或目的地,而是“连接” 在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写
ps1:处理流,是在节点流的基础上,进行二次封装。
ps2:处理流的初始化,前提是得有节点流的对象。??? 那这么强大 还有必要用节点流吗 看下去
BufferedReader/BufferWriter
从下列代码 可以看出缓冲流BufferedReader()对节点流FileReader()的匿名对象进行了封装 因此他说缓冲流BufferedReader是一个处理流
br = new BufferedReader(new FileReader(srcFile));
@Test
public void test2() {
File srcFile = new File("abc.txt");
File destFile = new File("abc1.txt");
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(srcFile));
bw = new BufferedWriter(new FileWriter(destFile));
// 方式二:
String data = br.readLine();
// while((data = br.readLine()) != null) {
// bw.write(data);
// bw.newLine();
// }
while(data != null) {
bw.write(data);
data = br.readLine();
if(data != null) {
bw.newLine();
}
}
// 方式一:
// char [] cbuf = new char[1024];
// int len;
// while ((len = br.read(cbuf)) != -1) {
// bw.write(cbuf,0,len);
// }
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
从代码层面便可看出缓冲流的好处 便是封装后的缓冲流提供了直接操作字符串的方法,但是,有个小bug,不会自动换行,需要我们手动换行,如果手动换行,会在最后多一个空行。
BufferInputStream/BufferOutputStream
缓冲流对字节型文件进行复制操作
@Test
public void test1() {
File srcFile = new File("libai.png");
File destFile = new File("libai2.png");
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
byte [] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1) {
bos.write(buffer,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对应这上文问题??? 那这么强大 还有必要用节点流吗
通过缓冲流在处理字节文件时并没有比文件流方便 因此我们可以总结出
经验分享:
1一般情况下,我们在使用字节流时,使用节点流。我们在使用字符流时,使用处理流。
2、我们一般在只做读取或写出操作时,会选择使用缓冲流。如果在文件的上传(复制)操作时,不建议使用缓冲流。
Object对象流
序列化和反序列化
序列化:保存基本数据类型的变量或引用数据类型对象,写到一个文件里。
反序列化:读取基本数据类的变量或引用数据类型的对象,从文件中把对象读到内存中。
自定义类需要满足如下要求方可序列化:
1、实现Serializable接口
2、需要提供一个全局常量。private static final long serialVersionUID6=849794470754667710L;
全局常量UID的作用
Java序列化机制通过在运行时判断类的serialVersionUID来验证版本的一致性。在进行反序列化时,JVM会把传过来的字节流中的
serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现
import java.io.Serial;
import java.io.Serializable;
/**
* @author
* @date 2023年10月23日 10:06:58
*/
public class Person implements Serializable {
@Serial
private static final long serialVersionUID = 6955987939247928760L;
private String name;
private Integer age;
private transient String gender;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person(String name, Integer age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
ObjectInputStream
@Test
public void test() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("person.dat"));
Person person = (Person) ois.readObject();
System.out.println(person);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if(ois != null) {
try {
ois.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
ObjectOutputStream
@Test
public void test5() {
// 测试transient关键字
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("person2.dat"));
Person person = new Person("libai",50,"男");
oos.writeObject(person);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(oos != null) {
try {
oos.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}