Posts Tagged ‘Serializable’

Qt中的QDataStream

September 20th, 2016

之前在Java中遇到了需要序列化和反序列化的场景,Qt也为我们提供了类似的数据流类—QDataStream和QTextStream,可以从任意的输入输出设备读取或写入数据。其中QDataStream用于读写二进制数据,他可以在读写数据的时候已经严格定义了数据流的类型和每个类型的长度,不用关心编码转换的问题。TextStream用于读写文本(如HTML、XML和源代码等)的格式,解决了二进制文件格式无法直接阅读和编辑的缺点。QTextStream考虑了Unicode编码与系统本地编码或其他编码之间的转换问题,并考虑了不同操作系统之间行尾符切换的问题(MS “\r\n”, Mac “ \n”)。

QDataStream 与  QIODevice 联系十分紧密,QIODevice代表数据的输入输出媒介,我们拿 QFile 举例。从下面代码示例可以看到QDataStream通过QFile对文件进行修改

QFile file("file.dat");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);   // we will serialize the data into the file
out << QString("the answer is");   // serialize a string
out << (qint32)42;        // serialize an integer
QFile file("file.dat");
file.open(QIODevice::ReadOnly);
QDataStream in(&file);    // read the data serialized from the file
QString str;
qint32 a;
in >> str >> a;           // extract "the answer is" and 42

通过以上代码,我们也可以将复杂的QHash,QSet直接写入QDataStream中,然后再从文件中读出该数据结构,具体代码见main。通过查看Document,我们发现除了QFile,我们还可以把QAbstractSocket, QBuffer, QFile, QLocalSocket, QNetworkReply和QProcess传入QDataStream,进行数据的序列化保存。最令我惊讶的是QAbstractSocket和QProcess,也就是说我们对一个进程进行dump,类似于criu提供的快照功能,这个是非常强大的。

类似的Qt在序列化数据的时候也要设置setVersion(),out.setVersion(QDataStream::Qt_4_8); in.setVersion(QDataStream::Qt_4_8);这样在反序列化的时候不会出现数据格式的问题,类似于Java中的serialVersionUID。

另外一个与QDataStream结合比较紧密的就是QByteArray,我在编写网络程序中,会使用到QByteArray。这个class相比const char *更加强大,使用这个类也就意味着我们不用考虑字符串结束符\0,但是需要注意的是每次字符串赋值都是一次深拷贝,这样避免了出现内存问题。如果我们不需要深拷贝的时候,可以使用fromRawData ( const char * data, int size )直接对字符串进行修改。除此之外还可以使用resize方法对大小进行调整,这个在socket数据通信中十分有用。

QDataStream::QDataStream ( QByteArray * a, QIODevice::OpenMode mode )
Constructs a data stream that operates on a byte array, a. The mode describes how the device is to be used.Alternatively, you can use QDataStream(const QByteArray &) if you just want to read from a byte array.Since QByteArray is not a QIODevice subclass, internally a QBuffer is created to wrap the byte array.

 

参考:

http://doc.qt.io/qt-4.8/qbytearray.html

Java序列化与反序列化

August 8th, 2016

最近在对一个大型Java项目进行调试,这个Java程序当点击一个按钮后就会未响应,后来经过调试发现代码中存在大量的io操作,他们将从文件读取的数据存入到HashMap、LinkList中。找到了程序性能瓶颈,我设计一种缓存思路,将这种Object直接写入文件,只要存在我要找的Object文件,我就不会费时费力的重新存入HashMap,而是从Object中读取。

根据查询Java Docs api,我发现一组Interface Serializable,只要class继承了Serializable接口,那么这个类就可以被序列化,因此我把该类中所有的成员变量所在class都继承了Serializable接口。具体读写Object文件的方式和一般文件读取非常类似。

存Object文件:

class T implements Serializable
{
	int i = 10;
	int j = 9;
	double d = 2.3;
	transient int k = 15;
}
....
FileOutputStream fos = new FileOutputStream("...");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);

写Object文件:

FileInputStream fis = new FileInputStream("....");
ObjectInputStream ois = new ObjectInputStream(fis);
T t= (T)ois.readObject();

Java提供了transient 关键字,他的意思就是表明该成员变量不会被序列化,也就是说反序列化后该成员变量保存的值会消失。如果继承了Serializable,我们是无法干预jre对class对象的序列化,所以writeObject、readObject对象一定要顺序相同,因为只有我们知道序列化的内容。

jre为每个序列化的class都赋值了serialVersionUID ,以此来计算反序列化时该值是否相同,不同的话也就意味着该class不能被恢复出来。

The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender’s class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named “serialVersionUID” that must be static, final, and of type long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果我们想控制序列化的时机,那么我们应该让class继承Externalizable接口,Externalizable接口事实上又继承了 Serializable接口,而且在其基础上增加了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些特殊的操作。

我们可以在writeExternal()和readExternal()中自定义序列化的方式,这样子可以增强灵活性。也就是说每次writeObject时,都会调用WriteExtenal()方法,而在WriteExtenal()方法中我们需要将当前对象的值写入到流中;而每次readObject()时,调用的是默认的构造函数,如果我们不在 readExternal()方法中读取每个成员变量,那么成员变量就是null!

 

参考:

http://www.cnblogs.com/chenfei0801/archive/2013/04/06/3002146.html