static和transient变量序列化问题

本文通过具体示例探讨Java中序列化机制的工作原理,特别是针对static和transient变量的行为进行了详细解释。文章揭示了static变量如何在不同序列化过程中保持一致性,以及transient变量为何在序列化后丢失。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看下面的代码:
1.Person.java

import java.io.*;
class Person implements Serializable{   public String name;
   public Person(String name){
       this.name=name;
       System.out.println("构建一个Person对象");
   }
}

2.DataObject.java

import java.io.*; 
public class DataObject implements Serializable 
{ 
 public static Person p=new Person("hello");//静态的
 public        Person p2=new Person("world");//非静态的
 
 public String toString() { 
  return p.name+":"+p2.name; 
 } 
 public static void main(String[] args) throws ClassNotFoundException,FileNotFoundException,IOException 
 {  
  DataObject object=new DataObject(); 
  object.p.name="why";
  System.out.println("序列化以前:"+object); //序列化以前:why:world
  
  ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("lql.out")); 
  out.writeObject(object);
  ObjectInputStream in=new ObjectInputStream(new FileInputStream("lql.out")); 
  DataObject object2=(DataObject)in.readObject(); 
  
  System.out.println("序列化以后:"+object2); //序列化以后:why:world } 
} 


3.运行以后发现上面的输出结果,我们说static变量不能被序列化,但是现在为什么我们看到的object和object2的状态却是连续的呢?
4.我们在最后添加上一些代码来继续这个问题:

System.out.println("object==object2?"+(object==object2));    //false
System.out.println("object.p==object2.p?"+(object.p==object2.p));//true
System.out.println("object.p2==object2.p2?"+(object.p2==object2.p2));//false


看到什么了?
第一句:序列化以前的和序列化以后的不是同一个对象!这不是废话嘛,我们都知道!
第二句:竟然输出了true!
第三句:输出false,本来就应该是false才对嘛。看出点什么?难道object.p和object2.p本来就是同一个对象实例?可不是嘛!同一个JVM内部只会存在静态变量的一个实例,当然他们是一样的了。也就是说,object.p并没有被序列化,而只是存在于JVM内部,当反序列化的时候,JVM用的还是这一个实例,难怪他们会是一样的了!
其实第二句完全等价于:System.out.println(DataObject.p==DataObject.p);//true,这样就更好理解了!并且就算Person类没有实现Serializable接口,如果是静态的,照样能读出来,但是却不是序列化的原因!
5. 你也许要问了,如果JVM中没有这个静态实例会是怎么样的呢?下面我们就来看一看:
import java.io.*;
public class SerTest{
public static void main(String[] args) throws ClassNotFoundException,FileNotFoundException,IOException 
 { 
  ObjectInputStream in=new ObjectInputStream(new FileInputStream("lql.out")); 
  DataObject object2=(DataObject)in.readObject(); 
  System.out.println(object2); 
 } 
}


/*
打印输出:
构建一个Person对象
hello:world
*/
这回我们看得很清楚了,因为这个新启动的JVM内部并没有事先已经存在一个Person对象实例,所以当反序列化的时候,由于反序列化构建一个对象属于对类的主动使用,所以会对类进行初始化,对类的静态变量进行赋值,所以我们看到的是只是经过初始化以后的对象实例,而不是序列化以前的那个实例,因此也就无法保持状态的连续了!6.那么对于transient变量又是怎么样的呢?
我们修改代码:
import java.io.*; 
public class Data2Object implements Serializable 
{ 
 public  transient Person p=new Person("hello")
 public String toString() { 
  return p.name; 
 } 
 public static void main(String[] args) throws ClassNotFoundException,FileNotFoundException,IOException 
 {  
  Data2Object object=new Data2Object(); 
 
  object.p.name="why";
  System.out.println("序列化以前:"+object); 
  
  ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("lql.out")); 
  out.writeObject(object);
  ObjectInputStream in=new ObjectInputStream(new FileInputStream("lql.out")); 
  Data2Object object2=(Data2Object)in.readObject(); 
  
  System.out.println("序列化以后:"+object2); 
  System.out.println("object==object2?"+(object==object2));   
  System.out.println("object.p==object2.p?"+(object.p==object2.p));
 } 
} 


运行以后发现输出:
/*
构建一个Person对象
序列化以前:why
Exception in thread "main" java.lang.NullPointerException
        at Data2Object.toString(Data2Object.java:8)
        at java.lang.String.valueOf(String.java:2827)
        at java.lang.StringBuilder.append(StringBuilder.java:115)
        at Data2Object.main(Data2Object.java:22)
*/
也就是说反序列化以后根本就找不到transient变量,因为transient根本就没有被序列化! 看一个序列化的例子:
import java.util.*;
import java.io.*;public final class Country implements Serializable{
   private static final Map INDEX=new HashMap();
   
   public static final Country CANADA =new Country("CANADA"); 
   public static final Country US=new Country("US"); 
   public static final Country EN =new Country("EN"); 
   public static final Country CHINA =new Country("CHINA"); 
   
   private String name;
   private Country(String name){
       this.name=name;
    INDEX.put(name,this);
   }
   private static Country lookup(String name){
       return (Country)INDEX.get(name);
   }
   public String toString(){
       return this.name;
   }
   private Object readResolve()throws ObjectStreamException{
       Country c=lookup(name);
       return c;
   }
   public static void main(String rags[]){
        System.out.println(Country.lookup("CHINA"));
   }
}


country本身实现了Serializable接口,可以进行序列化,而其中的成员除了name都是static。为了在反序列化的时候得到Country里面的静态成员,就需要提供一个readResolve()方法,用它来返回Map里面的静态实例。
import java.util.*;
import java.io.*;public final class SerTest{
 public static void main(String rags[])throws Exception{
     FileOutputStream fout=new FileOutputStream("test.out");
     ObjectOutputStream oout=new ObjectOutputStream(fout);
    oout.writeObject(Country.CHINA);
    System.out.println(Country.CHINA.hashCode());
    fout.close();
    oout.close();
    FileInputStream fin=new FileInputStream("test.out");
    ObjectInputStream oin=new ObjectInputStream(fin);
    Country c=(Country)oin.readObject();
    System.out.println(c.hashCode());
    fin.close();
    oin.close();
    System.out.println(c==Country.CHINA);
   }
}


如果注释掉readResolve方法,那么将会反序列化出不同的对象出来。
### 关于PAT B1088的相关IT内容 #### 题目概述 PAT B1088通常涉及字符串处理和模式匹配的内容。具体来说,该题目可能要求考生统计特定子串在给定字符串中的出现次数或者判断某些条件下的字符变换情况。这类问题的核心在于熟悉字符串操作的基础知识以及高效算法的设计。 #### 字符串处理技术 对于此类问题,常见的解决方案包括但不限于以下几种方法: 1. **暴力枚举法** 使用两层循环逐一比较目标子串与原字符串中的每一个位置是否匹配。这种方法简单易懂,但在大规模数据下效率较低[^3]。 2. **KMP算法** KMP(Knuth-Morris-Pratt)是一种高效的字符串匹配算法,能够在O(n+m)时间内完成匹配任务,其中n为目标字符串长度,m为模式串长度。通过构建前缀表来减少不必要的回溯操作[^4]。 3. **哈希映射优化** 如果需要频繁查询某个子串是否存在或其频率分布,可以考虑利用哈希表存储中间结果以加速后续计算过程。例如,在本题中如果涉及到大小写字母转换后的计数问题,则可借助`<algorithm>`头文件里的`tolower()`和`toupper()`函数简化逻辑实现[^5]。 ```cpp #include <iostream> #include <string> #include <cctype> // tolower, toupper 所需头文件 int main() { std::string s; getline(std::cin, s); int count = 0; for(auto c : s){ if(c >= 'A' && c <= 'Z') { ++count; } } std::cout << count << "\n"; } ``` 上述代码片段展示了如何读取一行输入并统计大写英文字母的数量。这里运用到了标准库提供的功能来进行字符分类检测。 #### 数据类型选择建议 当遇到数值范围较大的场景时,比如题目描述提到“不超过\(10^{10}\)”这样的约束条件时,推荐选用浮点数类型`double`而非整型变量来保存这些值,因为后者可能会因溢出而导致错误答案[^2]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值