JAVA中序列化和反序列化中的静态成员问题

midoll 425 2022-10-10

JAVA中序列化和反序列化中的静态成员问题

JAVA中的序列化和反序列化主要用于:

  1. 将对象或者异常等写入文件,通过文件交互传输信息;
  2. 将对象或者异常等通过网络进行传输。

那么为什么需要序列化和反序列化呢?简单来说,如果你只是自己同一台机器的同一个环境下使用同一个JVM来操作,序列化和反序列化是没必要的,当需要进行数据传输的时候就显得十分必要。比如你的数据写到文件里要被其他人的电脑的程序使用,或者你电脑上的数据需要通过网络传输给其他人的程序使用,像服务器客户端的这种模型就是一种应用,这个时候,大家想想,每个人的电脑配置可能不同,运行环境可能也不同,字节序可能也不同,总之很多地方都不能保证一致,所以为了统一起见,我们传输的数据或者经过文件保存的数据需要经过序列化和编码等操作,相当于交互双方有一个公共的标准,按照这种标准来做,不管各自的环境是否有差异,各自都可以根据这种标准来翻译出自己能理解的正确的数据。

在JAVA中有专门用于此类操作的API,供开发者直接使用,对象的序列化和反序列化可以通过将对象实现Serializable接口,然后用对象的输入输出流进行读写

demo


package test2;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
 
/**
 * Description: 测试对象的序列化
 */
public class SerializeDataobject {
 
    public static void main(String[] args) throws Exception {
    	
    	// 序列化DataObject对象
        Serialize();
        
    }
    
    /**
     * MethodName: SerializePerson 
     * Description: 序列化Person对象
     * @author 
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static void Serialize() throws FileNotFoundException, IOException {
    	
    	DataObject object = new DataObject();
    	object.setWord("123");
    	object.setI(2);
    	
        // 创建ObjectOutputStream对象输出流,其中用到了文件的描述符对象和文件输出流对象
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("DataObject.txt")));
        
        // 将DataObject对象存储到DataObject.txt文件中,完成对DataObject对象的序列化操作
        oo.writeObject(object);
        
        System.out.println("Person对象序列化成功!");
        
        // 最后一定记得关闭对象描述符!!!
        oo.close();
    }
}

上面这个类只用来进行序列化,对象被序列化后保存在文件"DataObject.txt"中,然后程序运行结束,JVM退出。接下来看另一段程序。


package test2;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
 
/**
 * Description: 测试对象的反序列
 */
public class DeserializeDataobject {
 
    public static void main(String[] args) throws Exception {
        
        // 反序列DataObject对象
        DataObject object = Deserialize();
        
        // 静态成员属于类级别的,所以不能序列化,序列化只是序列化了对象而已,
        // 这里的不能序列化的意思,是序列化信息中不包含这个静态成员域,下面
        // 之所以i输出还是2,是因为测试都在同一个机器(而且是同一个进程),因为这个jvm
        // 已经把i加载进来了,所以获取的是加载好的i,如果是传到另一台机器或者关掉程序重新
        // 写个程序读入DataObject.txt,此时因为别的机器或新的进程是重新加载i的,所以i信息就是初始时的信息,即0
        System.out.println(object);
    }
 
    /**
     * MethodName: DeserializePerson 
     * Description: 反序列DataObject对象
     * @author 
     * @return
     * @throws Exception
     * @throws IOException
     */
    private static DataObject Deserialize() throws Exception, IOException {
    	
    	// 创建ObjectInputStream对象输入流,其中用到了文件的描述符对象和文件输入流对象 
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("DataObject.txt")));
        
        // 从DataObject.txt文件中读取DataObject对象,完成对DataObject对象的反序列化操作
        DataObject object = (DataObject) ois.readObject();
        System.out.println("Person对象反序列化成功!");
        
        // 最后一定记得关闭对象描述符!!!
        ois.close();
        
        return object;
    }
 
}

上面这段程序用来实现对象的反序列化,它从文件"DataObject.txt"中读出对象的相关信息,然后进行了反序列化,最终输出对象中word和i的值,这个程序输出的结果才是word = “123”, i = 0 这个才是正确的结果,这是因为序列化和反序列化都有自己的main方法,先序列化,然后JVM退出,再次运行反序列化,JVM重新加载DataObject类,此时i = 0,"DataObject.txt"文件中其实是没有i的信息的,只有word的信息。这里通过先后执行序列化和反序列化,让JVM得到一次重新加载类的机会,模拟了两个JVM下运行的结果。

结论:

  • 静态成员属于类级别的,所以不能序列化,序列化只是序列化了对象而已,
  • 这里的不能序列化的意思,是序列化信息中不包含这个静态成员域
  • 如果测试都在同一个机器(而且是同一个进程),那么,静态成员也是会初始化进来,因为这个jvm已经把静态变量加载进来了,所以获取的是加载好的,如果是传到另一台机器或者关掉程序重新写个程序反序列化,此时因为别的机器或新的进程是重新加载静态变量的,所以静态变量就是初始时的信息