解決錯誤java.io.InvalidClassException


今天遇到一個現場問題,任務報錯java.io.InvalidClassException。在開發環境是沒有報錯的,正式環境報錯。大概類似於下面這樣(非報錯原文,摘自網上同類博客)

java.io.InvalidClassException: com.test.Test; local class incompatible: stream classdesc serialVersionUID = 7981560250804078237, local class serialVersionUID = -8334405535134160822

在網上查找資料后了解到 :
該異常是由於反序列化時, 當前類的serialVersionUID 與 反序列化后的類的serialVersionUID 不同所致。在未顯示聲明情況下由java編譯器計算得出,但是不同的java編譯器可能計算出的結果不同(本次出現該問題的直接原因是spark各個節點接收到的是sparksql  jar包不一致),因此強烈推薦顯式聲明serialVersionUID 。

聲明方法如下:

 
         
@SerialVersionUID(1234567890L) class xxx() extends yyy with Serializable {
  ......
}

問題補充:

1、如何查看類的serialVersionUID,

 針對java的話,可以使用 serialver命令。  首先解壓想查詢類所在jar包,然后cd至解壓目錄,serialver 完整類名  ,即可得到serialVersionUID

 針對Scala  不推薦使用serialver,使用serialver時,不包含scala標准庫,會報錯找不到scala Serializable類,建議直接使用代碼查詢

   下面提供了一個例子讀取類中的serialVersionUID

@SerialVersionUID(1l) case class IdentifyMessage1(userName: String,  code: Int)
@SerialVersionUID(12l) case class IdentifyMessage2(userName: String,  code: Int)
object SerializationClass {
  def main(args: Array[String]): Unit = {
    println("#1 " + java.io.ObjectStreamClass.lookup(IdentifyMessage1("hei", 7).getClass).getSerialVersionUID)
    println("#2 " + java.io.ObjectStreamClass.lookup(IdentifyMessage2("hei", 8).getClass).getSerialVersionUID)
    println("#3 " +com.sun.corba.se.impl.io.ObjectStreamClass.ObjectStreamClass.getActualSerialVersionUID(IdentifyMessage1("hei", 8).getClass)) 
println(
"#4 " +
com.sun.corba.se.impl.io.ObjectStreamClass.ObjectStreamClass.getSerialVersionUID((IdentifyMessage1("hei", 8).getClass)))
  }
}

 

不過雖然這個例子是我寫的,但是我也還有沒弄懂的地方,本例子在本地單機環境下運行是不會更改SerialVersionUID 的。這個問題如果有大神知道的話,歡迎留言

但是在spark里面是可以生效的。(兩者java版本一直)

 

 

附上關於Serializable接口的說明

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;
   
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members. Array classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM