例子
这里定义了一个User的序列化对象:
这里做个写入文件的测试,也就是尝试序列化对象:
@RestController
public class TestController {
@GetMapping("/write")
public static void writeObj() {
User user = new User("sc", 1);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("/Users/suncong/Desktop/user.txt")));
objectOutputStream.writeObject(user);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/read")
public void readObj(){
try {
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get("/Users/suncong/Desktop/user.txt")));
try {
Object object = objectInputStream.readObject();
User user = (User) object;
System.out.println(user);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
触发Write方法,我们可以看到user.txt存储的是一对二进制文件
触发Read请求,从序列化中读出来了:
去掉implements
触发Read 报错 反序列化非法
改变serialVersionUID
可以很清楚的看到报错:
这样就有个严重的问题了,?
不同的后端反序列化同一个对象的话 指定的ID不一样怎么办 serialVersionUID
是Java中用于序列化的一个重要属性,用于标识序列化对象的版本号。在序列化和反序列化过程中,Java虚拟机会根据serialVersionUID
来确定对象的版本信息,从而保证对象的正确性。
在Java中,serialVersionUID
的写法有两种:
显式声明serialVersionUID
变量
显式声明serialVersionUID
变量是最常用的方式,可以通过在类中定义一个名为serialVersionUID
的静态变量来指定序列化版本号。例如:
public class MyClass implements Serializable {
private static final long serialVersionUID = 123456789L;
// ...
}
在这个例子中,我们通过定义一个名为serialVersionUID
的静态变量,并赋予一个固定值来指定序列化版本号。这个值可以是任何长整型数字,通常建议使用随机数或时间戳等唯一标识符。
自动生成serialVersionUID
变量
如果一个类没有显式声明serialVersionUID
变量,则Java虚拟机会自动为它生成一个序列化版本号。这个自动生成的版本号是根据类的结构和成员变量等信息计算得出的,因此可以保证在不同的JVM上生成相同的值。例如:
public class MyClass implements Serializable {
// ...
}
在这个例子中,我们没有显式声明serialVersionUID
变量,因此Java虚拟机会自动为它生成一个版本号。如果需要查看自动生成的版本号,可以使用serialver
命令行工具(例如:serialver MyClass
)来获取。
用JSON或者XML格式的 更好的兼容的
public class MyClass {
private int id;
private String name;
// getters and setters
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.setId(1);
obj.setName("Alice");
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println(json); // {"id":1,"name":"Alice"}
}
}
jmap查看
注意,在开始前(包括本机jvm命令),确保两点:
1.如果你用的macOS 会稍微有点麻烦,详情看这个博客:
www.cnblogs.com/itpower/p/1…
开始:
jmap + pid
使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称
jmap -heap pid
打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息
jmap -histo:live pid
存活的对象 我这里有两千多 我就截短图了
jmap -clstats pid
打印类加载器信息
jmap -dump:format=b,file=heapdump.phrof pid
以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。