一、包装流
定义: 在原始字节流或字符流的基础性,为了提高读写效率进行再次处理的流, 称为包装流/处理流
1、缓存字节流 BufferedInputStream 、BufferedOutputStream
由于原始流在文件读写时 效率比较低(操作文件本身占用资源较多),可以通过创建缓冲区的方式提高读写效率, 将读取/写出的数据线放入缓冲区,到达一定数量后再次冲缓冲区读取/写出
mark(readLimit) 与 reset()用法
其中reset不能单独使用,必须mark(readLimit) ,readLimit表示标记后最多读取的上限,但是这里标记后读取的内容与BufferedInputStream的缓冲大小有关,比由上限决定,也就是说读取的内容超出上限可以继续重置到mark的位置。
public static void main(String[] args) throws IOException {
//创建缓冲流
InputStream is = new FileInputStream("d:/myfile.txt");
BufferedInputStream bis = new BufferedInputStream(is);
//是否支持mark 或 reset
System.out.println(bis.markSupported());
System.out.println((char)bis.read());//97
//重置
bis.mark(3); // pos标记往后退三个 最多可以读取字节上限
System.out.println("再次读取:"+(char)bis.read());
System.out.println("再次读取:"+(char)bis.read());
System.out.println("再次读取:"+(char)bis.read());
System.out.println("再次读取:"+(char)bis.read());
bis.reset(); // 这里 重置后 退回到3个以前的位置
// 重置后输出
int n =0;
while( (n = bis.read()) !=-1){
System.out.println("重置后;"+(char)n);
}
//关闭流
bis.close();
is.close();
}
2、缓存字符流 (BufferedReader 、BufferedWriter)
public static void main(String[] args) throws IOException {
// 缓冲字符流 可以一行一行读取 、写出
BufferedReader br = new BufferedReader(new FileReader("d:/小众网站.txt"));
//读一行
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
String s = null; //读的数据为空 则不需要读
while( (s = br.readLine()) !=null){
System.out.println(s);
}
br.close();
//缓冲写出流
FileOutputStream pw = new FileOutputStream("d:/abcd.txt");
//由于字节流不能直接放入 字符缓冲区,需要将它转成字符流 使用转换流并可以指定编码格式
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(pw));
bw.newLine();// 开启新一行(换行)
bw.write("这是测试转换流的方式");
bw.close();
}
3、打印流(输出流) PrintWriter 、PrintStream
public static void main(String[] args) throws FileNotFoundException {
// 打印流 ,提供一些打印输出方法
PrintWriter pw = new PrintWriter("d:/abcd.txt");
pw.print(100);
pw.println('a');//换行打印
pw.print("hello");
pw.close();
//System.out 字节打印流 PrintStream
4、数据字节流DataInputStream、DataOutputStream
它们用于读入写出Java基本数据类型的数据到文件或其他设备端,它们也属于包装流
DataOutputStream 常用方法
writerByte(byte):写一个字节到设备或文件
writerChar(char):写一个字符到设备或文件
writerInt(int):写一个4个字节的int到设备或文件
writer(boolean):写一个boolean类型到设备或文件
writerDouble(double):写一个double类型到设备或文件
writerFloat(float):写一个float类型到设备或文件
writerLong(long):写一个long类型到设备或文件
writerShort(short):写一个short类型到设备或文件
writerUTF(String):写一个字符串类型到设备或文件
DataInputStream: 读指定文件的数据,可以读数据类型
int readInt() :读一个int类型
short readShort():读一个short类型
readByte():读一个字节类型
read():读一个字节类型
readDouble(): 读一个double类型
readFloat():读一个float类型
readChar():读一个字符类型
readBoolean():读一个boolean类型
readLong() :读一个long类型
public static void main(String[] args) throws IOException {
//创建数据写出流
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("d:/data.txt"));
//写一个int类型 依次写出4个字节
dos.writeInt(100);
dos.writeBoolean(true);
//关闭
dos.close();
//读取文件 创建数据读入流 ,需要按写的顺序读进来
DataInputStream dis = new DataInputStream(
new FileInputStream("d:/data.txt"));
//读一个int类型 (依次读4个字节)
int num = dis.readInt();
System.out.println("读取的数据:"+ num);
System.out.println("读的数据:"+dis.readBoolean());
dis.close();
}
5、转换流
转换流是将字节流转成字符流的桥梁, 也可以在转换时指定编码格式。 InputStreamReader 和 OutputStreamWriter
public static void main(String[] args) throws IOException {
// 字节流转成字符流
InputStream is = new FileInputStream("d://小众网站.txt");
InputStreamReader isr = new InputStreamReader(is);
//缓冲流 读取数据
BufferedReader br = new BufferedReader(isr);
//读一行
String str =null;
while( (str= br.readLine()) !=null){
System.out.println(str);
}
//关闭流
br.close();
isr.close();
is.close();
}
public static void main(String[] args) throws IOException {
// 创建 字节转成字符的 写出流 FileOutputStream os =
FileOutputStream fos = new FileOutputStream("d://data.txt");
//指定编码 GBK 格式一个汉字占2个字节 UTF-8 格式一个汉字占3个字节
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
//缓冲形式的
BufferedWriter bw = new BufferedWriter(osw);
bw.write("你好");
bw.newLine();
bw.write("我不好");
bw.close();
}
6、随机字节流
RandomAccessFile 是随机字节流,它是一个可读可写的流 ,在文件操作时指定该对象的模式(model)后,可以读数据或写数据
实现 DataInputStream和DataOutputStream类
构造器:
RandomAccessFile rm = new RandomAccessFile(File ,mode);
RandomAccessFile rm = new RandomAccessFile(String ,mode);
mode表示对象的模式
r: 表示该对象只能读 不能写
rw/rws/rwd :表示该 对象是可读可写的;
public static void main(String[] args) throws IOException {
//创建可读 的流
RandomAccessFile reader = new RandomAccessFile("d://data.txt","r");
//创建可读可写的 的流
RandomAccessFile writer = new RandomAccessFile("d://data-1.txt","rw");
// 读和写的过程和之前一样
byte [] b= new byte[10];
int len=0;
while( (len = reader.read(b)) !=-1){
writer.write(b , 0 , len);
}
System.out.println("复制成功");
//关闭流
writer.close();
reader.close();
}
skipByte 和 seek的区别
// 跳字节读取
RandomAccessFile raf = new RandomAccessFile("d:/data.txt","rw");
// 跳过2个字节
raf.skipBytes(2);
System.out.println((char)raf.readByte()); //3
System.out.println("当前偏移量:"+raf.getFilePointer());//3
// 又丢弃1个字节 从当前位置 往后偏移1位
raf.skipBytes(1);
System.out.println("修改后的偏移量"+raf.getFilePointer());//4
System.out.println("偏移后的读取数据:"+(char)raf.readByte()); //5
raf.close();
// seek用法
RandomAccessFile raf2 = new RandomAccessFile("d:/data.txt","rw");
// 设置当前读取的位置 ,从0开始计算 ,指定n ,就从n的下一个字节 读取
raf2.seek( );
System.out.println("seek后的数据:"+(char)raf2.readByte());//3
raf2.seek(1); // 又从0开始 设置偏移量为1
System.out.println("修改后的偏移量"+raf.getFilePointer());//1
System.out.println("seek后的数据:"+(char)raf2.readByte())//2
raf2.close();
7、对象序列化流
对象流也称为序列化流,用于存储对象和读取对象的字节流,也是属于包装流
序列化和反序列化
将内存中的对象(Object,集合类等)保存到磁盘、网络介质、其他设置的过程,并在合适的时间能获取磁盘文件/网络的数据 ,这个过程就是对象的序列化和反序列化。
为什么需要序列化和反序列化呢?
在之前文件中存储的文本信息,这样不便于对数据的分类和操作,如果可以做到直接对对象的读和写这样可大大提高编程效率,并最大程度保证对象的完整性。
Java-IO中实现对象序列化的两种方式:
实现Serializable接口
实现Externalizable接口
Serializable接口
对象需要实现该接口,但是它没有任何需要实现的方法,只有一个用于标记该类可序列化的唯一标识。 任何类需要序列化都必须标记该变量
public class User implements Serializable {
// 对于能区分其他类的唯一表示
private static final long serialVersionUID = 1L;
private int uid;
private String name;
private String password;
// 有一部分属性不能序列化
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
//创建序列化的对象流 从内存到文件
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("d:/user.txt"));
User user= new User();
user.setUid(1001);
user.setName("admin");
user.setPassword("123456");
//序列化对象
oos.writeObject(user);
//关闭流
oos.close();
// 反序列化: 将文件中的数据 再读入到内存中 ,需要一个读的流 ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d://user.txt"));
// 反序列化尽量只读一次 (也可以读多次, 如何写出就如何读入)
Object obj = ois.readObject();
if(obj instanceof User){
User u = (User)obj;
System.out.println("反序列化的结果:"+u);
}
//关闭流
ois.close();
public interface Externalizable extends java.io.Serializable
Externalizable接口
public class Student implements Externalizable {
private int id;
private String name;
private String sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
// 自定义可序列化的属性
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(this.id);
out.writeUTF(this.name);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.name = in.readUTF();
}
public Student(int id, String name, String sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
public Student( ) {
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建序列化类
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("d:/stu.txt"));
//创建学生
List list = new ArrayList<>();
list.add(new Student(1001,"张飞","男"));
list.add(new Student(1002,"刘备","男"));
list.add(new Student(1003,"小乔","女"));
// 将集合序列化
oos.writeObject(list);
//关闭
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("d:/stu.txt"));
//读
Object obj = ois.readObject();
if(obj instanceof List){
List list2 = (List)obj;
for(Student s : list2){
System.out.println(s);
}
}
//关闭流
ois.close();
}
问题: 能否自定义序列化的属性 :
这里可以采用方式二,实现Externalizable,
并重写两个方法 接口继承而来,
在其基础上新增了两个未实现方法:readExternal(ObjectInputStream)和 writeExternal(ObjectOutputStreawm)
自定义需要序列化的属性
问题: 哪些属性不能实现序列化 ?
1、类中的static修饰的属性不能序列化
2、类中属性被transient修饰的不能序列化 例如 transient private Integer age = null;
3、实现Externalizable接口的类的属性不能全部序列化,必须手动写可序列化的属性。