当前位置: 首页 > 知识库问答 >
问题:

Scala中即使是微不足道的序列化示例也不起作用。为什么?

步致远
2023-03-14

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

@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(JavaHotSpot(TM)64位服务器VM,Java1.7.0_51)。键入表达式以计算它们。类型:帮助获取更多信息。

斯卡拉

斯卡拉

斯卡拉

斯卡拉

共有3个答案

印晋
2023-03-14

class-Person(age:Int){}相当于Java代码:

class Person{
    Person(Int age){}
}

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

你想要:

>

@serializable class-Person(val-age:Int)

你可以省略结尾处的空花括号。事实上,这是受到鼓励的。

何昆
2023-03-14

您可以使用可序列化特性:

使用具有可序列化特性的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酸洗序列化

https://github.com/scala/pickling或者从Scala 2.11开始的部分标准发行版

在示例代码中,对象不会写入文件,而是使用JSON而不是字节码序列化,这避免了因不同Scala版本之间字节码不兼容而产生的某些问题。

import scala.pickling._
import json._

case class Person(age: Int)

val person = Person(31)
val pickledPerson = person.pickle
val unpickledPerson = pickledPerson.unpickle[Person]
邹玮
2023-03-14

请注意,@serializablescaladoc告诉我们,它从2.9.0开始就被弃用了:

已弃用(自版本2.9.0起)而不是@serializable class C,请使用class C extends serializable

所以你只需要使用Serializabletrait:

class Person(val age: Int) extends Serializable

这对我有用(在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

这是输出:

// 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测试运行它。scala并得到了与您完全相同的错误。以下是我对其原因的猜测。

根据堆栈跟踪,一个奇怪的类Main$$anon$1不可序列化。合乎逻辑的问题是:它为什么会存在?毕竟,我们试图序列化,而不是奇怪的东西。

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实例方法(对象的类以对象命名,但使用$附加)。此实例方法依次尝试创建类Main$anon1美元的实例。在我的记忆中,匿名类就是这样命名的。

现在,让我们试着找到确切的Person类名(以Scala脚本的形式运行):

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

println(new Person(22).getClass)

这打印了一些我期待的东西:

class Main$$anon$1$Person

这意味着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
    }
  }
}

但在Scala中,所有嵌套类在Java中都被称为“嵌套非静态”(或“内部”)类。这意味着这些类始终包含对其封闭类实例的隐式引用。在本例中,封闭类是Main$$anon$1。正因为如此,当Java serializer尝试序列化Person时,它会临时遇到Main$$anon$1的一个实例,并尝试对其进行序列化,但由于它不可序列化,因此该过程失败。顺便说一句,序列化非静态内部类在Java世界是一件臭名昭著的事情,众所周知,它会导致类似这样的问题。

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

 类似资料:
  • 问题内容: 杰克逊(Jackson)做的事情确实很奇怪,我找不到任何解释。我正在执行多态序列化,当对象独立时它可以完美地工作。但是,如果将同一个对象放入列表中并对其进行序列化,则会删除类型信息。 它丢失类型信息的事实将导致人们怀疑类型擦除。但这是在列表 内容 的序列化过程中发生的。Jackson要做的只是检查要序列化的当前对象以确定其类型。 我使用杰克逊2.5.1创建了一个示例: 这是输出: 如您

  • 我有一个从CSV文件读取的: 据我所知,是一个POJO: 我还有一个简单的课程: 但当我尝试使用它时,例如从测试中: 我得到以下错误: 我读过这些问题和答案,但运气不好: 任务不可序列化Flink 无法在scala中序列化任务 任务不可序列化:java。伊奥。仅在类而非对象上调用闭包外函数时NotSerializableException

  • 问题内容: 为什么以下显示在Linux和Windows中有所不同? 在Windows中: ¿ 在Linux中: 一个 问题答案: System.out.println()以系统默认编码输出文本,但是控制台根据其自身的编码(或“代码页”)设置解释该输出。在Windows机器上,这两种编码似乎匹配,但是在Linux机器上,输出显然是以UTF-8格式输出的,而控制台将其解码为ISO-8859-1之类的单

  • 我正在再次学习一些面向对象的概念,所以我编写了这个简单的代码,只是为了看看重写方法是如何工作的,令人惊讶的是,它不工作了。另外,忽略类名为HelloWorld的事实,它只是一个用于学习目的的文件。它已经改成了Filha。java btw 昨天,错误是线程“main”java中的异常。错误:公共静态void main(String[]args){line中未解决的编译问题,但我关闭了我的计算机,今天

  • 我正试图编写代码,从一个Android应用程序发送一条短信,但当我试图发送短信时,它给我返回了错误: 我检查了,但我拥有清单中的权限,如下所示: null null 我在网上搜索了一下,但是所有的错误都是关于语法的,你能帮我一下吗?

  • 问题内容: 我是Java的新手,正在尝试学习速记语句的概念。 我想出了下面的代码。但是,该代码将无法编译,并在(即?:)语句旁边显示错误。 有人可以告诉我为什么它不起作用吗? 对不起,如果我的问题对某些人听起来很愚蠢。我是Java新手。 在此先感谢您的帮助! 问题答案: 三元表达 是一个 表达式 ,而不是一个语句,因此不能在需要语句的地方使用。 您可以这样写: 因为这是一个声明。