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

MKa*_*ama 11 serialization scala exception class

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

@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()
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑:

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

欢迎使用Scala版本2.10.3(Java HotSpot(TM)64位服务器VM,Java 1.7.0_51).输入表达式以对其进行评估.键入:帮助以获取更多信息.

scala> import java.io._ import java.io._

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

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

scala> val p = oin.readObject java.io.WriteAbortedException:write aborted; java.io.NotSerializableException:主要$$匿名$ 1日java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1354)在java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1990)在java.io.ObjectInputStream.readSerialData( ObjectInputStream.java:1915)在java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)在java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)在java.io.ObjectInputStream.readObject(ObjectInputStream.java:370 )在(:12)在()中的(:7)在()处sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java $打印(). :57)在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)在java.lang.reflect.Method.invoke(Method.java:606)在scala.tools.nsc.interpreter.IMain $ ReadEvalPrint.call( IMain.scala:734)at scala.tools.nsc.interpreter.IMain $ Request.loadAndRun(IMain.scala:983)at scala.tools.nsc.interpreter.IMain.loadAn dRunReq $ 1(IMain.scala:573)位于scala的scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568). tools.nsc.interpreter.ILoop.reallyInterpret $ 1(ILoop.scala:756)at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)at 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的scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply $ mcZ $ sp(ILoop.scala:878). interpreter.ILoop $$ anonfun $ process $ 1.apply(ILoop.scala:833)at scala.tools.nsc.interpreter.ILoop $$ anonfun $ process $ 1.apply(ILoop.scala:833)at scala.tools.nsc. util.ScalaClassLoader $ .savingContextLoader(ScalaClassLoader.scala:135)在scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)在scala.tools.nsc.MainGenericRunner.runTarget scala.tools.nsc上的scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)scala.tools.nsc.MainGenericRunner $ .main(MainGenericRunner.scala:105)$ 1(MainGenericRunner.scala:83). MainGenericRunner.main(MainGenericRunner.scala)引起:java.io.NotSerializableException:java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)的Main $$ anon $ 1 1547)在java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)在java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)在java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)在java的. io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)在Main $$ anon $ 1.(a.scala:11)位于Main.main(a.scala:1)的Main $ .main(a.scala:1)位于sun. sun.reflect.DelegatingMe的sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)中的reflect.NativeMethodAccessorImpl.invoke0(Native Method)thodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:606)at 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)at scala.tools.nsc.util.ScalaClassLoader $ URLClassLoader.run(ScalaClassLoader.scala:139)at scala.tools.nsc.CommonRunner $ class.run(ObjectRunner) .scala:28)在scala.tools.nsc.ObjectRunner $ .RUN(ObjectRunner.scala:在scala.tools.nsc.CommonRunner $ class.runAndCatch 45)(ObjectRunner.scala:35)在scala.tools.nsc.ObjectRunner $ .runAndCatch(ObjectRunner.scala:45)在scala.tools.nsc.ScriptRunner.scala $工具$ NSC $ ScriptRunner $$ runCompiled(ScriptRunner.scala:171)在scala.tools.nsc.ScriptRunner $$ anonfun $ $的runScript 1在sca .apply(ScriptRunner.scala:188)scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.apply $ mcZ $ sp(ScriptRunner.scala:157)at la.tools.nsc.ScriptRunner $$ anonfun $ runScript $ 1.apply(ScriptRunner.scala:188) scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.适用(ScriptRunner.scala:131)在scala.tools.nsc.ScriptRunner $$ anonfun $ withCompiledScript $ 1.适用(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.runScriptAndCatch(ScriptRunner.scala:201)的scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:188)scala.tools.nsc.MainGenericRunner.runTarget $ 1(MainGenericRunner.斯卡拉:76)......还有3个

Vla*_*eev 30

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

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

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

class Person(val age: Int) extends Serializable
Run Code Online (Sandbox Code Playgroud)

这对我:paste有用(输入REPL并粘贴这些行):

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
Run Code Online (Sandbox Code Playgroud)

这是输出:

// 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)
Run Code Online (Sandbox Code Playgroud)

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

编辑(NotSerializableException从文件运行Scala脚本时获取的原因)

我已经将我的代码放入一个文件并试图通过它运行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)
Run Code Online (Sandbox Code Playgroud)

这里的名称表明Main.main静态方法是程序入口点,并且此方法委托给Main$.main实例方法(object该类以对象命名,但带有$追加).此实例方法依次尝试创建类的实例Main$$anon$1.据我所知,匿名类是以这种方式命名的.

现在,让我们尝试找到确切的Person类名(将其作为Scala脚本运行):

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

println(new Person(22).getClass)
Run Code Online (Sandbox Code Playgroud)

这打印出我期待的东西:

class Main$$anon$1$Person
Run Code Online (Sandbox Code Playgroud)

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

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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是在Scala中,所有嵌套类都是Java中的"嵌套非静态"(或"内部")类.这意味着这些类总是包含对其封闭类的实例的隐式引用.在这种情况下,封闭类是Main$$anon$1.因此,当Java序列化程序尝试序列化时Person,它会传递地遇到一个实例Main$$anon$1并尝试序列化它,但由于它不是Serializable,因此该过程失败.顺便说一下,序列化非静态内部类是Java世界中一个臭名昭着的东西,众所周知会引起像这样的问题.

至于为什么它在REPL中工作,似乎在REPL声明的类中某种程度上不会最终成为内部类,因此它们没有任何隐含字段.因此序列化通常适用于它们.