3※、对象流、输出流、RandomAccessFile、序列化和反序列化、头部信息的获取、NotSerializableException解决和生成序列化版本号serialVersionUID

对象流、输出流

    • RandomAccessFile(随机访问类、针对文件)
      • RandomAccessFile-随机读写操作
    • ※--对象流(对象序列化)
      • 对象流-序列化和反序列化
      • 对象流-序列化中的头部信息设置
      • 对象流-序列化细节1
      • 对象流-序列化细节2
      • 对象流-序列化细节3
    • Properties属性类

RandomAccessFile(随机访问类、针对文件)

传统IO的使用
低级流:节点流(基础的字节流和字符流)InputStream \ OutputStream  Reader \ Writer
高级流:封装节点流
	--缓冲流【缓冲数组、性能较好】
		--缓冲流字节流[和节点流操作一致] BufferedInputStream \ BufferedOutputStream
		--缓冲流字符流     BufferedReader \ BufferedWriter
			--[输入-读取一行]
			--[输出-换行]
	--转换流【指定编码==字节流+编码(字符流)】
		--输出流 InputStreamReader 
		--输入流 OutputStreamWriter  
	--打印流【打印操作、打印方法(输出流)】
		--字节输出流 PrintStream
		--字符输出流 PrintWriter

RandomAccessFile【随机获取】【应用:文件的随机访问、文件的随机复制】
–文件访问模式的设置
–位置的移动{seek()、skipBytes()}

对象流:
序列化和反序列化的概念 【应用:序列化传输和存储】
–使用文件存储时,则需要重写流的头部信息逻辑
–readObject()
–writeObject()
–可序列化接口
–版本号

–随机访问、
–API:
seek():设置读取或写出数据操作的偏移量【从文件开头开始跳过】
skipBytes():跳过多少个字节【从当前位置开始跳过】
注意:不建议使用其他readXXX方法()

案例:
–随机访问文件中的某个位置 ,实现数据的写出或读取

RandomAccessFile-随机读写操作

/**
 * @author Lantzrung
 * @date 2022年8月2日
 * @Description
 */
import java.io.IOException;
import java.io.RandomAccessFile;

public class AccessFileDemo {
    public static void main(String[] args) throws IOException {
	// 使用RandomAccessFile进行读取数据操作
//	read();// 1、记得抛出异常操作
	wirte();
    }

    // 使用RandomAccessFile进行读取数据插入操作【随机获取、(指定位置的移动及获取操作)】
    public static void wirte() throws IOException {
	// 1、构建流 文件对象或者路径 模式读写【读、写、读写】
	// OPEN1: 这里是写入不能只是设置读取操作
//	RandomAccessFile acc = new RandomAccessFile("D:\\test\\student.txt", "r");
//	Exception in thread "main" java.io.IOException: 拒绝访问。
//	at java.io.RandomAccessFile.writeBytes(Native Method)
//	at java.io.RandomAccessFile.write(RandomAccessFile.java:512)
//	at com.g0802homework.AccessFileDemo.wirte(AccessFileDemo.java:27)
//	at com.g0802homework.AccessFileDemo.main(AccessFileDemo.java:15)

	// 这里不能单是使用w,而是要使用"r", "rw", "rws", or "rwd"
//	RandomAccessFile acc = new RandomAccessFile("D:\\test\\student.txt", "w");
//	Exception in thread "main" java.lang.IllegalArgumentException: Illegal mode "w" must be one of "r", "rw", "rws", or "rwd"
//	at java.io.RandomAccessFile.(RandomAccessFile.java:223)
//	at java.io.RandomAccessFile.(RandomAccessFile.java:124)
//	at com.g0802homework.AccessFileDemo.wirte(AccessFileDemo.java:29)
//	at com.g0802homework.AccessFileDemo.main(AccessFileDemo.java:15)

	// 正确的使用写入操作
	RandomAccessFile acc = new RandomAccessFile("D:\\test\\student.txt", "rw");// 不能添加追加模式

	// OPEN1:
	// 添加数据操作:
//	acc.seek(8);// 从头文件开始跳过
//	// 把新的写进去
//	acc.write("[code=20220102,name=li,age=15,address=gz]".getBytes());// 在文本中加进了该数据

	// OPEN2:
	// 因为RandomAccessFile不能添加追加操作,所以之前的文件会给覆盖掉,那我们要怎么操作才能不让该文件不覆盖呢?操作如下
	// 先进后面的数据读取出来,然后在进行添加的操作
	acc.seek(8);
	// 定义数组及长度
	byte[] res = new byte[1024];
	String datas = "";
	int len = 0;
	// 循环读取后面的内容
	while ((len = acc.read(res)) != -1) {
	    datas = datas + new String(res,0,len);
	}
	acc.seek(8);
	// 把新的写进去
	acc.write("[code=20220102,name=li,age=15,address=gz]".getBytes());// 在文本中加进了该数据
	// 这步很重要! 再把后面的添加进去
	// 要是不加这步就无法添加,就相当于是没有覆盖
	acc.write(datas.getBytes());
	// 关闭资源
	acc.close();
    }

    // 使用RandomAccessFile进行读取数据操作【随机获取、(指定位置的移动及获取操作)】
    public static void read() throws IOException {
	// students.txt的文本记录以下数据 Student [code=20220101, name=zhangsan, age=16,
	// address=gz]

	// 1、构建流 文件对象或者路径 模式【读、写、读写】
	RandomAccessFile acc = new RandomAccessFile("D:\\test\\students.txt", "r");

	// OPEN1:
	// 一个一个的获取数据
//	char item = (char) acc.read();
//	System.out.println(item);// S
//	item = (char) acc.read();
//	System.out.println(item);// t
//	item = (char) acc.read();
//	System.out.println(item);// u

	// OPEN2:
	// 跳过多少个字节进行读取 【从文件开头开始跳过】
//	acc.seek(9);
//	char item = (char) acc.read();
//	System.out.println(item); // c

	// OPEN3:
	// 跳过多少个字节进行读取 【从当前位置开始跳过】
//	acc.skipBytes(11);
//	char item = (char) acc.read();
//	System.out.println(item); // d

	// OPEN4:
	// 跳过多少个字节设置当前数组然后进行数组输出
//	acc.skipBytes(9);
//	byte[] res = new byte[13];
//	acc.read(res);
//	System.out.println(new String(res));// code=20220101

	// OPEN5:
//	acc.skipBytes(9);
//	byte[] res = new byte[13];
//	acc.read(res);
//	System.out.println(new String(res));// code=20220101
//	acc.seek(9);
//	acc.read(res);
//	System.out.println(new String(res));// code=20220101
//	
	// OPEN6:
	acc.skipBytes(9);
	byte[] res = new byte[13];
	acc.read(res);
	System.out.println(new String(res));// code=20220101
	acc.skipBytes(9);
	acc.read(res);
	System.out.println(new String(res));// angsan, age=1

	// 关闭资源
	acc.close();
    }
}

※–对象流(对象序列化)

序列化(将对象序列化为字节序列,可以保存在磁盘中或者通过网络进行传输)、反序列化(字节序列解析为对象)
可以应用于网络传输、比如写qq聊天室
–API:
writeObject();writeObject()方法当写出的对象没有实现Serializable接口时会抛出NotSerializableException异常
readObject(); readObject()方法当流读到尾部时会抛出EOFException异常
注意:读写的对象类型必须要实现可序列化的接口Serializable
–当在一个文件中写出多个序列化对象数据时,注意要定义子类去重写writeStreamHeader逻辑,不需要重复标记流的头部信息【reset()就是重置流的状态,相当于新的流】

–当类中有某一个类的引用并赋值时,则该引用类型也要实现可序列化接口【比如:学生中引用了一个老师】
–同一个对象只序列化一次,所以当对象数据修改后,再输出也是同样的数据(序列号相同,则不再序列化)(流有reset和没有reset的区别)【比如:同一个张三对象】
–在类实现序列化时,建议生成序列化版本号,用于辨识当前类,即使类被修改之后,也能将字节序列反序列化为正确类型的对象【比如:学生类后期修改了结构,原来的数据要反序列化为对象】

对象流-序列化和反序列化

将Student类型的对象保存在文件中,然后再从文件中读取该对象数据

/**
 * @author Lantzrung
 * @date 2022年8月2日
 * @Description
 */

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

// 实现基本流的读写操作writeStreamHeader
public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
	// 通过对象流将对象数据写出到文件进行存储【序列化】
	// writeObj();// 文件中有对象数据时,对象流需要重新reset

	// 通过对象流将文件的数据进行读取【反序列化】
	readObj();
    }

    // 通过对象流将文件的数据进行读取【反序列化】
    public static void readObj() throws IOException, ClassNotFoundException {
	// 1、通过对象流将文件的数据进行读取
	InputStream inputStream = new FileInputStream("D:\\Test\\test.txt");
	ObjectInputStream in = new ObjectInputStream(inputStream);

	// OPEN2:
	// 2、读取对象
//	Object obj = in.readObject();
//	System.out.println(obj);
//	in.close();
//	inputStream.close();
	// 输出结果为:Student [code=20220101, name=zhangsan, age=16, adress=gz]

	// OPEN3:
	// 把原本是Object的数据强制转换为Student类
//	Student obj = (Student) in.readObject();
//	System.out.println(obj.code);
//	System.out.println(obj);
//	// 关闭资源
//	in.close();
//	inputStream.close();

	// OPEN4:
	Student obj = (Student) in.readObject();
	System.out.println(obj.code);
	System.out.println(obj);
	// 读取第二次 EOFException 就是表明数据已经读完了
	obj = (Student) in.readObject();
	System.out.println(obj);
	//
	in.close();
	inputStream.close();
    }

    // 通过对象流将对象数据写出到文件进行存储[序列化]
    public static void writeObj() throws IOException {
	// 1、通过对象流将对象数据写出到文件进行存储
	OutputStream outputStream = new FileOutputStream("D:\\Test\\test.txt", true);
	ObjectOutputStream out = new ObjectOutputStream(outputStream);

	// OPEN1:
	// 2、写出对象 NotSerializableException: com.day0802.Student
	// 需要在学生类中实现Serializable的结果才不会报错
	Student stu = new Student("20220101", "zhangsan", 16, "gz");
	// 写出结果
	out.writeObject(stu);
	// 刷新资源
	out.flush();
	// 关闭资源
	out.close();
	outputStream.close();
	// test文本下输出的序列化信息:
	// 这些我们都是看不懂的,而且还有这不是乱码和别的格式化报错 而是序列化信息
	// sr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L
	// codeq ~ L nameq ~ xp t gzt 20220101t zhangsan
    }
}

// 2、写出对象 NotSerializableException: com.day0802.Student
class Student implements Serializable {

    public String code;
    public String name;
    public int age;
    public String adress;

    public Student(String code, String name, int age, String adress) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.adress = adress;
    }

    public Student() {
	super();
    }

    @Override
    public String toString() {
	return "Student [code=" + code + ", name=" + name + ", age=" + age + ", adress=" + adress + "]";
    }

}

对象流-序列化中的头部信息设置

/**
 * @author Lantzrung
 * @date 2022年8月2日
 * @Description
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

// 实现基本流的读写操作writeStreamHeader
public class ObjectStreamDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
	// 通过对象流将对象数据写出到文件进行存储【序列化】
	// 3、写入注释掉
//	 writeObj();// 文件中有对象数据时,对象流需要重新reset

	// 4、解除读取注释
	readObj();
    }

    // 通过对象流将文件的数据进行读取【反序列化】
    public static void readObj() throws IOException, ClassNotFoundException {
	InputStream inputStream = new FileInputStream("D:\\Test\\test.txt");
	ObjectInputStream in = new ObjectInputStream(inputStream);

	Student obj = (Student) in.readObject();
	System.out.println(obj.code);
	System.out.println(obj);
	Student obj1 = (Student) in.readObject();
	System.out.println(obj1);
	// 关闭资源
	in.close();
	inputStream.close();
	// 5、然后出现报错 当前读取出的数据前后不一致
//	20220101
//	Student [code=20220101, name=zhangsan, age=16, adress=gz]
//	Exception in thread "main" java.io.StreamCorruptedException: invalid type code: AC
//	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1381)
//	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
//	at com.g0802homework.ObjectStreamDemo2.readObj(ObjectStreamDemo2.java:36)
//	at com.g0802homework.ObjectStreamDemo2.main(ObjectStreamDemo2.java:25)
	// 原因是因为 ObjectOutputStream对象输出流中有这么一个头部信息writeStreamHeader
	// 因此我们要在类中重新定义这个头部信息的规则
//	protected void writeStreamHeader() throws IOException {
//        bout.writeShort(STREAM_MAGIC);
//        bout.writeShort(STREAM_VERSION);
//    }
	
	// 8、最后的反序列化结果为
//	20220101
//	Student [code=20220101, name=zhangsan, age=16, adress=gz]
//	Student [code=20220102, name=li, age=15, adress=gz]


    }

    // 通过对象流将对象数据写出到文件进行存储[序列化]
    public static void writeObj() throws IOException {
	// 1、通过对象流将对象数据写出到文件进行存储
	OutputStream outputStream = new FileOutputStream("D:\\Test\\test.txt", true);
//	ObjectOutputStream out = new ObjectOutputStream(outputStream);
	// 就是告诉计算机我这里是这个路径,判断它是否为空
	// 设置文件对象
	ObjectOutputStreamChild.setFile(new File("D:\\Test\\test.txt"));
	// 7、重新设置构建对象流
	ObjectOutputStreamChild out = new ObjectOutputStreamChild(outputStream);
	// ObjectOutputStream的源码中有这么一个头部信息的方法
	// writeStreamHeader();

	// OPEN1:
	// 当然也就可以不这样一个一个的注释 但是注意这里输出的序列化不一样的
	// 这是一个一个的输入的序列号
//	 sr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220101t zhangsanysr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220102t li

	// 这是把stu变量名字为li的改为stu1 ,然后加入out.writeObject(stu1) 的序列化结果
//	 sr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220101t zhangsansq ~     q ~ t 20220102t li
	// 1、先在导入第一个stu对象类
	// 2、再导入第二个stu对象类
//	Student stu = new Student("20220101", "zhangsan", 16, "gz");
	Student stu = new Student("20220102", "li", 15, "gz");
	// 写出结果
	out.writeObject(stu);
//	out.writeObject(stu1);
	// 刷新资源
	out.flush();
	// 关闭资源
	out.close();
	outputStream.close();
    }
}

// 6、提供一个修改头部信息的子类
class ObjectOutputStreamChild extends ObjectOutputStream {

    // 定义一个文件的引用,用于进行判断文件中是否已经拥有了数据
    private static File file = null;

    // 提供getSet方法
    public static File getFile() {
	return file;
    }

    public static void setFile(File file) {
	ObjectOutputStreamChild.file = file;
    }

    // 提供父类的构造器
    protected ObjectOutputStreamChild() throws IOException, SecurityException {
	super();
    }

    public ObjectOutputStreamChild(OutputStream out) throws IOException {
	super(out);
    }

    // 重写writeStreamHeader的操作逻辑
    @Override
    protected void writeStreamHeader() throws IOException {
	// 标志当前的信息和版本
	if (file.length() == 0) {// 第一次操作时候,空文件操作
	    super.writeStreamHeader();
	} else {// 已经存在数据后
	    this.reset();// 回设状态
	}
    }
}

class Student implements Serializable {

    public String code;
    public String name;
    public int age;
    public String adress;

    public Student(String code, String name, int age, String adress) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.adress = adress;
    }

    public Student() {
	super();
    }

    @Override
    public String toString() {
	return "Student [code=" + code + ", name=" + name + ", age=" + age + ", adress=" + adress + "]";
    }

}

对象流-序列化细节1

–当类中有某一个类的引用并赋值时,则该引用类型也要实现可序列化接口【比如:学生中引用了一个老师】

/**
 * @author Lantzrung
 * @date 2022年8月2日
 * @Description
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import javax.security.auth.Subject;

// 在同一次流写出操作时,写出同一个对象的效果【流有reset和没有reset的区别】
public class ObjectStreamDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, IOException {
	readObj();
//	 writeObj();
	// 注意:当出现这个结果时候是要设置头部信息的或者删除里面的文本里面的多个序列化信息
	// Exception in thread "main" java.io.StreamCorruptedException: invalid type
	// code: AC

//	OPEN1:
//	第一次写出的操作中输出的是注意这里只有一个序列化  
	// sr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L
	// codeq ~ L nameq ~ xp t gzt 20220102t liq ~ 
	// 但是读到的是20220102 而且地址没有根据改写为shenzhen
//	Student [code=20220102, name=li, age=15, adress=gz]
//	Student [code=20220102, name=li, age=15, adress=gz]

	// OPEN3:
	// 先进行清空,然后开始重新写入序列化信息

	// 当前输入的序列化信息有两条,一条是广州,一条是深圳
	//  sr com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L
	// codeq ~ L nameq ~ xp t gzt 20220102t liysr
	// com.g0802homework.StudentO熭?態? I ageL adresst Ljava/lang/String;L codeq
	// ~ L nameq ~ xp t shenzhent 20220102t li
//	20220102
//	Student [code=20220102, name=li, age=15, adress=gz]
//	Student [code=20220102, name=li, age=15, adress=shenzhen]

    }

    // 【反序列化】
    public static void readObj() throws IOException, ClassNotFoundException {
	// 1、通过对象流将文件的数据进行读取
	InputStream InputStream = new FileInputStream("D:\\Test\\test.txt");
	ObjectInputStream in = new ObjectInputStream(InputStream);
	// 2、读取对象
	Student obj = (Student) in.readObject();
	System.out.println(obj.code);
	// 第一次读取
	System.out.println(obj);
	//
	Student obj1 = (Student) in.readObject();
	// 第二次读取
	System.out.println(obj1);
	// OPEN2:第三次读取出现异常 Exception in thread "main" java.io.EOFException 说明流已经读取完了
	// 所以不要超出输入流的数据
//	Student obj2 = (Student) in.readObject();
//	System.out.println(obj2);

    }

    // 通过对象流将对象数据写出到文件进行存储【序列化】
    public static void writeObj() throws IOException {
	// 1、通过对象流将对象数据写出到文件进行存储
	OutputStream outputStream = new FileOutputStream("D:\\Test\\test.txt", true);
	// 设置文件对象
	ObjectOutputStreamChild.setFile(new File("D:\\Test\\test.txt"));
	// 构建对象流
	ObjectOutputStream out = new ObjectOutputStream(outputStream);
	Student stu = new Student("20220102", "li", 15, "gz");
	// 写出一次
	out.writeObject(stu);
	// 刷新
	out.flush();
	// 重置对象流的状态
	out.reset();
	// OPEN3:第二次操作写出第二次
	stu.adress = "shenzhen";
	out.writeObject(stu);
	// 刷新
	out.flush();
	// 关闭
	out.close();
	outputStream.close();
    }
}

对象流-序列化细节2

–同一个对象只序列化一次,所以当对象数据修改后,再输出也是同样的数据(序列号相同,则不再序列化)(流有reset和没有reset的区别)【比如:同一个张三对象】
注意:细节不然会出现报错 在一个类中有引用了另一个类时,则都需要实现可序列化接口
不然会出现Exception in thread “main” java.io.NotSerializableException报错

/**
 * @author Lantzrung
 * @date 2022年8月3日
 * @Description
 */
package com.daily01;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;

// 注意:细节不然会出现报错 在一个类中有引用了另一个类时,则都需要实现可序列化接口

//	Exception in thread "main" java.io.NotSerializableException: com.daily01.Teacher
//	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
//	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
//	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
//	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
//	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
//	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
public class ObjectStreamDemo4 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
	// 通过对象流将对象数据写出到文件进行存储【序列化】
	writeObj();// 文件中有对象数据时,对象流要重新reset
    }

    // 通过对象流将对象数据写出到文件进行存储[序列化]
    public static void writeObj() throws IOException {
	// 1、通过对象流将对象数据写出到文件进行存储
	OutputStream outputStream = new FileOutputStream("D:\\test\\test.txt", true);
	// 设置文件对象
	ObjectOutputStreamChild.setFile(new File("D:\\test\\test.txt"));
	// 构建对象流
	ObjectOutputStreamChild out = new ObjectOutputStreamChild(outputStream);
	// 2、写出对象 NotSerializableException: com.day0802.Student
	Student stu = new Student("20220101", "zhangsan", 16, "gz");
	// 给学生设置一个班主任老师 NotSerializableException: com.day0802.Teacher
	stu.setMaster(new Teacher("2022", "zhang", 36, "gz"));
	// 写出一次
	out.writeObject(stu);
	// 刷新
	out.flush();
	// 管必须
	out.close();
	outputStream.close();
    }
}

class Student implements Serializable {
    public String code;
    public String name;
    public int age;
    public String address;

    // OPEN1 关联引用老师对象
    public Teacher master;

    public Teacher getMaster() {
	return master;
    }

    public void setMaster(Teacher master) {
	this.master = master;
    }

    public String getCode() {
	return code;
    }

    public void setCode(String code) {
	this.code = code;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public int getAge() {
	return age;
    }

    public void setAge(int age) {
	this.age = age;
    }

    public String getAddress() {
	return address;
    }

    public void setAddress(String address) {
	this.address = address;
    }

    public Student(String code, String name, int age, String address, Teacher master) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.address = address;
	this.master = master;
    }

    public Student(String code, String name, int age, String address) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.address = address;
    }

    public Student() {
	super();
    }

    @Override
    public String toString() {
	return "Student [code=" + code + ", name=" + name + ", age=" + age + ", address=" + address + ", master="
		+ master + "]";
    }

}

class Teacher {
    public String code;
    public String name;
    public int age;
    public String address;

    public String getCode() {
	return code;
    }

    public void setCode(String code) {
	this.code = code;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public int getAge() {
	return age;
    }

    public void setAge(int age) {
	this.age = age;
    }

    public String getAddress() {
	return address;
    }

    public void setAddress(String address) {
	this.address = address;
    }

    public Teacher(String code, String name, int age, String address) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.address = address;
    }

    public Teacher() {
	super();
    }

    @Override
    public String toString() {
	return "Teacher [code=" + code + ", name=" + name + ", age=" + age + ", address=" + address + "]";
    }

}

对象流-序列化细节3

–在类实现序列化时,建议生成序列化版本号,用于辨识当前类,即使类被修改之后,也能将字节序列反序列化为正确类型的对象【比如:学生类后期修改了结构,原来的数据要反序列化为对象】

在文件中已经存储对象数据,但是由于后期业务更改,类的结构变化了[版本号不一致导致数据不能反序列化]
解决方案:在类定义时,加上serialVersionUID

/**
 * @author Lantzrung
 * @date 2022年8月3日
 * @Description
 */
package com.daily01;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

//在文件中已经存储对象数据,但是由于后期业务更改,类的结构变化了[版本号不一致导致数据不能反序列化]
//解决方案:在类定义时,加上serialVersionUID
public class ObjectStreamDemo5 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
	// 通过对象流将对象数据写出到文件进行存储【序列化】
//		writeObj();// 文件中有对象数据时,对象流要重新reset

	//
	readObj();

	// OPEN1:
//	正常读写入操作 不加入老师的全参和无参toString方法 注释掉学生类中的关联老师对象
	// 关联引用老师对象
//	序列化为: 
//	sr com.daily01.Student鋶=1徣~ I ageL addresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220101t zhangsan
	// 输出结果为:
//	20220101
//	Student [code=20220101, name=zhangsan, age=16, address=gz]

	// OPEN2:
	// 修改完后重新读
	// 就会出现报错 非法类的异常InvalidClassException
	// 前后版本不一致 serialVersionUID = -1977857380187125634, local class serialVersionUID
	// = 3949624502463657062
	// Exception in thread "main" java.io.InvalidClassException:
	// com.daily01.Student; local class incompatible: stream classdesc
	// serialVersionUID = -1977857380187125634, local class serialVersionUID =
	// 3949624502463657062

	// 解决方案:先把关联老师的所有全参无参toString删除或者注释
//	加上序列号
//	(此操作是在Student 警告处:)
//	The serializable class Student does not declare a static final serialVersionUID field of type long
//	Add generated serial version ID 中加上序列号的
	// 重新写入关于学生的全参有参构造器和toString方法
	// 然后再重新执行读取写入操作
//	序列化为: 
//	sr com.daily01.Student鋶=1徣~ I ageL addresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220101t zhangsan
	// 输出结果为:
//	20220101
//	Student [code=20220101, name=zhangsan, age=16, address=gz]
	// 重新写入关于老师的全参有参构造器和toString方法
	// 然后再重新执行读取写入操作
//	序列化为: 
//	sr com.daily01.Student鋶=1徣~ I ageL addresst Ljava/lang/String;L codeq ~ L nameq ~ xp   t gzt 20220101t zhangsan
	// 输出结果为:
//	20220101
//	Student [code=20220101, name=zhangsan, age=16, address=gz, master=null]
	//到这里不需要管main中的报错,当然你可以加上一个null或者老师的名字
	
	
    }

    // 通过对象流将文件的数据进行读取【反序列化】
    public static void readObj() throws IOException, ClassNotFoundException {
	// 1、通过对象流将文件的数据进行读取
	InputStream inputStream = new FileInputStream("D:\\test\\test.txt");
	ObjectInputStream in = new ObjectInputStream(inputStream);
	// 2、读取对象
	Student obj = (Student) in.readObject();
	System.out.println(obj.code);
	System.out.println(obj);
	//
	in.close();
	inputStream.close();
    }

    // 通过对象流将对象数据写出到文件进行存储[序列化]
    public static void writeObj() throws IOException {
	// 1、通过对象流将对象数据写出到文件进行存储
	OutputStream outputStream = new FileOutputStream("D:\\test\\test.txt", true);
	// 设置文件对象
	ObjectOutputStreamChild.setFile(new File("D:\\test\\test.txt"));
	// 构建对象流
	ObjectOutputStreamChild out = new ObjectOutputStreamChild(outputStream);
	// 2、写出对象 NotSerializableException: com.day0802.Student
	Student stu = new Student("20220101", "zhangsan", 16, "gz");
	// 写出一次
	out.writeObject(stu);
	// 刷新
	out.flush();
	// 管必须
	out.close();
	outputStream.close();
    }
}
/**
 * @author Lantzrung
 * @date 2022年8月3日
 * @Description
 */
package com.daily01;

import java.io.Serializable;

//学生类    实现可序列化接口【标记接口】
public class Student implements Serializable {
    
    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = -1977857380187125634L;
    public String code;
    public String name;
    public int age;
    public String address;

    // OPEN1:不加入老师的全参和无参toString方法 注释关联老师方法
    
    // OPEN2: 加入关联老师的构造器 自行修改全参无参和toString方法qwq
    // 关联引用老师对象
    public Teacher master;

    public Teacher getMaster() {
	return master;
    }

    public void setMaster(Teacher master) {
	this.master = master;
    }

    public Student() {
	super();
    }

    public Student(String code, String name, int age, String address, Teacher master) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.address = address;
	this.master = master;
    }

    @Override
    public String toString() {
	return "Student [code=" + code + ", name=" + name + ", age=" + age + ", address=" + address + ", master="
		+ master + "]";
    }

    public String getCode() {
	return code;
    }

    public void setCode(String code) {
	this.code = code;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public int getAge() {
	return age;
    }

    public void setAge(int age) {
	this.age = age;
    }

    public String getAddress() {
	return address;
    }

    public void setAddress(String address) {
	this.address = address;
    }
}

/**
 * @author Lantzrung
 * @date 2022年8月3日
 * @Description
 */
package com.daily01;

public class Teacher {
    public String code;
    public String name;
    public int age;
    public String address;

    public String getCode() {
	return code;
    }

    public void setCode(String code) {
	this.code = code;
    }

    public String getName() {
	return name;
    }

    public void setName(String name) {
	this.name = name;
    }

    public int getAge() {
	return age;
    }

    public void setAge(int age) {
	this.age = age;
    }

    public String getAddress() {
	return address;
    }

    public void setAddress(String address) {
	this.address = address;
    }

    public Teacher(String code, String name, int age, String address) {
	super();
	this.code = code;
	this.name = name;
	this.age = age;
	this.address = address;
    }

    public Teacher() {
	super();
    }

    @Override
    public String toString() {
	return "Teacher [code=" + code + ", name=" + name + ", age=" + age + ", address=" + address + "]";
    }

}

Properties属性类

一般结合io流,读取配置文件来配置环境
–properties.setProperty(“name”, “张三”);
–properties.getProperty(“name”)
–properties.load(new FileInputStream(“D:\test\test.txt”));
–获取key集合properties.stringPropertyNames();

你可能感兴趣的:(05,IO流和多线程,nio,java,jvm,算法,开发语言)