错误方式
@Test public void testDeserializeTest() throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); BigInteger bi = new BigInteger("0"); oos.writeObject(bi); String str = baos.toString(); System.out.println(str); ObjectInputStream ois = new ObjectInputStream( new BufferedInputStream(new ByteArrayInputStream(str.getBytes()))); Object obj = ois.readObject(); assertEquals(obj.getClass().getName(), "java.math.BigInteger"); assertEquals(((BigInteger) obj).intValue(), 0); }
@Test public void testDeserialize() throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); BigInteger bi = new BigInteger("0"); oos.writeObject(bi); byte[] str = baos.toByteArray(); ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(str))); Object obj = ois.readObject(); assertNotNull(obj); assertEquals(obj.getClass().getName(), "java.math.BigInteger"); assertEquals(((BigInteger) obj).intValue(), 0); }
原因是由于:
将字 ByteArrayOutputStream对象调用为toString转为为字符串时,会将 ObjectOutputStream对象放置在对象流头部的前两个字节(0xac)(0xed)序列化为两个“?”
当这个字符串使用getByte()时会将两个“?”变为(0x3f )(0x3f) 。然而这两个字符并不构成有效的对象流头。所以转化对象时候会失败。
测试代码 单元测试无法输出结果这里用main测试
1 public static void main(String[] args) throws IOException { 2 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 3 ObjectOutputStream oos = new ObjectOutputStream(baos); 4 String s = "111"; 5 oos.writeObject(s); 6 String str = baos.toString(); 7 byte[] baStr = baos.toByteArray(); 8 byte[] gbStr = str.getBytes(); 9 byte[] testStr = baStr; 10 StringBuffer sb = new StringBuffer(); 11 for (int i = 0; i < testStr.length; i++) { 12 sb.append(Integer.toBinaryString(testStr[i]) + " "); 13 } 14 System.out.println(sb.toString()); 15 }
1.如果将6行的str直接打印在页面上 则显示如下结果
2.将第9行赋予baStr 则得到的二进制首两位值为
11111111111111111111111110101100(0xac) 11111111111111111111111111101101(0xed)
3.将第9行赋予gbStr 则得到的二进制首两位值为
11111111111111111111111111101111(0xef) 11111111111111111111111110111111(0xbf)
(由于字符集和英文原作者不一样所以解析出来的结果也不一样)
发现字符被改变了以至于ObjectOutputStream无法识别该字符数组所以抛出了java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
所以笔者建议:
1.使用 toByteArray()代替toString() ,使用 ByteArrayInputStream(byte [])构造函数。
2.使用base64转换为字符串
注:LZ出现这个问题是因为在maven打包项目的时候重新编译了项目中的二进制文件从而破坏了二进制文件的完整性。所以该文件无法反序列化。
附:maven打包跳过二进制文件
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <!-- 防止二进制文件被编译 --> <nonFilteredFileExtensions> <nonFilteredFileExtension>dat</nonFilteredFileExtension> <nonFilteredFileExtension>swf</nonFilteredFileExtension> <nonFilteredFileExtension>xml</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin> </plugins>
英文原文:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4968673
The provided test code serializes an object to a ByteArrayOutputStream, converts the generated byte array into a string using the ByteArrayOutputStream.toString() method, converts the string back into a byte array using the String.getBytes() method, and then attempts to deserialize the object from the byte array using a ByteArrayInputStream. This procedure will in most cases fail because of the transformations that take place within ByteArrayOutputStream.toString() and String.getBytes(): in order to convert the contained sequence of bytes into a string, ByteArrayOutputStream.toString() decodes the bytes according to the default charset in effect; similarly, in order to convert the string back into a sequence of bytes, String.getBytes() encodes the characters according to the default charset. Converting bytes into characters and back again according to a given charset is generally not an identity-preserving operation. As the javadoc for the String(byte[], int, int) constructor (which is called by ByteArrayOutputStream.toString()) states, "the behavior ... when the given bytes are not valid in the default charset is unspecified". In the test case provided, the first two bytes of the serialization stream, 0xac and 0xed (see java.io.ObjectStreamConstants.STREAM_MAGIC), both get mapped to the character '?' since they are not valid in the default charset (ISO646-US in the JDK I'm running). The two '?' characters are then mapped back to the byte sequence 0x3f 0x3f in the reconstructed data stream, which do not constitute a valid header. The solution, from the perspective of the test case, is to use ByteArrayOutputStream.toByteArray() instead of toString(), which will yield the raw byte sequence; this can then be fed directly to the ByteArrayInputStream(byte[]) constructor.
原文地址 http://blog.sina.com.cn/s/blog_61f4999d0100yi89.html