kryomysql 新增字段段为什么会失败

Java序列化示例教程 - ImportNew
| 标签: ,
Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一。Java序列化API允许我们将一个对象转换为流,并通过网络发送,或将其存入文件或数据库以便未来使用,反序列化则是将对象流转换为实际程序中使用的Java对象的过程。Java同步化过程乍看起来很好用,但它会带来一些琐碎的安全性和完整性问题,在文章的后面部分我们会涉及到,以下是本教程涉及的主题。
Java序列化接口
使用序列化和serialVersionUID进行类
Java外部化接口
Java序列化方法
序列化结合继承
序列化代理模式
Java序列化接口
如果你希望一个类对象是可序列化的,你所要做的是实现java.io.Serializable接口。序列化一种标记接口,不需要实现任何字段和方法,这就像是一种选择性加入的处理,通过它可以使类对象成为可序列化的对象。
序列化处理是通过ObjectInputStream和ObjectOutputStream实现的,因此我们所要做的是基于它们进行一层封装,要么将其保存为文件,要么将其通过网络发送。我们来看一个简单的序列化示例。
package com.journaldev.
import java.io.S
public class Employee implements Serializable {
// private static final long serialVersionUID = -4208496L;
// private S
public String toString(){
return &Employee{name=&+name+&,id=&+id+&,salary=&+salary+&}&;
//getter and setter methods
public String getName() {
public void setName(String name) {
this.name =
public int getId() {
public void setId(int id) {
public int getSalary() {
public void setSalary(int salary) {
this.salary =
// public String getPassword() {
// public void setPassword(String password) {
this.password =
注意一下,这是一个简单的java bean,拥有一些属性以及getter-setter方法,如果你想要某个对象属性不被序列化成流,你可以使用transient关键字,正如示例中我在salary变量上的做法那样。
现在我们假设需要把我们的对象写入文件,之后从相同的文件中将其反序列化,因此我们需要一些工具方法,通过使用ObjectInputStream和ObjectOutputStream来达到序列化的目的。
package com.journaldev.
import java.io.FileInputS
import java.io.FileOutputS
import java.io.IOE
import java.io.ObjectInputS
import java.io.ObjectOutputS
* A simple class with generic serialize and deserialize method implementations
* @author pankaj
public class SerializationUtil {
// deserialize to Object from given file
public static Object deserialize(String fileName) throws IOException,
ClassNotFoundException {
FileInputStream fis = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
ois.close();
// serialize the given object and save it to file
public static void serialize(Object obj, String fileName)
throws IOException {
FileOutputStream fos = new FileOutputStream(fileName);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
fos.close();
注意一下,方法的参数是Object,它是任何Java类的基类,这样写法以一种很自然的方式保证了通用性。
现在我们来写一个测试程序,看一下Java序列化的实战。
package com.journaldev.
import java.io.IOE
public class SerializationTest {
public static void main(String[] args) {
String fileName=&employee.ser&;
Employee emp = new Employee();
emp.setId(100);
emp.setName(&Pankaj&);
emp.setSalary(5000);
//serialize to file
SerializationUtil.serialize(emp, fileName);
} catch (IOException e) {
e.printStackTrace();
Employee empNew =
empNew = (Employee) SerializationUtil.deserialize(fileName);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
System.out.println(&emp Object::&+emp);
System.out.println(&empNew Object::&+empNew);
运行以上测试程序,可以得到以下输出。
emp Object::Employee{name=Pankaj,id=100,salary=5000}
empNew Object::Employee{name=Pankaj,id=100,salary=0}
由于salary是一个transient变量,它的值不会被存入文件中,因此也不会在新的对象中被恢复。类似的,静态变量的值也不会被序列化,因为他们是属于类而非对象的。
使用序列化和serialVersionUID进行类重构
Java序列化允许java类中的一些变化,如果他们可以被忽略的话。一些不会影响到反序列化处理的变化有:
在类中添加一些新的变量。
将变量从transient转变为非tansient,对于序列化来说,就像是新加入了一个变量而已。
将变量从静态的转变为非静态的,对于序列化来说,就也像是新加入了一个变量而已。
不过这些变化要正常工作,java类需要具有为该类定义的serialVersionUID,我们来写一个测试类,只对之前测试类已经生成的序列化文件进行反序列化。
package com.journaldev.
import java.io.IOE
public class DeserializationTest {
public static void main(String[] args) {
String fileName=&employee.ser&;
Employee empNew =
empNew = (Employee) SerializationUtil.deserialize(fileName);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
System.out.println(&empNew Object::&+empNew);
现在,在Employee类中去掉”password”变量的注释和它的getter-setter方法,运行。你会得到以下异常。
java.io.InvalidClassException: com.journaldev.serialization.E local class incompatible: stream classdesc serialVersionUID = -4208496, local class serialVersionUID = -9432383
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at com.journaldev.serialization.SerializationUtil.deserialize(SerializationUtil.java:22)
at com.journaldev.serialization.DeserializationTest.main(DeserializationTest.java:13)
empNew Object::null
原因很显然,上一个类和新类的serialVersionUID是不同的,事实上如果一个类没有定义serialVersionUID,它会自动计算出来并分配给该类。Java使用类变量、方法、类名称、包,等等来产生这个特殊的长数。如果你在任何一个IDE上工作,你都会得到警告“可序列化类Employee没有定义一个静态的final的serialVersionUID,类型为long”。
我们可以使用java工具”serialver”来产生一个类的serialVersionUID,对于Employee类,可以执行以下命令。
SerializationExample/bin$serialver -classpath . com.journaldev.serialization.Employee
记住,从程序本身生成序列版本并不是必须的,我们可以根据需要指定值,这个值的作用仅仅是告知反序列化处理机制,新的类是相同的类的新版本,应该进行可能的反序列化处理。
举个例子,在Employee类中仅仅将serialVersionUID字段的注释去掉,运行SerializationTest程序。现在再将Employee类中的password字段的注释去掉,运行DeserializationTest程序,你会看到对象流被成功地反序列化了,因为Employee类中的变动与序列化处理是相容的。
Java外部化接口
如果你在序列化处理中留个心,你会发现它是自动处理的。有时候我们想要去隐藏对象数据,来保持它的完整性,可以通过实现java.io.Externalizable接口,并提供writeExternal()和readExternal()方法的实现,它们被用于序列化处理。
package com.journaldev.
import java.io.E
import java.io.IOE
import java.io.ObjectI
import java.io.ObjectO
public class Person implements Externalizable{
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeObject(name+&xyz&);
out.writeObject(&abc&+gender);
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
id=in.readInt();
//read in the same order as written
name=(String) in.readObject();
if(!name.endsWith(&xyz&)) throw new IOException(&corrupted data&);
name=name.substring(0, name.length()-3);
gender=(String) in.readObject();
if(!gender.startsWith(&abc&)) throw new IOException(&corrupted data&);
gender=gender.substring(3);
public String toString(){
return &Person{id=&+id+&,name=&+name+&,gender=&+gender+&}&;
public int getId() {
public void setId(int id) {
public String getName() {
public void setName(String name) {
this.name =
public String getGender() {
public void setGender(String gender) {
this.gender =
注意,在将其转换为流之前,我已经更改了字段的值,之后读取时会得到这些更改,通过这种方式,可以在某种程度上保证数据的完整性,我们可以在读取流数据之后抛出异常,表明完整性检查失败。来看一个测试程序。
package com.journaldev.
import java.io.FileInputS
import java.io.FileOutputS
import java.io.IOE
import java.io.ObjectInputS
import java.io.ObjectOutputS
public class ExternalizationTest {
public static void main(String[] args) {
String fileName = &person.ser&;
Person person = new Person();
person.setId(1);
person.setName(&Pankaj&);
person.setGender(&Male&);
FileOutputStream fos = new FileOutputStream(fileName);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
FileInputS
fis = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = (Person)ois.readObject();
ois.close();
System.out.println(&Person Object Read=&+p);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
运行以上测试程序,可以得到以下输出。
Person Object Read=Person{id=1,name=Pankaj,gender=Male}
那么哪个方式更适合被用来做序列化处理呢?实际上使用序列化接口更好,当你看到这篇教程的末尾时,你会知道原因的。
Java序列化方法
我们已经看到了,java的序列化是自动的,我们所要做的仅仅是实现序列化接口,其实现已经存在于ObjectInputStream和ObjectOutputStream类中了。不过如果我们想要更改存储数据的方式,比如说在对象中含有一些敏感信息,在存储/获取它们之前我们要进行加密/解密,这该怎么办呢?这就是为什么在类中我们拥有四种方法,能够改变序列化行为。
如果以下方法在类中存在,它们就会被用于序列化处理。
readObject(ObjectInputStream ois):如果这个方法存在,ObjectInputStream readObject()方法会调用该方法从流中读取对象。
writeObject(ObjectOutputStream oos):如果这个方法存在,ObjectOutputStream writeObject()方法会调用该方法从流中写入对象。一种普遍的用法是隐藏对象的值来保证完整性。
Object writeReplace():如果这个方法存在,那么在序列化处理之后,该方法会被调用并将返回的对象序列化到流中。
Object readResolve():如果这个方法存在,那么在序列化处理之后,该方法会被调用并返回一个最终的对象给调用程序。一种使用方法是在序列化类中实现单例模式,你可以从中读到更多知识。
通常情况下,当实现以上方法时,应该将其设定为私有类型,这样子类就无法覆盖它们了,因为它们本来就是为了序列化而建立的,设定为私有类型能避免一些安全性问题。
序列化结合继承
有时候我们需要对一个没有实现序列化接口的类进行扩展,如果依赖于自动化的序列化行为,而一些状态是父类拥有的,那么它们将不会被转换为流,因此以后也无法获取。
在此,readObject()和writeObject()就可以派上大用处了,通过提供它们的实现,我们可以将父类的状态存入流中,以便今后获取。我们来看一下实战。
package com.journaldev.serialization.
public class SuperClass {
public int getId() {
public void setId(int id) {
public String getValue() {
public void setValue(String value) {
this.value =
父类是一个简单的java bean,没有实现序列化接口。
package com.journaldev.serialization.
import java.io.IOE
import java.io.InvalidObjectE
import java.io.ObjectInputS
import java.io.ObjectInputV
import java.io.ObjectOutputS
import java.io.S
public class SubClass extends SuperClass implements Serializable, ObjectInputValidation{
private static final long serialVersionUID = -6390329L;
public String getName() {
public void setName(String name) {
this.name =
public String toString(){
return &SubClass{id=&+getId()+&,value=&+getValue()+&,name=&+getName()+&}&;
//adding helper method for serialization to save/initialize super class state
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException{
ois.defaultReadObject();
//notice the order of read and write should be same
setId(ois.readInt());
setValue((String) ois.readObject());
private void writeObject(ObjectOutputStream oos) throws IOException{
oos.defaultWriteObject();
oos.writeInt(getId());
oos.writeObject(getValue());
public void validateObject() throws InvalidObjectException {
//validate the object here
if(name == null || &&.equals(name)) throw new InvalidObjectException(&name can't be null or empty&);
if(getId() &=0) throw new InvalidObjectException(&ID can't be negative or zero&);
注意,将额外数据写入流和读取流的顺序应该是一致的,我们可以在读与写之中添加一些逻辑,使其更安全。
同时还需要注意,这个类实现了ObjectInputValidation接口,通过实现validateObject()方法,可以添加一些业务验证来确保数据完整性没有遭到破坏。
以下通过编写一个测试类,看一下我们是否能够从序列化的数据中获取父类的状态。
package com.journaldev.serialization.
import java.io.IOE
import com.journaldev.serialization.SerializationU
public class InheritanceSerializationTest {
public static void main(String[] args) {
String fileName = &subclass.ser&;
SubClass subClass = new SubClass();
subClass.setId(10);
subClass.setValue(&Data&);
subClass.setName(&Pankaj&);
SerializationUtil.serialize(subClass, fileName);
} catch (IOException e) {
e.printStackTrace();
SubClass subNew = (SubClass) SerializationUtil.deserialize(fileName);
System.out.println(&SubClass read = &+subNew);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
运行以上测试程序,可以得到以下输出。
SubClass read = SubClass{id=10,value=Data,name=Pankaj}
因此通过这种方式,可以序列化父类的状态,即便它没有实现序列化接口。当父类是一个我们无法改变的第三方的类,这个策略就有用武之地了。
序列化代理模式
Java序列化也带来了一些严重的误区,比如:
类的结构无法大量改变,除非中断序列化处理,因此即便我们之后已经不需要某些变量了,我们也需要保留它们,仅仅是为了向后兼容。
序列化会导致巨大的安全性危机,一个攻击者可以更改流的顺序,继而对系统造成伤害。举个例子,用户角色被序列化了,攻击者可以更改流的值为admin,再执行恶意代码。
序列化代理模式是一种使序列化能达到极高安全性的方式,在这个模式下,一个内部的私有静态类被用作序列化的代理类,该类的设计目的是用于保留主类的状态。这个模式的实现需要合理实现readResolve()和writeReplace()方法。
让我们先来写一个类,实现了序列化代码模式,之后再对其进行分析,以便更好的理解原理。
package com.journaldev.serialization.
import java.io.InvalidObjectE
import java.io.ObjectInputS
import java.io.S
public class Data implements Serializable{
private static final long serialVersionUID = 6448459L;
public Data(String d){
this.data=d;
public String getData() {
public void setData(String data) {
this.data =
public String toString(){
return &Data{data=&+data+&}&;
//serialization proxy class
private static class DataProxy implements Serializable{
private static final long serialVersionUID = 5436744L;
private String dataP
private static final String PREFIX = &ABC&;
private static final String SUFFIX = &DEFG&;
public DataProxy(Data d){
//obscuring data for security
this.dataProxy = PREFIX + d.data + SUFFIX;
private Object readResolve() throws InvalidObjectException {
if(dataProxy.startsWith(PREFIX) && dataProxy.endsWith(SUFFIX)){
return new Data(dataProxy.substring(3, dataProxy.length() -4));
}else throw new InvalidObjectException(&data corrupted&);
//replacing serialized object to DataProxy object
private Object writeReplace(){
return new DataProxy(this);
private void readObject(ObjectInputStream ois) throws InvalidObjectException{
throw new InvalidObjectException(&Proxy is not used, something fishy&);
Data和DataProxy类都应该实现序列化接口。
DataProxy应该能够保留Data对象的状态。
DataProxy是一个内部的私有静态类,因此其他类无法访问它。
DataProxy应该有一个单独的构造方法,接收Data作为参数。
Data类应该提供writeReplace()方法,返回DataProxy实例,这样当Data对象被序列化时,返回的流是属于DataProxy类的,不过DataProxy类在外部是不可见的,所有它不能被直接使用。
DataProxy应该实现readResolve()方法,返回Data对象,这样当Data类被反序列化时,在内部其实是DataProxy类被反序列化了,之后它的readResolve()方法被调用,我们得到了Data对象。
最后,在Data类中实现readObject()方法,抛出InvalidObjectException异常,防止黑客通过伪造Data对象的流并对其进行解析,继而执行攻击。
我们来写一个小测试,检查一下这样的实现是否能工作。
package com.journaldev.serialization.
import java.io.IOE
import com.journaldev.serialization.SerializationU
public class SerializationProxyTest {
public static void main(String[] args) {
String fileName = &data.ser&;
Data data = new Data(&Pankaj&);
SerializationUtil.serialize(data, fileName);
} catch (IOException e) {
e.printStackTrace();
Data newData = (Data) SerializationUtil.deserialize(fileName);
System.out.println(newData);
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
运行以上测试程序,可以得到以下输出。
Data{data=Pankaj}
如果你打开,可以看到DataProxy对象已经被作为流存入了文件中。
这就是Java序列化的所有内容,看上去很简单但我们应当谨慎地使用它,通常来说,最好不要依赖于默认实现。你可以从上面的链接中下载项目,玩一玩,这能让你学到更多。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:)
不错的文章。
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNew您还没有登录,快捷通道只有在登录后才能使用。 还没有帐号? 赶紧
初赛提交试跑Kryo RuntimeException
在线时间19小时
提交了提前试跑,发现结果都是Your topology may be ok ,but the accuracy is zero。下载日志来看发现序列化和反序列化的时候Kryo直接崩溃了java.lang.RuntimeException: com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): java.net.InetSocketAddressSerialization trace:bornHost (com.mon.message.MessageExt)&&&&at backtype.storm.serialization.KryoTupleDeserializer.deserialize(KryoTupleDeserializer.java:103) ~[jstorm-core-2.1.1.jar:na]&&&&at backtype.storm.serialization.KryoTupleDeserializer.deserialize(KryoTupleDeserializer.java:60) ~[jstorm-core-2.1.1.jar:na]&&&&at com.alibaba.jstorm.task.TaskReceiver$DeserializeRunnable.deserialize(TaskReceiver.java:141) [jstorm-core-2.1.1.jar:na]&&&&at com.alibaba.jstorm.task.TaskReceiver$DeserializeRunnable.onEvent(TaskReceiver.java:163) [jstorm-core-2.1.1.jar:na]&&&&at backtype.storm.utils.DisruptorQueueImpl.consumeBatchToCursor(DisruptorQueueImpl.java:193) [jstorm-core-2.1.1.jar:na]&&&&at backtype.storm.utils.DisruptorQueueImpl.consumeBatchWhenAvailable(DisruptorQueueImpl.java:161) [jstorm-core-2.1.1.jar:na]&&&&at com.alibaba.jstorm.task.TaskReceiver$DeserializeRunnable.run(TaskReceiver.java:187) [jstorm-core-2.1.1.jar:na]&&&&at com.alibaba.jstorm.callback.AsyncLoopRunnable.run(AsyncLoopRunnable.java:95) [jstorm-core-2.1.1.jar:na]&&&&at java.lang.Thread.run(Thread.java:756) [na:1.8.0_66]Caused by: com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): java.net.InetSocketAddress好像是Kryo的包没有引入进去,这个不是允许用的么?为什么会找不到这个class?好奇怪啊[ 此帖被黑暗法师在 18:40重新编辑 ]
在线时间37小时
不是有错误提示吗missing no-arg constructor&&你的数据结构缺少无参数构造函数
在线时间19小时
回 1楼玄弟的帖子
SPout&&&&Bolt之类的也是需要无参数构造函数的吗?
在线时间7小时
Re初赛提交试跑Kryo RuntimeException
也是这种情况[ 此帖被feiyoshang在 10:40重新编辑 ]
在线时间19小时
Re初赛提交试跑Kryo RuntimeException
我知道为什么会这样了!!!!!
在线时间37小时
不需要,只有你传递的消息中含有的数据结构,需要无参数构造函数
在线时间5小时
回 4楼黑暗法师的帖子
我也遇到了这种情况,请问您最后是怎么解决的呢
在线时间37小时
你传输的消息自定义的那些class 缺少无参数构造函数,加个无参数构造函数就可以了
在线时间21小时
回 7楼玄弟的帖子
你好,我也存在这个问题,用本地模式跑的时候没有问题,一提交到集群就会报错,无参构造函数也添加了。我定义的数据结构如图:'995')this.width='995';">只有在Spout里面把List&MessageExt&用这个自定义tuple封装了一下。堆栈和楼主基本一样,请问是什么问题?万分感激!
在线时间37小时
具体我没看,看错误提示 MessageExt 没有无参数构造函数。如果是这个原因的话,我建议你不好把MessageExt 作为你这个class的一个字段。从RocketMQ消费出数据之后,对MessageExt的数据读出来存到你的这个class对应的对象里头去。
在线时间21小时
回 9楼玄弟的帖子
谢谢~刚发现是MessageExt里面的一个成员变量没有无参构造函数,之前没追看到这一层,谢谢啦!
在线时间6小时
回 10楼sxian的帖子
怎么解决的?那个参数没有无参构造函数???????
访问内容超出本站范围,不能确定是否安全
限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
您目前还是游客,请
验证问题: 26 + 31 = ?
&回复后跳转到最后一页

我要回帖

更多关于 sql server 新增字段 的文章

 

随机推荐