Serializable序列化和Externalizable序列化与反序列化的使用

1、序列化是干啥用的?

序列化的原本意图是希望对一个Java对象作一下“变换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外变换成字节序列也更便于网络运输和传播,所以概念上很好理解:

  • 序列化:把Java对象转换为字节序列。
  • 反序列化:把字节序列恢复为原先的Java对象。而且序列化机制从某种意义上来说也弥补了平台化的一些差异,毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象。

2、对象序列化的方式?

在Java中,如果一个对象要想实现序列化,必须要实现下面两个接口之一:

  • Serializable 接口
  • Externalizable 接口
  • 那这两个接口是如何工作的呢?两者又有什么关系呢?我们分别进行介绍。

2.1 Serializable接口

一个对象想要被序列化,那么它的类就要实现此接口或者它的子接口。

这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。不想序列化的字段可以使用transient修饰。

由于Serializable对象完全以它存储的二进制位为基础来构造,因此并不会调用任何构造函数,因此Serializable类无需默认构造函数,但是当Serializable类的父类没有实现Serializable接口时,反序列化过程会调用父类的默认构造函数,因此该父类必需有默认构造函数,否则会抛异常。

使用transient关键字阻止序列化虽然简单方便,但被它修饰的属性被完全隔离在序列化机制之外,导致了在反序列化时无法获取该属性的值,而通过在需要序列化的对象的Java类里加入writeObject()方法与readObject()方法可以控制如何序列化各属性,甚至完全不序列化某些属性或者加密序列化某些属性。

2.2 Externalizable 接口

它是Serializable接口的子类,用户要实现的writeExternal()和readExternal() 方法,用来决定如何序列化和反序列化。

因为序列化和反序列化方法需要自己实现,因此可以指定序列化哪些属性,而transient在这里无效。

对Externalizable对象反序列化时,会先调用类的无参构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor异常,因此Externalizable对象必须有默认构造函数,而且必需是public的。

2.3 对比

使用时,你只想隐藏一个属性,比如用户对象user的密码pwd,如果使用Externalizable,并除了pwd之外的每个属性都写在writeExternal()方法里,这样显得麻烦,可以使用Serializable接口,并在要隐藏的属性pwd前面加上transient就可以实现了。如果要定义很多的特殊处理,就可以使用Externalizable。
当然这里我们有一些疑惑,Serializable 中的writeObject()方法与readObject()方法科可以实现自定义序列化,而Externalizable 中的writeExternal()和readExternal() 方法也可以,他们有什么异同呢?

  • readExternal(),writeExternal()两个方法,这两个方法除了方法签名和readObject(),writeObject()两个方法的方法签名不同之外,其方法体完全一样。
  • 需要指出的是,当使用Externalizable机制反序列化该对象时,程序会使用public的无参构造器创建实例,然后才执行readExternal()方法进行反序列化,因此实现Externalizable的序列化类必须提供public的无参构造。
  • 虽然实现Externalizable接口能带来一定的性能提升,但由于实现ExternaLizable接口导致了编程复杂度的增加,所以大部分时候都是采用实现Serializable接口方式来实现序列化。

3、Serializable 如何序列化对象?

3.1 Serializable演示

然而Java目前并没有一个关键字可以直接去定义一个所谓的“可持久化”对象。对象的持久化和反持久化需要靠程序员在代码里手动显式地进行序列化和反序列化还原的动作。举个例子,假如我们要对Student类对象序列化到一个名为student.txt的文本文件中,然后再通过文本文件反序列化成Student类对象:

1、Student类定义

public class Student implements Serializable {private String name;private Integer age;private Integer score;@Overridepublic String toString() {return "Student:" + '\n' +"name = " + this.name + '\n' +"age = " + this.age + '\n' +"score = " + this.score + '\n';}// ... 其他省略 ...
}

2、序列化

public static void serialize(  ) throws IOException {Student student = new Student();student.setName("CodeSheep");student.setAge( 18 );student.setScore( 1000 );ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) );objectOutputStream.writeObject( student );objectOutputStream.close();System.out.println("序列化成功!已经生成student.txt文件");System.out.println("==============================================");
}

3、反序列化

public static void deserialize(  ) throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream( new FileInputStream( new File("student.txt") ) );Student student = (Student) objectInputStream.readObject();objectInputStream.close();System.out.println("反序列化结果为:");System.out.println( student );
}

4、运行结果
控制台打印:

序列化成功!已经生成student.txt文件
==============================================
反序列化结果为:
Student:
name = CodeSheep
age = 18
score = 1000

3.2 Serializable接口有何用?

上面在定义Student类时,实现了一个Serializable接口,然而当我们点进Serializable接口内部查看,发现它竟然是一个空接口,并没有包含任何方法!

试想,如果上面在定义Student类时忘了加implements Serializable时会发生什么呢?

实验结果是:此时的程序运行会报错,并抛出NotSerializableException异常:
在这里插入图片描述
我们按照错误提示,由源码一直跟到ObjectOutputStream的writeObject0()方法底层一看,才恍然大悟:

在这里插入图片描述
如果一个对象既不是字符串、数组、枚举,而且也没有实现Serializable接口的话,在序列化时就会抛出NotSerializableException异常
!原来Serializable接口也仅仅只是做一个标记用!!!它告诉代码只要是实现了Serializable接口的类都是可以被序列化的!然而真正的序列化动作不需要靠它完成。

3.3 serialVersionUID号有何用?

相信你一定经常看到有些类中定义了如下代码行,即定义了一个名为serialVersionUID的字段:

private static final long serialVersionUID = -4392658638228508589L;

你知道这句声明的含义吗?

为什么要搞一个名为serialVersionUID的序列号?

继续来做一个简单实验,还拿上面的Student类为例,我们并没有人为在里面显式地声明一个serialVersionUID字段。
我们首先还是调用上面的serialize()方法,将一个Student对象序列化到本地磁盘上的student.txt文件:接下来我们在Student类里面动点手脚,比如在里面再增加一个名为id的字段,表示学生学号:

public class Student implements Serializable {private String name;private Integer age;private Integer score;private Integer id;

这时候,我们拿刚才已经序列化到本地的student.txt文件,还用如下代码进行反序列化,试图还原出刚才那个Student对象:

运行发现报错了,并且抛出了InvalidClassException异常
在这里插入图片描述

这地方提示的信息非常明确了:序列化前后的serialVersionUID号码不兼容!

从这地方最起码可以得出两个重要信息:

1、serialVersionUID是序列化前后的唯一标识符

2、默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!

  • 第1个问题: serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的“暗号”,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。
  • 第2个问题: 如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!

所以,为了serialVersionUID的确定性,写代码时还是建议,凡是implements Serializable的类,都最好人为显式地为它声明一个serialVersionUID明确值!

当然,如果不想手动赋值,你也可以借助IDE的自动添加功能,比如我使用的IntelliJ IDEA,按alt + enter就可以为类自动生成和添加serialVersionUID字段,十分方便:

两种特殊情况

1、凡是被static修饰的字段是不会被序列化的

2、凡是被transient修饰符修饰的字段也是不会被序列化的

对于第一点,因为序列化保存的是对象的状态而非类的状态,所以会忽略static静态域也是理所应当的。

对于第二点,就需要了解一下transient修饰符的作用了。

如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如这个字段存放的是隐私值,如:密码等),那这时就可以用transient修饰符来修饰该字段。

比如在之前定义的Student类中,加入一个密码字段,但是不希望序列化到txt文本,则可以:

public class Student implements Serializable {private static final long serialVersionUID = -4392658638228508589L;private transient String name;private Integer age;private Integer score;private transient String passwd;

这样在序列化Student类对象时,password字段会设置为默认值null,这一点可以从反序列化所得到的结果来看出:

public static void serialize() throws IOException {Student student = new Student();student.setName("CodeSheep");student.setAge(18);student.setScore(1000);student.setPasswd("123");

4、实现Externalizable

public UserInfo() {userAge=20;//这个是在第二次测试使用,判断反序列化是否通过构造器
}
public void writeExternal(ObjectOutput out) throws IOException  {//  指定序列化时候写入的属性。这里仍然不写入年龄out.writeObject(userName);out.writeObject(usePass);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException  {// 指定反序列化的时候读取属性的顺序以及读取的属性// 如果你写反了属性读取的顺序,你可以发现反序列化的读取的对象的指定的属性值也会与你写的读取方式一一对应。因为在文件中装载对象是有序的userName=(String) in.readObject();usePass=(String) in.readObject();
}

我们在序列化对象的时候,由于这个类实现了Externalizable 接口,在writeExternal()方法里定义了哪些属性可以序列化,哪些不可以序列化,所以,对象在经过这里就把规定能被序列化的序列化保存文件,不能序列化的不处理,然后在反序列的时候自动调用readExternal()方法,根据序列顺序挨个读取进行反序列,并自动封装成对象返回,然后在测试类接收,就完成了反序列。

Externalizable 实例类的唯一特性是可以被写入序列化流中,该类负责保存和恢复实例内容。 若某个要完全控制某一对象及其超类型的流格式和内容,则它要实现 Externalizable 接口的 writeExternal 和 readExternal 方法。这些方法必须显式与超类型进行协调以保存其状态。这些方法将代替定制的 writeObject 和 readObject 方法实现。

  • writeExternal(ObjectOutput out)
    该对象可实现 writeExternal 方法来保存其内容,它可以通过调用 DataOutput 的方法来保存其基本值,或调用 ObjectOutput 的 writeObject 方法来保存对象、字符串和数组。
  • readExternal(ObjectInput in)
    对象实现 readExternal 方法来恢复其内容,它通过调用 DataInput 的方法来恢复其基础类型,调用 readObject 来恢复对象、字符串和数组。

externalizable和Serializable的区别:

1、实现serializable接口是默认序列化所有属性,如果有不需要序列化的属性使用transient修饰。externalizable接口是serializable的子类,实现这个接口需要重写writeExternal和readExternal方法,指定对象序列化的属性和从序列化文件中读取对象属性的行为。

2、实现serializable接口的对象序列化文件进行反序列化不走构造方法,载入的是该类对象的一个持久化状态,再将这个状态赋值给该类的另一个变量。实现externalizable接口的对象序列化文件进行反序列化先走构造方法得到控对象,然后调用readExternal方法读取序列化文件中的内容给对应的属性赋值。

5、序列化的受控和加强

5.1 约束性加持

从上面的过程可以看出,序列化和反序列化的过程其实是有漏洞的,因为从序列化到反序列化是有中间过程的,如果被别人拿到了中间字节流,然后加以伪造或者篡改,那反序列化出来的对象就会有一定风险了。
毕竟反序列化也相当于一种 “隐式的”对象构造 ,因此我们希望在反序列化时,进行受控的对象反序列化动作。

那怎么个受控法呢?答案就是: 自行编写readObject()函数,用于对象的反序列化构造,从而提供约束性。

既然自行编写readObject()函数,那就可以做很多可控的事情:比如各种判断工作。

还以上面的Student类为例,一般来说学生的成绩应该在0 ~ 100之间,我们为了防止学生的考试成绩在反序列化时被别人篡改成一个奇葩值,我们可以自行编写readObject()函数用于反序列化的控制:

private void readObject( ObjectInputStream objectInputStream ) throws IOException, ClassNotFoundException {// 调用默认的反序列化函数objectInputStream.defaultReadObject();// 手工检查反序列化后学生成绩的有效性,若发现有问题,即终止操作!if( 0 > score || 100 < score ) {throw new IllegalArgumentException("学生分数只能在0到100之间!");}
}

比如我故意将学生的分数改为101,此时反序列化立马终止并且报错:
在这里插入图片描述

对于上面的代码,为什么自定义的private的readObject()方法可以被自动调用,跟一下底层源码来一探究竟,跟到了ObjectStreamClass类的最底层,是反射机制在起作用!是的,在Java里,果然万物皆可“反射”(滑稽),即使是类中定义的private私有方法,也能被抠出来执行了,简直引起舒适了。

5.2 单例模式增强一个容易被忽略的问题是:可序列化的单例类有可能并不单例!举个代码小例子就清楚了。比如这里我们先用java写一个常见的「静态内部类」方式的单例模式实现:public class Singleton implements Serializable {

    private static final long serialVersionUID = -1576643344804979563L;private Singleton() {}private static class SingletonHolder {private static final Singleton singleton = new Singleton();}public static synchronized Singleton getSingleton() {return SingletonHolder.singleton;}
}

然后写一个验证主函数:

public class Test2 {public static void main(String[] args) throws IOException, ClassNotFoundException {ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream( new File("singleton.txt") ));// 将单例对象先序列化到文本文件singleton.txt中objectOutputStream.writeObject( Singleton.getSingleton() );objectOutputStream.close();ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream( new File("singleton.txt") ));// 将文本文件singleton.txt中的对象反序列化为singleton1Singleton singleton1 = (Singleton) objectInputStream.readObject();objectInputStream.close();Singleton singleton2 = Singleton.getSingleton();// 运行结果竟打印 false !System.out.println( singleton1 == singleton2 );}}

运行后我们发现:反序列化后的单例对象和原单例对象并不相等了,这无疑没有达到我们的目标。解决办法是:在单例类中手写readResolve()函数,直接返回单例对象:

private Object readResolve() {return SingletonHolder.singleton;
}
package serialize.test;import java.io.Serializable;public class Singleton implements Serializable {private static final long serialVersionUID = -1576643344804979563L;private Singleton() {}private static class SingletonHolder {private static final Singleton singleton = new Singleton();}public static synchronized Singleton getSingleton() {return SingletonHolder.singleton;}private Object readResolve() {return SingletonHolder.singleton;}
}

这样一来,当反序列化从流中读取对象时,readResolve()会被调用,用其中返回的对象替代反序列化新建的对象。


利用Serializable和Externalizable序列化与反序列化的使用

1、Serializable序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认构造器的,Serializable不需要自己实现序列化方法可以使用默认的序列化方法,而Externalizable需要自己实现序列化!!!
2、Serializable:一个对象想要被序列化,那么它的类就要实现 此接口,这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。
3.如是要一个类是可序列化的,那么它的子类也是可序列化的。
Externalizable:他是Serializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性。序列化就是将对象存进文件,反序列化就是将对象从文件中读取出来。
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
注意:
对象的序列化并不属于新的Reader和Writer层次结构的一部分,而是沿用老式的InputStream和OutputStream结构,在某些情况下,不得不混合使用两种类型的层次结构。

恢复了一个反序列化的对象后,如果想对其做更多的事情(对象.getClass().xxx),必须保证JVM能在本地类路径或者因特网的其他什么地方找到相关的.class文件。
恢复对象的默认构建器必须是public的,否则会抛异常。
由于Externalizable对象默认时不保存对象的任何字段,所以transient关键字只能伴随Serializable使用,虽然Externalizable对象中使用transient关键字也不报错,但不起任何作用

方法writeObject处理对象的序列化。如果声明该方法,它将会被ObjectOutputStream调用而不是默认的序列化进程。如果你是第一次看见它,你会很惊奇尽管它们被外部类调用但事实上这是两个private的方法。并且它们既不存在于java.lang.Object,也没有在Serializable中声明。那么ObjectOutputStream如何使用它们的呢?这个吗,ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod(通过getDeclareMethod可以获得私有方法),所以这些方法不得不被声明为private以至于供ObjectOutputStream来使用。
调用了defaultWriteObject()和defaultReadObject()。它们做的是默认的序列化进程,就像写/读所有的non-transient和 non-static字段(但他们不会去做serialVersionUID的检查).通常说来,所有我们想要自己处理的字段都应该声明为transient。这样的话,defaultWriteObject/defaultReadObject便可以专注于其余字段,而我们则可为这些特定的字段
serialVersionUID的作用:
序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
具体的序列化过程是这样的:序列化操作的时候系统会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会去检测文件中的serialVersionUID,判断它是否与当前类的serialVersionUID一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。
有两种生成方式:
一个是默认的1L,比如:private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = xxxxL; 例如当你的类Serialized存到硬盘上面后,可是后来你却更改了类别的field(增加或减少或改名),当你Deserialize时,就会出现Exception的,这样就会造成不兼容性的问题。但当serialVersionUID相同时,它就会将不一样的field以type的预设值Deserialize,可避开不兼容性问题。
案例一:

 public class SkillMode implements Externalizable {private static final long serialVersionUID = 1L;private Logger logger = LoggerFactory.getLogger(SkillMode.class);private Map<Integer,DmDialogDefinition> allSkill;private String version;private String versionMessage;@Overridepublic void writeExternal(ObjectOutput out) throws IOException {
​       out.writeObject(version);
​       out.writeObject(versionMessage);
​       out.writeObject(allSkill); //其他对象都是按照这样的格式读进去}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
​       version = (String) in.readObject();
​       versionMessage = (String) in.readObject();
​       allSkill = (Map<Integer, DmDialogDefinition>) in.readObject();//其他对象都是按照这样的格式读出来}public Map<Integer, DmDialogDefinition> getAllSkill() {return allSkill;}public void setAllSkill(Map<Integer, DmDialogDefinition> allSkill) {this.allSkill = allSkill;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getVersionMessage() {return versionMessage;}public void setVersionMessage(String versionMessage) {this.versionMessage = versionMessage;}public void load(File file) throws IOException { // 从文件中读出至类中
​       logger.debug("File.load path:{}",file.getAbsolutePath());ObjectInputStream ios = null;InputStream in = null;try {
​         in = new FileInputStream(file);
​         ios = new ObjectInputStream(new BufferedInputStream(in));
​         in = null;this.readExternal(ios);} catch (ClassNotFoundException e) {
​         e.printStackTrace();throw new IOException(e);} finally {saveClose(ios);saveClose(in);}}public void save(File file) throws IOException {ObjectOutputStream oos = null;OutputStream out = null;try {
​         out = new FileOutputStream(file);
​         oos = new ObjectOutputStream(new BufferedOutputStream(out));
​         out = null;this.writeExternal(oos); // 保存对象到文件}  finally {saveClose(oos);saveClose(out);}}private void saveClose(AutoCloseable c){if (c != null){try {
​           c.close();} catch (Exception e) {
​           e.printStackTrace();}}}}
​     用法:
​     1.从文件中读取出来:
​     SkillModel  skillModel = new SkillModel();File file=new File("path") //path为存取的文件路径if(file.exists()){
​      skillModel.load(File);//将从文件中读取出来的值传给定义的skillModel实体}
​     
​     skillModel.save(file);//将skillModel实体存到文件中



​ 案例二:

public class Person implements Serializable {private static final long serialVersionUID = 123456789L;public int id;public String name;public transient String age; //不会被序列化public Person(int id, String name) {this.id = id;this.name = name;}public String toString() {return "Person: " + id + " " + name;}}序列化功能:public class SerialTest {public static void main(String[] args) throws IOException {Person person = new Person(1234, "wang");System.out.println("Person Serial" + person);FileOutputStream fos = new FileOutputStream("Person.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);
​       oos.writeObject(person);
​       oos.flush();
​       oos.close();}}反序列化功能:public class DeserialTest {public static void main(String[] args) throws IOException, ClassNotFoundException {Person person;//在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,String的值是"" ,对象型的是 nullFileInputStream fis = new FileInputStream("Person.txt");ObjectInputStream ois = new ObjectInputStream(fis);
​    person = (Person) ois.readObject();
​    ois.close();System.out.println("Person Deserial" + person);}}

一个序列化的类要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就需要有默认的无参的构造函数。在父类没有实现 Serializable接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。
如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

根据父类对象序列化的规则,我们可以将不需要被序列化的字段抽取出来放到父类中,子类实现 Serializable 接口,父类不实现,根据父类序列化规则去父类无参构造方法中初始化。

用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。实现Externalizable接口的类必须要提供一个无参的构造器,且访问权限为 public。

dubbo利用RPC调用时需要将对象序列化之后才能调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win10系统如何查看电脑是否是UEFI启动模式

Win10系统如何查看电脑是否是UEFI启动模式?现在越来越多的新电脑都采用UEFI来引导电脑系统&#xff0c;UEFI提高了开机后操作系统的启动速度&#xff0c;使电脑更加流畅&#xff0c;安全性更强&#xff0c;而传统的BIOS则没有UEFI用起来那么好。如何查看自己Win10系统电脑是否…

win10如何关闭Windows Defender安全保护程序

win10如何关闭Windows Defender安全保护程序&#xff1f;win10如何关闭Windows Defender安全保护程序?在安装的windows操作系统中&#xff0c;自带着安全保护程序“Windows Defender应用”&#xff0c;其实主要是为了保护好其他运行的软件&#xff0c;那该怎么来关闭这个软件呢…

2020-12-11

bios怎么恢复出厂设置? 1 第一步 打开电脑 在电脑出现开机提示时按下热键(一般是del或F2)就可以进入到bios设置界面 2 第二步 在bios设置界面中找到并选择“Load Optimized Default”&#xff0c; 然后按下回车键 3 第三步 在弹出窗口中选择“OK“ 然后回车键确定即可 …

JAVA序列化和反序列化的底层实现原理解析 更新时间:2019年11月27日 14:38:39 作者:myseries 这篇文章主要介绍了JAVA序列化和反序列化的底层实现原理解析,文中通

一、基本概念 1、什么是序列化和反序列化 &#xff08;1&#xff09;Java序列化是指把Java对象转换为字节序列的过程&#xff0c;而Java反序列化是指把字节序列恢复为Java对象的过程&#xff1b; &#xff08;2&#xff09;序列化&#xff1a;对象序列化的最主要的用处就是在…

笔记本电脑重启快捷键是什么

品牌型号&#xff1a;联想小新Air-14 2020 系统&#xff1a;win10 1909 64位企业版 部分用户可能电脑型号不一样&#xff0c;但系统版本一致都适合该方法。 用电脑用久了的朋友们都知道电脑的快捷键在哪里&#xff0c;笔记本电脑重启快捷键是什么?下面就给大家分享一下笔记…

新手iso系统怎么安装 新手安装iso镜像文件详细步骤

操作系统大多是iso格式的文件&#xff0c;尤其是Windows系统&#xff0c;都是.iso镜像&#xff0c;如果电脑可以正常使用&#xff0c;iso镜像文件只需解压出来就可以安装&#xff0c;但是对于新手来说&#xff0c;可能解压iso系统都是个问题&#xff0c;那么新手iso系统怎么安装…

Java获取一天的起止时间(Java8写法使用 LocalDateTime)

Calendar calendar new GregorianCalendar();// 0可以修改&#xff0c;0代表当前时间&#xff0c;-1代表前一天calendar.add(Calendar.DAY_OF_MONTH,0);//一天的开始时间 yyyy:MM:dd 00:00:00calendar.set(Calendar.HOUR_OF_DAY,0);calendar.set(Calendar.MINUTE,0);calend…

Windows10系统以太网显示“未识别的网络”如何解决

如果你的Win10系统电脑连不上网&#xff0c;那么可能会是三种情况导致的&#xff1a;受限未识别网络、拨号不成功或者网线没插好。其中&#xff0c;本地连接(以太网)提示“未识别的网络”情况是我们最经常碰到的。如果你的Win10系统无法访问网络&#xff0c;且在有线连接路由器…

如何在bios里调节风扇转速 bios设置风扇转速的方法

CPU的温度对电脑的正常运行有很大的影响&#xff0c;如果温度过高那么电脑就不能正常工作&#xff0c;风扇在降低温度中发挥着很大的作用&#xff0c;那么如何在bios里调节风扇转速呢?下面就给大家分享bios设置风扇转速的方法。 1、启动电脑按快捷键进入BIOS里面&#xff0c;…

win7开机黑屏怎么解决?win7开机黑屏出现reboot and select的解决方法

很多用户有遇见电脑黑屏的情况&#xff0c;而且还出现 reboot and select proper boot deviceor insert boot media in selected boot device 的提示&#xff0c;win7开机黑屏怎么解决呢?可能是硬盘接口数据线出现松动了&#xff0c;也可能是硬盘坏了。今天小编整理了相关的教…

win10系统进入监护人模式的方法

现在很多的小学生也会一直玩电脑很多家长就担心影响孩子学习因此想要设置监护人模式但是不知道win10系统监护人模式怎么进入&#xff0c;今天就给你们带来了win10系统进入监护人模式的方法&#xff0c;快来看看吧。 win10系统进入监护人模式的方法&#xff1a; 1、首先点击左…

解决win10电脑声音太小的问题

右击任务栏声音图标&#xff0c;点击选择菜单中的“播放设备”。 如果声音图标未显示&#xff0c;请在控制面板(大图标查看方式)中&#xff0c;点击选择“声音”。 在声音对话框&#xff0c;点击选中扬声器&#xff0c;然后点击下面的“属性”。 在扬声器 属性对话框&#xff0…

win7 ie临时文件夹怎么修改存放位置

首先&#xff0c;鼠标左键双击打开Internet Explorer,然后点击右上角的设置图标,点Internet选项。 打开之后&#xff0c;找到并点击“设置” 打开之后&#xff0c;找到并点击“移动文件夹” 然后找一个存放IE临时文件的位置,设好了之后点确定。 要让IE临时文件的移动生效,要对移…

笔记本蓝牙怎么打开

笔记本蓝牙有什么用呢?我们可以用蓝牙耳机连接上笔记本的蓝牙&#xff0c;然后就可以听音乐&#xff0c;看电影等操作。所以接下来就来给小伙伴说说怎么打开笔记本蓝牙。 以win8为例进行演示 1、首先&#xff0c;把鼠标拉倒最右下角&#xff0c;然后点设置。 2、然后&#x…

Java8时间间隔计算Period.between与LocalDate.until区别LocalDate,LocalDateTime计算两个时间的相差时间

记录一线上问题,编辑会员卡信息,有效期截止时间由2022-04-17修改为2023-04-18,操作成功之后显示剩余的有效天数为24天,实际上怎么也得有三百多天.之前本地测试一直没有问题,现将定位过程记录一下. 本地复测之后发现计算的是24天,果然有问题. Period period Period.between(Loc…

win8升级win10出现错误代码1049怎么回事

最近有用户把自己的win8系统升级到win10后遇到了错误代码1049&#xff0c;那么这是咋回事呢?下面小编就给大家介绍一下win8升级win10出现错误代码1049的解决方法。 1、报错信息如下所示&#xff0c;完全不理解是什么意思&#xff0c;点击《转到存储》菜单&#xff0c;看到的是…

简单几步解决笔记本风扇一直嗡嗡响的问题

我们在使用电脑时难免会遇到一些问题。例如我们都知道笔记本都会自带有一个风扇&#xff0c;如果长时间使用的话就会帮助扇热。但最近有网友就表示自己的笔记本风扇一直嗡嗡响&#xff0c;不知道怎么回事&#xff0c;也不知道该如何解决很是苦恼&#xff0c;所以今天小编为大家…

acer笔记本电脑如何进入bios设置

acer笔记本电脑对于外出办公的用户来说是一个不错的选择&#xff0c;但最近小编发现有许多使用acer笔记本电脑的朋友是不知道怎么进入BIOS设置的。因此&#xff0c;针对这个问题我就赶紧整理了这个acer笔记本电脑进入BIOS设置的教程&#xff0c;现在就来分享给大家&#xff0c;…

[内排序]八大经典排序合集

文章目录1 排序的基本概念1.1 什么是排序1.2 排序的稳定性1.3 内排序和外排序2 插入排序2.1 直接插入排序1. 排序思路2. 直接插入排序实例3. 排序算法4. 算法分析5. 折半插入排序 / 二分插入排序5.1 排序思路5.2 排序算法5.3 算法分析2.2 希尔排序1. 排序思路2. 希尔排序实例3.…

怎么解决机械革命笔记本蓝屏问题

1、重启电脑&#xff0c;出现logo界面后连续按F2键直到打开bios&#xff0c;接着切换至“Advanced”&#xff0c;将光标移至“OS Support”选项&#xff0c;并将后面数值调成“Others”。 2、接下来将光标移至“SATA Device”选项&#xff0c;将硬盘模式调成“AHCI”。 3、最后…