即使Scala中的琐碎序列化示例也不起作用。为什么?

时间:2014-04-20 12:51:36

标签: class scala exception serialization

我正在尝试类的最简单的序列化示例:

@serializable class Person(age:Int) {}
val fred = new Person(45)
import java.io._
val out = new ObjectOutputStream(new FileOutputStream("test.obj"))
out.writeObject(fred)
out.close()

抛出异常" java.io.NotSerializableException:Main $$ anon $ 1 $ Person"包在我身上。为什么? 有简单的序列化示例吗? 我也试过

@serializable class Person(nm:String) {
    private val name:String=nm
}
val fred = new Person("Fred")
...

并尝试删除@serializable和其他一些排列。文件" test.obj"创建,大小超过2Kb,内容合理。

编辑:

阅读" test.obj"回来(从下面的第二个回答)导致

  

欢迎使用Scala版本2.10.3(Java HotSpot(TM)64位服务器VM,   Java 1.7.0_51)。输入表达式以对其进行评估。输入:help   了解更多信息。

     

阶> import java.io._ import java.io ._

     

阶> val fis = new FileInputStream(" test.obj")fis:   java.io.FileInputStream = java.io.FileInputStream@716ad1b3

     

阶> val oin = new ObjectInputStream(fis)oin:   java.io.ObjectInputStream = java.io.ObjectInputStream@1f927f0a

     

阶> val p = oin.readObject java.io.WriteAbortedException:writing   中止; java.io.NotSerializableException:Main $$ anon $ 1           at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)           at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)           at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1915)           at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)           at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)           at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)           在12)           在 。()           7岁时)           在 。()           at $ print()           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           在scala.tools.nsc.interpreter.IMain $ ReadEvalPrint.call(IMain.scala:734)           在scala.tools.nsc.interpreter.IMain $ Request.loadAndRun(IMain.scala:983)           在scala.tools.nsc.interpreter.IMain.loadAndRunReq $ 1(IMain.scala:573)           在scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)           在scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)           在scala.tools.nsc.interpreter.ILoop.reallyInterpret $ 1(ILoop.scala:756)           在scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)           在scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713)           在scala.tools.nsc.interpreter.ILoop.processLine $ 1(ILoop.scala:577)           在scala.tools.nsc.interpreter.ILoop.innerLoop $ 1(ILoop.scala:584)           在scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)           在scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply $ mcZ $ sp(ILoop.scala:878)           在scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply(ILoop.scala:833)           在scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply(ILoop.scala:833)           在scala.tools.nsc.util.ScalaClassLoader $ .savingContextLoader(ScalaClassLoader.scala:135)           在scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)           在scala.tools.nsc.MainGenericRunner.runTarget $ 1(MainGenericRunner.scala:83)           在scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)           在scala.tools.nsc.MainGenericRunner $ .main(MainGenericRunner.scala:105)           在scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)引起   by:java.io.NotSerializableException:Main $$ anon $ 1           at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)           at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)           at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)           at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)           at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)           at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)           在Main $$ anon $ 1.(a.scala:11)           在Main $ .main(a.scala:1)           在Main.main(a.scala)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           在scala.tools.nsc.util.ScalaClassLoader $$ anonfun $ run $ 1.apply(ScalaClassLoader.scala:71)           在scala.tools.nsc.util.ScalaClassLoader $ class.asContext(ScalaClassLoader.scala:31)           在scala.tools.nsc.util.ScalaClassLoader $ URLClassLoader.asContext(ScalaClassLoader.scala:139)           在scala.tools.nsc.util.ScalaClassLoader $ class.run(ScalaClassLoader.scala:71)           在scala.tools.nsc.util.ScalaClassLoader $ URLClassLoader.run(ScalaClassLoader.scala:139)           在scala.tools.nsc.CommonRunner $ class.run(ObjectRunner.scala:28)           在scala.tools.nsc.ObjectRunner $ .run(ObjectRunner.scala:45)           在scala.tools.nsc.CommonRunner $ class.runAndCatch(ObjectRunner.scala:35)           在scala.tools.nsc.ObjectRunner $ .runAndCatch(ObjectRunner.scala:45)           在scala.tools.nsc.ScriptRunner.scala $ tools $ nsc $ ScriptRunner $$ runCompiled(ScriptRunner.scala:171)           在scala.tools.nsc.ScriptRunner $$ anonfun $ runScript $ 1.apply(ScriptRunner.scala:188)           在scala.tools.nsc.ScriptRunner $$ anonfun $ runScript $ 1.apply(ScriptRunner.scala:188)           在scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply $ mcZ $ sp(ScriptRunner.scala:157)           在scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply(ScriptRunner.scala:131)           在scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply(ScriptRunner.scala:131)           在scala.tools.nsc.util.package $ .trackingThreads(package.scala:51)           在scala.tools.nsc.util.package $ .waitingForThreads(package.scala:35)           在scala.tools.nsc.ScriptRunner.withCompiledScript(ScriptRunner.scala:130)           在scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:188)           在scala.tools.nsc.ScriptRunner.runScriptAndCatch(ScriptRunner.scala:201)           在scala.tools.nsc.MainGenericRunner.runTarget $ 1(MainGenericRunner.scala:76)           ......还有3个

3 个答案:

答案 0 :(得分:29)

请注意,@serializable scaladoc告诉它自2.9.0以来已弃用:

  

不推荐使用(从版本2.9.0开始)而不是@serializable类C,使用类C extends Serializable

所以你只需要使用Serializable特质:

class Person(val age: Int) extends Serializable

这适用于我(在REPL中键入:paste并粘贴这些行):

import java.io.{ObjectOutputStream, ObjectInputStream}
import java.io.{FileOutputStream, FileInputStream}

class Person(val age: Int) extends Serializable {
  override def toString = s"Person($age)"
}

val os = new ObjectOutputStream(new FileOutputStream("/tmp/example.dat"))
os.writeObject(new Person(22))
os.close()

val is = new ObjectInputStream(new FileInputStream("/tmp/example.dat"))
val obj = is.readObject()
is.close()
obj

这是输出:

// Exiting paste mode, now interpreting.

import java.io.{ObjectOutputStream, ObjectInputStream}
import java.io.{FileOutputStream, FileInputStream}
defined class Person
os: java.io.ObjectOutputStream = java.io.ObjectOutputStream@5126abfd
is: java.io.ObjectInputStream = java.io.ObjectInputStream@41e598aa
obj: Object = Person(22)
res8: Object = Person(22)

所以,你可以看到,[de]序列化尝试成功了。

编辑(关于您从文件运行Scala脚本时获得NotSerializableException的原因)

我已将我的代码放入文件并尝试通过scala test.scala运行它并得到与您完全相同的错误。以下是我对其发生原因的猜测。

根据堆栈跟踪,一个奇怪的类Main$$anon$1不可序列化。逻辑问题是:为什么它首先出现在那里?我们毕竟试图序列化Person,而不是奇怪的事情。

Scala脚本的特殊之处在于它隐式包装在名为Main的对象中。这由堆栈跟踪表示:

at Main$$anon$1.<init>(test.scala:9)
at Main$.main(test.scala:1)
at Main.main(test.scala)

这里的名称表明Main.main静态方法是程序入口点,并且此方法委托给Main$.main实例方法(object的类以对象命名但是{附加{1}}。此实例方法依次尝试创建类$的实例。据我所知,匿名类是这样命名的。

现在,让我们尝试找到准确的Main$$anon$1类名称(将其作为Scala脚本运行):

Person

这打印出我期待的东西:

class Person(val age: Int) extends Serializable {
  override def toString = s"Person($age)"
}

println(new Person(22).getClass)

这意味着class Main$$anon$1$Person 不是顶级课程;相反,它是由编译器生成的匿名类中定义的嵌套类!所以事实上我们有类似的东西:

Person

但是在Scala中,所有嵌套类都是Java中的“嵌套非静态”(或“内部”)类。这意味着这些类始终包含对其封闭类的实例的隐式引用。在这种情况下,封闭类是object Main { def main(args: Array[String]) { new { // this is where Main$$anon$1 is generated, and the following code is its constructor body class Person(val age: Int) extends Serializable { ... } // all other definitions } } } 。因此,当Java序列化程序尝试序列化Main$$anon$1时,它会传递地遇到Person的实例并尝试序列化它,但由于它不是Main$$anon$1,因此该过程失败。顺便说一下,序列化非静态内部类是Java世界中一个臭名昭着的东西,众所周知会引起像这样的问题。

至于为什么它在REPL中工作,似乎在REPL声明的类中不会以某种方式结束为内部的,因此它们没有任何隐式字段。因此序列化对他们来说是正常的。

答案 1 :(得分:4)

您可以使用Serializable Trait:

使用带有Seri​​alizable Trait的Java序列化的简单序列化示例:

case class Person(age: Int) extends Serializable

用法:

序列化,写对象

val fos = new FileOutputStream( "person.serializedObject" )
val o = new ObjectOutputStream( fos )
o writeObject Person(31)

反序列化,读取对象

val fis = new FileInputStream( "person.serializedObject" )
val oin = new ObjectInputStream( fis )
val p= oin.readObject

创建以下输出

fis: java.io.FileInputStream = java.io.FileInputStream@43a2bc95
oin: java.io.ObjectInputStream = java.io.ObjectInputStream@710afce3
p: Object = Person(31)

如您所见,反序列化无法推断出对象类型本身,这是一个明显的缺点。

使用Scala-Pickling进行序列化

https://github.com/scala/pickling或标准分发的一部分,以Scala 2.11开头

在exmple代码中,对象不会写入文件,而是使用JSON而不是ByteCode Serialization,这可以避免源自不同Scala版本之间字节代码不兼容的某些问题。

import scala.pickling._
import json._

case class Person(age: Int)

val person = Person(31)
val pickledPerson = person.pickle
val unpickledPerson = pickledPerson.unpickle[Person]

答案 2 :(得分:1)

class Person(age:Int) {}等同于Java代码:

class Person{
    Person(Int age){}
}

这可能不是你想要的。请注意,参数age只是被丢弃,Person没有成员字段。

你想要:

  1. @serializable case class Person(age:Int)

  2. @serializable class Person(val age:Int)

  3. 您可以在末尾省略空的花括号。事实上,它受到了鼓励。