Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
沉默王二
Jmx Java
提交
8f94b6b4
J
Jmx Java
项目概览
沉默王二
/
Jmx Java
大约 1 年 前同步成功
通知
160
Star
18
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
Jmx Java
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
8f94b6b4
编写于
3月 23, 2023
作者:
沉默王二
💬
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
序列化
上级
cebabe20
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
144 addition
and
164 deletion
+144
-164
README.md
README.md
+1
-1
docs/home.md
docs/home.md
+1
-1
docs/io/print.md
docs/io/print.md
+3
-4
docs/io/serialize.md
docs/io/serialize.md
+139
-158
未找到文件。
README.md
浏览文件 @
8f94b6b4
...
...
@@ -176,7 +176,7 @@
-
[
缓冲流:Java IO 的读写效率有了质的飞升
](
docs/io/buffer.md
)
-
[
转换流:Java 字节流和字符流的桥梁
](
docs/io/char-byte.md
)
-
[
打印流:PrintStream & PrintWriter
](
docs/io/print.md
)
-
[
序列流:
字节和对象之间
的序列化和反序列化
](
docs/io/serialize.md
)
-
[
序列流:
Java 对象
的序列化和反序列化
](
docs/io/serialize.md
)
-
[
Serializable:明明就一个空的接口嘛
](
docs/io/Serializbale.md
)
-
[
Java transient关键字
](
docs/io/transient.md
)
...
...
docs/home.md
浏览文件 @
8f94b6b4
...
...
@@ -187,7 +187,7 @@ head:
-
[
缓冲流:Java IO 的读写效率有了质的飞升
](
io/buffer.md
)
-
[
转换流:Java 字节流和字符流的桥梁
](
io/char-byte.md
)
-
[
打印流:PrintStream & PrintWriter
](
io/print.md
)
-
[
序列流:
字节和对象之间
的序列化和反序列化
](
io/serialize.md
)
-
[
序列流:
Java 对象
的序列化和反序列化
](
io/serialize.md
)
-
[
Serializable:明明就一个空的接口嘛
](
io/Serializbale.md
)
-
[
说说Java transient关键字
](
io/transient.md
)
...
...
docs/io/print.md
浏览文件 @
8f94b6b4
---
title
:
打印流:PrintStream
&
PrintWriter
title
:
打印流:PrintStream
和
PrintWriter
shortTitle
:
打印流
category
:
-
Java核心
tag
:
-
Java IO
description
:
Java程序员进阶之路,小白的零基础Java教程,Java打印流:PrintStream
&
PrintWriter
description
:
Java程序员进阶之路,小白的零基础Java教程,Java打印流:PrintStream
和
PrintWriter
head
:
-
-
meta
-
name
:
keywords
content
:
Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java IO,打印流,PrintStream,PrintWriter,java 打印流
content
:
Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java IO,打印流,PrintStream,PrintWriter,java 打印流,java PrintStream,java PrintWriter
---
# 7.7 打印流
...
...
docs/io/serialize.md
浏览文件 @
8f94b6b4
---
title
:
Java序列化流,字节和对象之间
的序列化和反序列化
shortTitle
:
Java序列化
流(序列化和反序列化)
title
:
序列流:Java 对象
的序列化和反序列化
shortTitle
:
序列
流(序列化和反序列化)
category
:
-
Java核心
tag
:
...
...
@@ -9,219 +9,201 @@ description: Java程序员进阶之路,小白的零基础Java教程,Java序
head
:
-
-
meta
-
name
:
keywords
content
:
Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java IO,序列化流,java序列化,java反序列化,ObjectOutputStream,ObjectInputStream
content
:
Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java入门,教程,Java IO,序列化流,java序列化,java反序列化,ObjectOutputStream,ObjectInputStream
,java 序列流
---
序列化有什么好处呢?可以把对象写入文本文件或者在网络中传输。
# 7.8 序列流(序列化和反序列化)
如何实现序列化呢?让被序列化的对象所属类实现
[
Serializbale序列化接口
](
https://tobebetterjavaer.com/io/Serializbale.html
)
。
Java 的序列流(ObjectInputStream 和 ObjectOutputStream)是一种可以将 Java 对象序列化和反序列化的流
。
接着我们来继续聊序列化和反
序列化。
序列化是指将一个对象转换为一个字节序列(包含
`对象的数据`
、
`对象的类型`
和
`对象中存储的属性`
等信息),以便在网络上传输或保存到文件中,或者在程序之间传递。在 Java 中,序列化通过实现 java.io.Serializable 接口来实现,只有实现了
[
Serializable 接口
](
https://tobebetterjavaer.com/io/Serializbale.html
)
的对象才能被
序列化。
## 何谓序列化
反序列化是指将一个字节序列转换为一个对象,以便在程序中使用。
Java 提供了一种对象
**序列化**
的机制。用一个字节序列可以表示一个对象,该字节序列包含该
`对象的数据`
、
`对象的类型`
和
`对象中存储的属性`
等信息。字节序列写出到文件之后,相当于文件中
**持久保存**
了一个对象的信息。
![](
https://cdn.tobebetterjavaer.com/stutymore/serialize-20230323105551.png
)
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行
**反序列化**
。
`对象的数据`
、
`对象的类型`
和
`对象中存储的数据`
信息,都可以用来在内存中创建对象。看图理解序列化:
### 01、ObjectOutputStream
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/io/serialize-8a1b6818-5f58-4057-b521-f8ba670d72a1.png
)
`java.io.ObjectOutputStream`
继承自 OutputStream 类,因此可以将序列化后的字节序列写入到文件、网络等输出流中。
## ObjectOutputStream类
来看 ObjectOutputStream 的构造方法:
`ObjectOutputStream(OutputStream out)`
`java.io.ObjectOutputStream`
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
### 构造方法
`public ObjectOutputStream(OutputStream out)`
: 创建一个指定OutputStream的ObjectOutputStream。
构造代码如下:
该构造方法接收一个 OutputStream 对象作为参数,用于将序列化后的字节序列输出到指定的输出流中。例如:
```
java
FileOutputStream
f
ileOut
=
new
FileOutputStream
(
"aa
.txt"
);
ObjectOutputStream
o
ut
=
new
ObjectOutputStream
(
fileOut
);
FileOutputStream
f
os
=
new
FileOutputStream
(
"file
.txt"
);
ObjectOutputStream
o
os
=
new
ObjectOutputStream
(
fos
);
```
### 序列化操作
一个对象要想序列化,必须满足两个条件:
1.
一个对象要想序列化,必须满足两个条件:
-
该类必须实现
[
`java.io.Serializable` 接口
](
https://tobebetterjavaer.com/io/Serializbale.html
)
,否则会抛出
`NotSerializableException`
。
-
该类的所有字段都必须是可序列化的。如果一个字段不需要序列化,则需要使用
[
`transient` 关键字
](
https://tobebetterjavaer.com/io/transient.html
)
进行修饰。
-
该类必须实现
[
`java.io.Serializable` 接口
](
https://tobebetterjavaer.com/io/Serializbale.html
)
,
`Serializable`
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出
`NotSerializableException`
。
-
该类的所有属性都必须是可序列化的。如果有一个属性不需要可序列化,则该属性必须注明是瞬态的,使用
[
`transient` 关键字
](
https://tobebetterjavaer.com/io/transient.html
)
修饰。
使用示例如下:
```
java
public
class
Employee
implements
java
.
io
.
Serializable
{
public
class
Employee
implements
Serializable
{
public
String
name
;
public
String
address
;
public
transient
int
age
;
// transient瞬态修饰成员,不会被序列化
public
void
addressCheck
()
{
System
.
out
.
println
(
"Address check : "
+
name
+
" -- "
+
address
);
}
}
```
2.
写出对象方法
`public final void writeObject (Object obj)`
: 将指定的对象写出。
接下来,来聊聊
`writeObject (Object obj)`
方法,该方法是 ObjectOutputStream 类中用于将对象序列化成字节序列并输出到输出流中的方法,可以处理对象之间的引用关系、继承关系、静态字段和 transient 字段。
```
java
public
class
SerializeDemo
{
public
static
void
main
(
String
[]
args
)
{
Employee
e
=
new
Employee
();
e
.
name
=
"zhangsan"
;
e
.
address
=
"beiqinglu"
;
e
.
age
=
20
;
try
{
// 创建序列化流对象
ObjectOutputStream
out
=
new
ObjectOutputStream
(
new
FileOutputStream
(
"employee.txt"
));
// 写出对象
out
.
writeObject
(
e
);
// 释放资源
out
.
close
();
fileOut
.
close
();
System
.
out
.
println
(
"Serialized data is saved"
);
// 姓名,地址被序列化,年龄没有被序列化。
}
catch
(
IOException
i
)
{
i
.
printStackTrace
();
public
class
ObjectOutputStreamDemo
{
public
static
void
main
(
String
[]
args
)
{
Person
person
=
new
Person
(
"沉默王二"
,
20
);
try
{
FileOutputStream
fos
=
new
FileOutputStream
(
"logs/person.dat"
);
ObjectOutputStream
oos
=
new
ObjectOutputStream
(
fos
);
oos
.
writeObject
(
person
);
oos
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
}
}
输出结果
:
Serialized
data
is
saved
```
class
Person
implements
Serializable
{
private
String
name
;
private
int
age
;
public
Person
(
String
name
,
int
age
)
{
this
.
name
=
name
;
this
.
age
=
age
;
}
## ObjectInputStream类
public
String
getName
()
{
return
name
;
}
public
int
getAge
()
{
return
age
;
}
}
```
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象
。
上面的代码中,首先创建了一个 Person 对象,然后使用 FileOutputStream 和 ObjectOutputStream 将 Person 对象序列化并输出到 person.dat 文件中。在 Person 类中,实现了 Serializable 接口,表示该类可以进行对象序列化
。
###
构造方法
###
02、ObjectInputStream
`public ObjectInputStream(InputStream in)`
: 创建一个指定InputStream的ObjectInputStream
。
ObjectInputStream 可以读取 ObjectOutputStream 写入的字节流,并将其反序列化为相应的对象(包含
`对象的数据`
、
`对象的类型`
和
`对象中存储的属性`
等信息)
。
### 反序列化操作1
说简单点就是,序列化之前是什么样子,反序列化后就是什么样子。
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用
`ObjectInputStream`
读取对象的方法:
来看一下构造方法:
`ObjectInputStream(InputStream in)`
: 创建一个指定 InputStream 的 ObjectInputStream。
*
`public final Object readObject ()`
: 读取一个对象。
其中,ObjectInputStream 的 readObject 方法用来读取指定文件中的对象,示例如下:
```
java
public
class
DeserializeDemo
{
public
static
void
main
(
String
[]
args
)
{
Employee
e
=
null
;
try
{
// 创建反序列化流
FileInputStream
fileIn
=
new
FileInputStream
(
"employee.txt"
);
ObjectInputStream
in
=
new
ObjectInputStream
(
fileIn
);
// 读取一个对象
e
=
(
Employee
)
in
.
readObject
();
// 释放资源
in
.
close
();
fileIn
.
close
();
}
catch
(
IOException
i
)
{
// 捕获其他异常
i
.
printStackTrace
();
return
;
}
catch
(
ClassNotFoundException
c
)
{
// 捕获类找不到异常
System
.
out
.
println
(
"Employee class not found"
);
c
.
printStackTrace
();
return
;
}
// 无异常,直接打印输出
System
.
out
.
println
(
"Name: "
+
e
.
name
);
// zhangsan
System
.
out
.
println
(
"Address: "
+
e
.
address
);
// beiqinglu
System
.
out
.
println
(
"age: "
+
e
.
age
);
// 0
}
String
filename
=
"logs/person.dat"
;
// 待反序列化的文件名
try
(
FileInputStream
fileIn
=
new
FileInputStream
(
filename
);
ObjectInputStream
in
=
new
ObjectInputStream
(
fileIn
))
{
// 从指定的文件输入流中读取对象并反序列化
Object
obj
=
in
.
readObject
();
// 将反序列化后的对象强制转换为指定类型
Person
p
=
(
Person
)
obj
;
// 打印反序列化后的对象信息
System
.
out
.
println
(
"Deserialized Object: "
+
p
);
}
catch
(
IOException
|
ClassNotFoundException
e
)
{
e
.
printStackTrace
();
}
```
**JVM可以反序列化的对象,必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 `ClassNotFoundException` 异常。**
我们首先指定了待反序列化的文件名(前面通过 ObjectOutputStream 序列化后的文件),然后创建了一个 FileInputStream 对象和一个 ObjectInputStream 对象。接着我们调用 ObjectInputStream 的 readObject 方法来读取指定文件中的对象,并将其强制转换为 Person 类型。最后我们打印了反序列化后的对象信息。
### 03、Kryo
实际开发中,很少使用 JDK 自带的序列化和反序列化,这是因为:
-
可移植性差:Java 特有的,无法跨语言进行序列化和反序列化。
-
性能差:序列化后的字节体积大,增加了传输/保存成本。
-
安全问题:攻击者可以通过构造恶意数据来实现远程代码执行,从而对系统造成严重的安全威胁。相关阅读:
[
Java 反序列化漏洞之殇
](
https://cryin.github.io/blog/secure-development-java-deserialization-vulnerability/
)
。
### 反序列化操作2
Kryo 是一个优秀的 Java 序列化和反序列化库,具有高性能、高效率和易于使用和扩展等特点,有效地解决了 JDK 自带的序列化机制的痛点。
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
`InvalidClassException`
异常。发生这个异常的原因如下:
>GitHub 地址:[https://github.com/EsotericSoftware/kryo](https://github.com/EsotericSoftware/kryo)
> 1、该类的序列版本号与从流中读取的类描述符的版本号不匹配
>
> 2、该类包含未知数据类型
>
> 2、该类没有可访问的无参数构造方法
使用示例:
`Serializable`
接口给需要序列化的类,提供了一个序列版本号。
`serialVersionUID`
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
第一步,在 pom.xml 中引入依赖。
```
<!-- 引入 Kryo 序列化工具 -->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.4.0</version>
</dependency>
```
第二步,创建一个 Kryo 对象,并使用
`register()`
方法将对象进行注册。然后,使用
`writeObject()`
方法将 Java 对象序列化为二进制流,再使用
`readObject()`
方法将二进制流反序列化为 Java 对象。最后,输出反序列化后的 Java 对象。
```
java
public
class
Employee
implements
java
.
io
.
Serializable
{
// 加入序列版本号
private
static
final
long
serialVersionUID
=
1L
;
public
String
name
;
public
String
address
;
// 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
public
int
eid
;
public
void
addressCheck
()
{
System
.
out
.
println
(
"Address check : "
+
name
+
" -- "
+
address
);
}
public
class
KryoDemo
{
public
static
void
main
(
String
[]
args
)
throws
FileNotFoundException
{
Kryo
kryo
=
new
Kryo
();
kryo
.
register
(
KryoParam
.
class
);
KryoParam
object
=
new
KryoParam
(
"沉默王二"
,
123
);
Output
output
=
new
Output
(
new
FileOutputStream
(
"logs/kryo.bin"
));
kryo
.
writeObject
(
output
,
object
);
output
.
close
();
Input
input
=
new
Input
(
new
FileInputStream
(
"logs/kryo.bin"
));
KryoParam
object2
=
kryo
.
readObject
(
input
,
KryoParam
.
class
);
System
.
out
.
println
(
object2
);
input
.
close
();
}
}
```
## 序列化集合练习
class
KryoParam
{
private
String
name
;
private
int
age
;
public
KryoParam
()
{
}
1.
将存有多个自定义对象的集合序列化操作,保存到
`list.txt`
文件中。
2.
反序列化
`list.txt`
,并遍历集合,打印对象信息。
public
KryoParam
(
String
name
,
int
age
)
{
this
.
name
=
name
;
this
.
age
=
age
;
}
### 案例分析
public
String
getName
()
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
1.
把若干学生对象 ,保存到集合中。
2.
把集合序列化。
3.
反序列化读取时,只需要读取一次,转换为集合类型。
4.
遍历集合,可以打印所有的学生信息
public
int
getAge
()
{
return
age
;
}
### 案例代码实现
public
void
setAge
(
int
age
)
{
this
.
age
=
age
;
}
```
java
public
class
SerTest
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
// 创建 学生对象
Student
student
=
new
Student
(
"老王"
,
"laow"
);
Student
student2
=
new
Student
(
"老张"
,
"laoz"
);
Student
student3
=
new
Student
(
"老李"
,
"laol"
);
ArrayList
<
Student
>
arrayList
=
new
ArrayList
<>();
arrayList
.
add
(
student
);
arrayList
.
add
(
student2
);
arrayList
.
add
(
student3
);
// 序列化操作
// serializ(arrayList);
// 反序列化
ObjectInputStream
ois
=
new
ObjectInputStream
(
new
FileInputStream
(
"list.txt"
));
// 读取对象,强转为ArrayList类型
ArrayList
<
Student
>
list
=
(
ArrayList
<
Student
>)
ois
.
readObject
();
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++
){
Student
s
=
list
.
get
(
i
);
System
.
out
.
println
(
s
.
getName
()+
"--"
+
s
.
getPwd
());
}
}
private
static
void
serializ
(
ArrayList
<
Student
>
arrayList
)
throws
Exception
{
// 创建 序列化流
ObjectOutputStream
oos
=
new
ObjectOutputStream
(
new
FileOutputStream
(
"list.txt"
));
// 写出对象
oos
.
writeObject
(
arrayList
);
// 释放资源
oos
.
close
();
}
@Override
public
String
toString
()
{
return
"KryoParam{"
+
"name='"
+
name
+
'\''
+
", age="
+
age
+
'}'
;
}
}
```
### 04、小结
>参考链接:[https://www.cnblogs.com/yichunguo/p/11775270.html](https://www.cnblogs.com/yichunguo/p/11775270.html),整理:沉默王二
本节我们介绍了 Java 的序列化机制,并推荐了一款高性能的 Java 类库 Kryo 来取代 JDK 自带的序列化机制,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛使用。
以上,希望能帮助到大家。
---------
...
...
@@ -229,5 +211,4 @@ public class SerTest {
微信搜
**沉默王二**
或扫描下方二维码关注二哥的原创公众号沉默王二,回复
**111**
即可免费领取。
![](
https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png
)
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录