一、流的概念
内存与存储设备之间传输数据的通道。
内存与存储设备之间的数据传输就需要通过流来完成。
从文件-内存:输入流
从内存-文件:输出流
二、流的分类
按传输方向
输入流:将存储设备中的内容读到内存中
输出流:将内存中的内容写入到存储设备中
按单位
字节流:以字节为单位,可以读写所有类型的数据(图片、视频等)
字符流:以字符为单位,只能读写文本数据(纯文本文件)
按功能
节点流:具有实际传输数据的读写功能
过滤流:在节点流的基础上增强功能
①、字节流
以字节读写文件
InputStream:字节输入流
常用方法:
close()关闭输入流并释放与流相关联的任何系统资源
read()从输入流读取数据的下一个字节
read(byte[] b),从输入流读取一些字节数,并将他们存储到缓冲区b
OutStream:字节输出流
常用方法
close()关闭输出流并释放与流相关联的任何系统资源
flush()刷新此输出流并强制任何缓冲的输出字节被写出
write(byte[] b,int off,int len)从下标为off的字节开始,写入len长度个字节
FileInputStream:从文件中将数据读到内存中
构造方法
FileInputStream("文件路径1")
FileInputStream(File file):将一个File对象所表示的文件路径封装在一个字节输入流中
public class Test{
public static void main(String[] args) throws IOException{
FileInputStream fis = new FileInputStream(new File("sources/12.txt"));
int len = -1;
when((len = fis.read())!= -1) {
system.out.print((char) len);
}
fis.close();
}
}
FileOutStream:将内存中的数据写入到本地文件中
②:字节缓冲流(过滤流)
作用
提高IO效率,减少访问磁盘的次数
数据存储在缓冲区中,flush是将缓冲区的内容写入文件中,也可以直接close,因为在close方法中也调用flush
分类
BufferedInputStream缓冲输入流
是IutputStream的子类,表示高效字节输入流。流资源在创建对象时,会在代码底层默认创建出一个大小为8192的字节数组,提供数组缓冲区,通过字节数组进行文件内容的读操作,因此效率高
BufferedOutputStream缓冲输出流
是OutputStream的子类,表示高效字节输出流。流资源在创建对象时,会在代码底层默认创建出一个大小为8192的字节数组,提供数组缓冲区,通过字节数组进行文件写操作,因此效率高
构造方法
BufferedOutStream(OutputStream out);创建字节缓冲输出流对象
BufferedIutStream(IutputStream in);创建字节缓冲输入流对象
案列:使用缓冲流实现图片复制功能
public class Test{
public static void main(String[] args) throws IOException{
//创建输入流对象
FileInputStream fis = new FileInputStream("sources/1.jpg");
FileOnputStream fos = new FileOnputStream("sources/2.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOnputStream bos = new BufferedOnputStream(fos);
int len;
byte[] data = new byte[1024];
while(len = bis.read(data))!= -1){
bos.write(data);
}
bis.close();
bos.close();
}
}
三、序列化与反序列化
使用流传输对象的过程称为序列化、反序列化
名词解释
序列化
将对象通过流的方式保存到文件中(内存-存储设备)
反序列化
从文件中将对象读取出来在内存中重新构造对象的过程(存储设备-内存)
案例:创建学生类,必须实现Serializable接口,表示该类可以进行序列化操作
package com.demo;
import java.io.Serializable;
public class Student implements Serializable{
public Student(){
}
public Student(int sid,String sname,int age){
this.sid=sid;
this.sname=sname;
this.age=age;
}
private int sid;
private String sname;
private int age;
public int getSid(){
return sid;
}
public void setSid(int sid){
this.sid=sid;
}
public String getSname(){
return sname;
}
public void setSname(String sname){
this.sname = sname;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
@Override
public String toString(){
return "Student{" +
"sid="+sid+
",sname='"+sname+'\''+
",age="+age+
'}';
}
}
序列化操作
package com.demo03;
import java.io.*;
public class Test {
public static void main(string[] args) throws IOException {//创建文件输出流对象
FileOutputstream fos = new Fileoutputstream("sources/stu.bin");//创建对象输出流
objectoutputstream oos = new objectoutputstream(fos);//序列化操作(将内存对象保存到本地文件)
student stu = new student(1001,"张三",23);
oos.writeobject(stu);//关闭流
oos.close(0);
System.out.print]n("序列化完毕! !");
}
}
反序列化操作
package com.gf.demo03 ;
import java.io.*;
public class Test {
public static void main(string[] args) throws IOException,CTassNotFoundException {//创建文件输入流对象
FileInputStream fis = new FileInputstream("sources/stu,bin");
//创建对象输入流
ObjectInputstream ois =new objectinputstream(fis);
//反序列化操作
object o = ois.readobject( ;
//转型
student stu = (student) o;
System.out.printIn(stu) ;
//关闭流
ois.close() ;
}
}
注意事项
要序列化的类必须实现Serializable接口,标识类可以进行序列化操作
必须保证其所有属性都可序列化操作,即都实现Serializable接口
如果类中某些属性不允许序列化操作,则可以使用transient关键字修饰,不参与序列化
反序列化时,如果读取到文件尾部就不能再读了,否则会出现异常: javaio.EOFException