今天同事问到ArrayList中的
- private transient E[] elementData;
private transient E[] elementData;
声明为transient,为什么还可以序列化成功呢?
我的回答是ArrayList重写了
- private void writeObject(java.io.ObjectOutputStream s)
-
throws java.io.IOException{
-
int expectedModCount = modCount;
-
- s.defaultWriteObject();
-
-
- s.writeInt(elementData.length);
-
-
-
for (int i=0; i<size; i++)
- s.writeObject(elementData[i]);
-
-
if (modCount != expectedModCount) {
-
throw new ConcurrentModificationException();
- }
- }
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
int expectedModCount = modCount;
// Write out element count, and any hidden stuff
s.defaultWriteObject();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
在使用ObjectOutputStream序列化对象时会调用这个writeObject方法。
第二个问题是为什么要声明为transient呢?
在google了下,发现主流说法如下:
- ArrayList实现了java.io.Serializable接口,所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下:
-
-
* private static final long serialVersionUID = 8683452581122892189L;
-
* private transient Object elementData[];
-
* private int size;
-
-
-
可以看出serialVersionUID和size都将自动序列化到介质中,但elementData数组对象却定义为transient了。
- 也就是说 ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现?因为elementData数组中存储的
- “元素”其实仅是对这些元素的一个引用,并不是真正的对象,序列化一个对象的引用是毫无意义的,因为序列化是为了
- 反序列化,当你反序列化时,这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进
- 行序列化操作。这就是writeObject()的作用。
ArrayList实现了java.io.Serializable接口,所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下:
* private static final long serialVersionUID = 8683452581122892189L;
* private transient Object elementData[];
* private int size;
可以看出serialVersionUID和size都将自动序列化到介质中,但elementData数组对象却定义为transient了。
也就是说 ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现?因为elementData数组中存储的
“元素”其实仅是对这些元素的一个引用,并不是真正的对象,序列化一个对象的引用是毫无意义的,因为序列化是为了
反序列化,当你反序列化时,这些对象的引用已经不可能指向原来的对象了。所以在这儿需要手工的对ArrayList的元素进
行序列化操作。这就是writeObject()的作用。
果真如此么??????
验证下:
把ArrayList的内容完全copy到一个新类里面,命名为MyArrayList,如下:
- public class MyArrayList<E> extends AbstractList<E>
-
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- {
-
private static final long serialVersionUID = 8683452581122892189L;
-
-
-
-
-
-
private E[] elementData;
-
- 。。。。。。。。
-
-
private void writeObject(java.io.ObjectOutputStream s)
-
throws java.io.IOException{
-
int expectedModCount = modCount;
-
- s.defaultWriteObject();
-
-
if (modCount != expectedModCount) {
-
throw new ConcurrentModificationException();
- }
- }
-
-
-
-
-
-
private void readObject(java.io.ObjectInputStream s)
-
throws java.io.IOException, ClassNotFoundException {
-
- s.defaultReadObject();
- }
-
- }
public class MyArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private E[] elementData;
。。。。。。。。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
int expectedModCount = modCount;
// Write out element count, and any hidden stuff
s.defaultWriteObject();
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
}
}
把transient去掉,write/readObject采用默认方式。
测试下MyArraylist序列化功能:
- MyArrayList al = new MyArrayList<String>();
-
al.add("sssssssssssssssss");
-
al.add("bbbbbbbbbbbbbbbbbbt");
-
al.add("gggggggggggggggggg");
-
-
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\al.tmp"));
- oos.writeObject(al);
-
-
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\al.tmp"));
-
- MyArrayList<String> a = (MyArrayList<String>)ois.readObject();
-
for(String s: a)
- {
- System.out.println(s);
- }
MyArrayList al = new MyArrayList<String>();
al.add("sssssssssssssssss");
al.add("bbbbbbbbbbbbbbbbbbt");
al.add("gggggggggggggggggg");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\al.tmp"));
oos.writeObject(al);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\al.tmp"));
MyArrayList<String> a = (MyArrayList<String>)ois.readObject();
for(String s: a)
{
System.out.println(s);
}
输出结果为:
- sssssssssssssssss
- bbbbbbbbbbbbbbbbbbt
- gggggggggggggggggg
sssssssssssssssss
bbbbbbbbbbbbbbbbbbt
gggggggggggggggggg
到此证明:引用序列化无效的说法是错误的,这点在ObjectOutputStream中也有说明。
那是为什么呢?
既然是数组,要序列化到文件中,那就单独测试下数组对象的序列化和反序列化吧
- String[] stra = new String[4];
-
stra[0] = "mmmmmmmmmm";
-
stra[2] = "nnnnnnnnnn";
-
-
oos = new ObjectOutputStream(new FileOutputStream("D:\\sa.tmp"));
- oos.writeObject(stra);
-
-
ois = new ObjectInputStream(new FileInputStream("D:\\sa.tmp"));
-
- String[] str = (String[])ois.readObject();
-
for(String s: str)
- {
- System.out.println(s);
- }
String[] stra = new String[4];
stra[0] = "mmmmmmmmmm";
stra[2] = "nnnnnnnnnn";
oos = new ObjectOutputStream(new FileOutputStream("D:\\sa.tmp"));
oos.writeObject(stra);
ois = new ObjectInputStream(new FileInputStream("D:\\sa.tmp"));
String[] str = (String[])ois.readObject();
for(String s: str)
{
System.out.println(s);
}
输出结果为:
- mmmmmmmmmm
-
null
- nnnnnnnnnn
-
null
mmmmmmmmmm
null
nnnnnnnnnn
null
从输出结果来看,数组序列化时,不管是否有值,都会将整个数组序列化到文件中。
由此可以看出,比较靠谱的原因是:
ArrayList是会开辟多余空间来保存数据的,而系列化和反序列化这些没有存放数据的空间是要消耗更多资源的,所以ArrayList的数组就声明为transient,自己实现write/readObject方法,仅仅系列化已经存放的数据。
分享到:
相关推荐
在C#编程中,序列化和反序列化是两个重要的概念,它们主要用于数据持久化和网络传输。序列化是将对象的状态转化为可存储或可传输的格式,而反序列化则是将这种格式恢复为原来的对象状态。在C#中,有多种格式可以用于...
ISerializable接口是.NET Framework提供的一种自定义序列化的方式,允许开发者精确控制对象的序列化和反序列化过程。下面我们将详细讲解如何在C#中实现自定义序列化,特别是通过ISerializable接口。 首先,让我们看...
在Java编程语言中,对象的序列化和反序列化是两个关键的概念,它们允许我们将对象的状态转换为可存储或传输的格式,然后再恢复为原始对象。这个过程对于数据持久化、网络传输以及跨进程通信等场景非常有用。下面将...
本主题主要探讨的是使用`XmlSerializer`对ArrayList和自定义类实例进行序列化和反序列化的操作。 首先,让我们从ArrayList的序列化开始。ArrayList是.NET框架中一种动态数组,可以存储各种类型的数据。在C#中,如果...
这意味着`ArrayList`提供了自定义的序列化策略,即使`elementData`被声明为`transient`,它仍然能够被正确地序列化和反序列化。 自定义的`writeObject()`和`readObject()`方法允许开发者在序列化过程中增加更多的...
4. 操作完成后,使用FileOutputStream和ObjectOutputStream将ArrayList序列化到本地文件。 5. 应用启动时,通过FileInputStream和ObjectInputStream从文件反序列化恢复ArrayList。 需要注意的是,虽然序列化可以...
### Web Service 中的序列化详解 #### 序言 序列化是计算机科学中的一个重要概念,尤其是在Web服务领域,它指的是将复杂的数据结构或者对象状态转换为可以存储或传输的格式的过程;而反序列化则是其逆过程,即将...
本文通过分析ArrayList的序列化来介绍Java序列化的相关内容。主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了java.io.Serializable接口才能被序列化 transient的作用是什么 怎么自定义序列...
### C#中的序列化与反序列化 #### 一、概念理解 序列化与反序列化是编程领域中经常遇到的概念。简而言之,**序列化**是指将对象的状态(即对象的数据)转换为可以存储或传输的形式的过程;而**反序列化**则是将...
`java.util.Optional`、`java.util.ArrayList`、`java.util.HashMap`等内置类都实现了序列化,方便我们在实际开发中使用。 10. **源码分析**: 博文中可能深入分析了`ObjectOutputStream`和`ObjectInputStream`的...
当序列化`Student`对象时,`writeReplace`方法返回了一个包含`gender`和`address`属性的`ArrayList`对象,而不是原始的`Student`对象。这意味着序列化后只会保存`gender`和`address`这两个字段的信息。 #### 2. `...
Java序列化是Java平台提供的一种持久化对象的机制,它允许我们将对象的状态转换为字节流,以便存储或在网络上传输。在这个特定的场景中,我们关注的是如何使用Java序列化来多次追加对象到一个TXT文件,而不是覆盖...
使用Gson进行ArrayList序列化的过程如下: 1. 创建一个Java对象类,该类的属性与JSON对象的键相对应。 2. 实例化一个ArrayList,填充你需要的数据。 3. 使用GsonBuilder构建Gson实例,可以设置各种序列化和反序列化...
【JAVA对象序列化保存为XML文件的工具类】 在Java编程中,对象序列化是一种将对象的状态转换为字节流的过程,以便可以存储或在网络上传输。而在反序列化时,这个字节流又可以恢复为原来的对象。Java提供了一个方便...
### C#中关于序列化`HashTable`的具体用法详解 #### 一、`HashTable`简介 在.NET Framework中,`HashTable`是`System.Collections`命名空间下提供的一个容器类,主要用于处理和表现键值对(key-value pairs)。键...
`Parcelable`是Android提供的一种高效的数据序列化方式,相比`Serializable`,它的序列化和反序列化速度更快,但实现过程相对复杂。 标题"Android Parcelable序列化自定义类集合在Activity间传递"所涉及的知识点...
在ArrayList的序列化过程中,为了保持序列化的一致性,必须在writeObject方法中手动将elementData数组中的元素序列化到输出流中。在反序列化的过程中,readObject方法将被用来从输入流中读取元素并恢复ArrayList的...
在“序列化与集合窗体应用”项目中,可能涵盖了如何将集合对象(如ArrayList或Dictionary)进行序列化,然后保存到文件或数据库中。这在处理大量数据时非常有用,因为可以直接读取序列化的数据,避免了重新计算或...
Kryo序列化及反序列化用法示例 Kryo是Java对象图形序列化框架,它主要特点是性能高效和易用,该项目用来序列化对象到文件、数据库或者网络。序列化是指将对象转换为字节流的过程,而反序列化是指将字节流转换回对象...