Java字节流
流的概念
流的概念:内存与存储设备之间数据传输的通道,数据借助流传输
流的分类
- 按方向:以内存为参照
- 输入流:将 存储设备 中的内容读入到 内存 中
- 输出流:将 内存 中的内容写入到 存储设备 中
- 按单位:
- 字节流:以字节为单位,读写所有数据
- 字符流:以字符为单位,读写文本数据
- 按功能:
- 节点流(底层流):具有实际传输数据的读写功能
- 过滤流:在节点流的基础上增强功能,如缓冲流
字节流抽象类
- 字节流的父类(抽象类):
- InputStream:字节输入流
read()
close()
:节约资源
- OutputStream:字节输出流
write()
close()
:节约资源
- InputStream:字节输入流
文件字节流
-
FileInputStream:
read(byte[] b)
:从流中读取多个字节,将读到内容存入b数组,返回实际读到的字节数;如果达到文件的尾部,则返回-1
//1 创建 FileInputStream,并指定路径
FileInputStream fis = new FileInputStream("F:\\a.txt");
//2 读文件
//读一个字节
int data = 0;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
//3.关闭
fis.close();
使用byte[]读取多个字节
FileInputStream fis = new FileInputStream("F:\\a.txt");
byte[] b = new byte[3];
int count = fis.read(b);//read()范围值为读取字节的个数
System.out.println(new String(b));
System.out.println(count);
int count2 = fis.read(b);
System.out.println(new String(b));
System.out.println(count2);
fis.close();
读取结果:为 fjk
和 ljk
,默认读取的方法在读取结束时不会覆盖未读取的内容,需要对其进行限制:
int count5 = fis.read(b);
System.out.println(new String(b,0,count5));//l
System.out.println(count5);//1
使用循环读取:
FileInputStream fis = new FileInputStream("F:\\a.txt");
byte[] b = new byte[3];
int count = 0;
while((count = fis.read(b))!= -1){
System.out.println(new String(b,0,count));
}
fis.close();
- FileOutputStream:
write(byte[] b)
:一次写多个字节,将b数组中所有字节,写入输出流- 覆盖:默认写入时对原文件进行覆盖,如果不想进行覆盖,在读取文件时设置为
true
FileOutputStream fileOutputStream = new FileOutputStream("F:\\b.txt",true);
- 单个字节写入
//1 创建文件输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("F:\\b.txt");
//2. 写入文件
fileOutputStream.write(97);
fileOutputStream.write('c');
fileOutputStream.write('s');
//3. 关闭
fileOutputStream.close();
System.out.println("执行完毕");
- 字符串写入
//1 创建文件输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("F:\\b.txt");
//2. 写入文件
String s = "Hello,world";
fileOutputStream.write(s.getBytes());//执行写入操作的时候覆盖源文件
//3. 关闭
fileOutputStream.close();
System.out.println("执行完毕");
复制文件
字节流可以复制任何文件
//1. 创建流
//文件字节输入流
FileInputStream fileInputStream = new FileInputStream("F:\\wall.jpg");
//文件字节输出流
FileOutputStream fileOutputStream = new FileOutputStream("F:\\1.jpg");
//2. 边读边写
byte[] b = new byte[1024];
int count = 0;
while ((count=fileInputStream.read(b))!=-1){
fileOutputStream.write(b,0,count);
}
//3. 关闭
fileInputStream.close();
fileOutputStream.close();
System.out.println("复制完毕");
字节缓冲流
- 缓冲流:
- BufferedInputStream
- BufferedOutputStream
- 作用:
- 提高IO效率,减少访问磁盘的次数
- 数据存储在缓冲区中,flush是将缓冲区的内容写入文件中,也可以直接close
缓冲流读取文件
//1. 创建BufferedInputStream
//把字节流传入缓冲流
FileInputStream fileInputStream = new FileInputStream("F:\\a.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
//2. 读取
//读取到缓冲区,然后再读取到文件中,缓冲区大小维8192
int data = 0;
while((data=bufferedInputStream.read())!=-1){
System.out.print((char)data);
}
//3.关闭缓冲区
bufferedInputStream.close();
创建缓冲区(与前文的字符串读取相同,byte[]起到缓冲区作用)
//1. 创建BufferedInputStream
//把字节流传入缓冲流
FileInputStream fileInputStream = new FileInputStream("F:\\buffer.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
//2. 读取
//自己创建缓冲区
byte[] b = new byte[1024];
int count = 0;
while((count = fileInputStream.read(b))!=-1){
System.out.println(new String(b,0,count));
}
//3.关闭缓冲区
bufferedInputStream.close();
缓冲流写入文件
- 在使用缓冲区写入文件时,需要进行
flush
刷新才能将缓冲区的内容写入硬盘中 - 当缓冲区写满时,会自动调用
flush
进行写入 - 在
close
缓冲流时,会调用flush
进行把缓冲区的内容写入硬盘中 - 主动进行
flush
操作可以提高效率
//1.创建字节输出缓冲流
FileOutputStream fileOutputStream = new FileOutputStream("F:\\buffer.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//2.写入文件
for (int i = 0; i < 9; i++) {
//仅写入缓冲区中,没有写入硬盘中,缓冲区满了会自动调用flush
bufferedOutputStream.write("hello,world!\r\n".getBytes());
//刷新到硬盘中,提高效率
bufferedOutputStream.flush();
}
//3.close(内部调用flush)
//即使不进行刷新,关闭缓冲区时也会写入到硬盘中
bufferedOutputStream.close();
对象流
- 对象流:
- ObjectOutputStream
- ObjectInputStream
- 增强了缓冲区功能
- 增强了读写8种基本数据类型和字符串功能
- 增强了读写对象的功能
- readObject():从流中读取一个对象(反序列化)
- writeObject(Object obj):向流中写入一个对象(序列化)
序列化
序列化是将数据类型和类进行写入,要序列化类必须实现 Serializable
接口
- 类的定义:
public class Student implements Serializable{}
- 类的序列化
//1.创建对象流
FileOutputStream fileOutputStream = new FileOutputStream("F:\\stu.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//2.序列化(写入操作)
Student stu1 = new Student("小王",29);
objectOutputStream.writeObject(stu1);//需要进行序列化,需要使类实现序列化的接口
//3.关闭
objectOutputStream.close();
System.out.println("序列化完毕");
反序列化
反序列化:读取文件,再重构成对象
一个对象仅能反序列化一次
//1. 创建对象流
FileInputStream fileInputStream = new FileInputStream("F:\\stu.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//2. 读取文件(反序列化)
Student stu = (Student)objectInputStream.readObject();
//如果读取两次会出现异常,每个对象反序列化仅能进行一次
// Student stu2 = (Student)objectInputStream.readObject();
System.out.println(stu.toString());
//3. 关闭
objectInputStream.close();
注意事项
序列化:
- 序列化类必须要实现Serializable接口
- 序列化类中的对象属性,即引用类型字段也要实现Seralizable接口
- 序列化版本号ID,保证序列化的类和反序列化的类是同一个类
private static final long serialVersionUID = xxxL;
- 使用
transient
(瞬间的)修饰属性,使属性不被序列化 - 静态属性不能序列化
- 序列化多个对象,可以借助集合实现
序列化:
//序列化多个对象
Student stu1 = new Student("小王",29);
Student stu2 = new Student("林",12);
ArrayList<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
objectOutputStream.writeObject(list);//需要进行序列化,需要使类实现序列化的接口
反序列化:
//反序列化
ArrayList<Student> list = (ArrayList<Student>) objectInputStream.readObject();
System.out.println(list.toString());