该文章Github地址:https://github.com/AntonyCheng/java-notes
在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn.net/AntonyCheng/article/details/136555245),该模板集成了最常见的开发组件,同时基于修改配置文件实现组件的装载,除了这些,模板中还有非常丰富的整合示例,同时单体架构也非常适合SpringBoot框架入门,如果觉得有意义或者有帮助,欢迎Star & Issues & PR!
上一章:由浅到深认识Java语言(37):I/O流
44.I/O流(操作文件中的数据)
对象的序列化
对象序列化:简单来说就是用 IO 流技术把对象写入(ObjectOutputStream 类)文件,反之,从文件中读取(ObjectInputStream 类)一个对象就是对象的反序列化,==所以先要做序列化,才能做反序列化,这两个类依然是"装饰者";==
**开启对象序列化:**每一个普通类是没有序列化权限的,必须要实现 Serializable 接口以启用序列化;
**对象的序列化意义:**把对象变成一个文件,允许这个对象文件在网络上传输,可以让任何人使用;
JavaBeans
概念
JavaBeans是 Java 中一种特殊的类,可以将多个对象封装到一个对象(bean)中。特点是可序列化,提供无参构造器,提供 getter 方法和 setter 方法访问对象的属性。名称中的“Bean”是用于Java的可重用软件组件的惯用叫法。
优点
- Bean可以控制它的属性、事件和方法是否暴露给其他程序。
- Bean可以接收来自其他对象的事件,也可以产生事件给其他对象。
- 有软件可用来配置Bean。
- Bean的属性可以被序列化,以供日后重用。
规范
要成为 JavaBean 类别,则必需遵循关于命名、构造器、方法的特定规范。有了这些规范,才能有可以使用、复用、替代和连接 JavaBeans 的工具。
规范如下:
- 变量私有化。
- 有一个public的无参数构造函数。
- 属性可以透过get、set、is(可替代get,用在布尔型属性上)方法或遵循特定命名规则的其他方法访问。
- 可序列化。
package player;
public class PersonBean implements java.io.Serializable {
/**
* name 属性(注意大小写)
*/
private String name = null;
private boolean deceased = false;
/** 无参构造器 */
public PersonBean() {
}
/**
* name 属性的 Getter方法
*/
public String getName() {
return name;
}
/**
* name 属性的Setter方法
* @param value
*/
public void setName(final String value) {
name = value;
}
/**
* deceased 属性的Getter方法
* 布林值属性Getter方法的不同形式(这里使用了is而非get)
*/
public boolean isDeceased() {
return deceased;
}
/**
* deceased 属性的Setter方法
* @param value
*/
public void setDeceased(final boolean value) {
deceased = value;
}
}
ObjectOutputStream 对象的序列化
- 构造方法 ObjectOutputStream(OutputStream out) 传递字节输出流;
- 方法 void writeObject(Object o) 写入对象;
示例如下:
package top.sharehome.Demo;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Demo {
public static void main(String[] args) {
Person p = new Person("张三",21);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream("d:\\大学课程学习文档\\java\\Practice\\IO\\test.txt");
oos = new ObjectOutputStream(fos);
oos.writeObject(p);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Person implements Serializable {
//这里需要加一串序列号,其作用后面内容有介绍;
static final long serialVersionUID = 42L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
打印效果如下:
ObjectInputStream 对象的反序列化
- 构造方法 ObjectInputStream(InputStream in) 传递字节输入流;
- 方法 Object readObject() 读取对象,由于得到的是 Object 对象,所以读取到对象后还需要进行对象的强制转换;
示例如下:
package top.sharehome.Demo;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Demo {
public static void main(String[] args) {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("d:\\大学课程学习文档\\java\\Practice\\IO\\test.txt");
ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
System.out.println(obj.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Person implements Serializable {
//这里需要加一串序列号,其作用后面内容有介绍;
static final long serialVersionUID = 42L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
打印效果如下:
不能序列化的情况
静态属性:
类中的静态成员也成为实例实例,实例成员是属于类本身的,随着类的加载而加载,而我们创建的是对象,做的是对象的序列化,而实例成员并不属于我们所创建的对象,所以静态属性不会被序列化,写入文件的结果是数据类型的默认值;
transient 关键字修饰:
transient 关键字只有一个作用,那就是阻止变量的值被序列化写入文件中;
示例如下:
package top.sharehome.Demo;
import java.io.*;
public class Demo {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
Person p = new Person("李四",20);
try {
fos = new FileOutputStream("d:\\大学课程学习文档\\java\\Practice\\IO\\test.txt");
fis = new FileInputStream("d:\\大学课程学习文档\\java\\Practice\\IO\\test.txt");
oos = new ObjectOutputStream(fos);
ois = new ObjectInputStream(fis);
oos.writeObject(p);
Object obj = ois.readObject();
System.out.println(obj.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Person implements Serializable {
//这里需要加一串序列号,其作用后面内容有介绍;
static final long serialVersionUID = 42L;
private String name;
private transient int age; //将年龄防序列化
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
打印效果如下:
静态变量和此种情况的打印效果一模一样;
序列号
固定格式:
static final long serialVersionUID = 42L; //赋值部分可自行修改,即42L可修改;
当我们需要把一个类序列化时,不仅需要让这个类实现 Serializable 接口,还需要在类的成员变量中加入上述格式的序列号;
**序列号目的:**防止写入的文件被篡改,此时序列号就成了这个类的唯一标识;