Java序列化 - 处理瞬态对象时的异常

时间:2012-11-01 15:05:09

标签: java serialization

更新

感谢大家的大力帮助,在这里我必须为这个例外添加一个可能的原因:

如果您在域类中添加readObject()writeObject()(如下面的类大学),并且您还必须在main()方法中调用ObjectOutputStream.writeObject()方法,请确保:< / p>

---不要在域类中放置ObjectOutputStream.close()方法。它会导致异常,因为对它进行操作是非法的,因为它已关闭(程序如何在文件已经关闭后读取文件或关闭文件?)。

我知道这是一个小问题,但调试非常微妙,因为异常不会告诉它任何事情。所以这是一个有点想要记录。


我尝试实现一个简单的片段来破坏瞬态对象的序列化,它将序列化该瞬态对象的原始字段,然后通过ObjectInputStream对这些原始字段进行反序列化,最后用它们组成一个新对象。场景是这样的:

public class College implements Serializable{
    private static final long serialVersionUID = 1L;

    private String name;
    private transient City city; //transient
    private String zipCode;

    // constructors & getter/setters

private void writeObject(ObjectOutputStream os){
    try {
        os.defaultWriteObject();
        os.writeInt(city.getCode());
        os.writeInt(city.getPopulation());
        os.writeObject(city.getName());
        os.flush();
        os.close(); // update: not good practice, may throw exception.
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private void readObject(ObjectInputStream os){
    try {
        os.defaultReadObject();
        int code = os.readInt();
        int population = os.readInt();
        String name = (String)os.readObject();
        City theCity = new City(code, name, population);
        System.out.println(theCity.toString());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}
}

这是City类

public class City {
    // the primitive & String fields
    private int code;
    private String name;
    private int population;

    // getter/setters

}

这是编写和阅读college对象的可爱代码(有点粗略,对不起)

public class GeneralTest {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    City city = new City(375, "New York", 380897);
    College college = new College("NYU", city, "10289");
    College readCollege = null;
    City readCity = null;

    System.out.println("Before serialization -- City: ["+city.toString()+"], College: ["+college.toString()+"]");

    try {
        FileOutputStream fs = new FileOutputStream("college.foo");
        ObjectOutputStream os = new ObjectOutputStream(fs);
        os.writeObject(college);
        os.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        FileInputStream fis = new FileInputStream("college.foo");
        ObjectInputStream ois = new ObjectInputStream(fis);
        readCollege = (College)ois.readObject();

        //get values to compose a city object
        int id = ois.readInt();
        int population = ois.readInt();
        String name = ois.readUTF();
        readCity = new City(id, name, population);

        ois.close();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("After serialization -- City: ["+readCity.toString()+"], College: ["+readCollege.toString()+"]");
}

}

然后我得到了这个例外,而readCollegereadCity显然是空的:(

java.io.IOException: Write error
    at java.io.FileOutputStream.writeBytes(Native Method)
    at java.io.FileOutputStream.write(FileOutputStream.java:282)
    at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1847)
    at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1756)
    at java.io.ObjectOutputStream.writeNonProxyDesc(ObjectOutputStream.java:1257)
    at java.io.ObjectOutputStream.writeClassDesc(ObjectOutputStream.java:1211)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1395)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
    at java.io.ObjectOutputStream.writeFatalException(ObjectOutputStream.java:1547)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:333)
    at serialized.GeneralTest.main(GeneralTest.java:27)
java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.readByte(ObjectInputStream.java:2721)
    at java.io.ObjectInputStream$BlockDataInputStream.readUTFChar(ObjectInputStream.java:3113)
    at java.io.ObjectInputStream$BlockDataInputStream.readUTFBody(ObjectInputStream.java:3010)
    at java.io.ObjectInputStream$BlockDataInputStream.readUTF(ObjectInputStream.java:2819)
    at java.io.ObjectInputStream.readUTF(ObjectInputStream.java:1050)
    at serialized.College.readObject(College.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:969)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at serialized.GeneralTest.main(GeneralTest.java:40)
java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2553)
    at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1899)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1873)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at serialized.GeneralTest.main(GeneralTest.java:40)
Exception in thread "main" java.lang.NullPointerException
    at serialized.GeneralTest.main(GeneralTest.java:52)

有什么想法吗?提前谢谢。

3 个答案:

答案 0 :(得分:1)

以下是您输出的代码部分

    FileOutputStream fs = new FileOutputStream("college.foo");
    ObjectOutputStream os = new ObjectOutputStream(fs);
    os.writeObject(college);
    os.close();

您创建了自己的流,编写了college对象,然后关闭它。

这是你正在阅读的部分:

    FileInputStream fis = new FileInputStream("college.foo");
    ObjectInputStream ois = new ObjectInputStream(fis);
    readCollege = (College)ois.readObject();

此时,您已经阅读了所写的所有内容。你之前写过大学,现在你读了它。

然后你去做:

    //get values to compose a city object
    int id = ois.readInt();
    int population = ois.readInt();
    String name = ois.readUTF();
    readCity = new City(id, name, population);

你怎么能在这里读书?你只写了college。然后你读回college(进入readCollege引用。然后你试图读取另外两个整数和一个字符串?那些数据应该来自哪里?没有什么可读的。< / p>

答案 1 :(得分:1)

用ois.readObject()替换ois.readUTF()并将其强制转换为String。

String name = (String) ois.readObject();

没有编译/尝试过......

答案 2 :(得分:1)

执行此行时:

readCollege = (College)ois.readObject();

之后流中没有任何东西了。

所以你的未来尝试阅读相同的流会抛出逻辑java.io.EOFException: (此外,请注意readUTF()替换readObject()

//get values to compose a city object
        int id = ois.readInt();   // there's nothing more !! throws exception!
        int zip = ois.readInt();
        String name = (String)ois.readObject();

实际上,在写入流时,您尚未添加自定义值。

所以,一个解决方案示例,添加三行专用于添加City所需的元素:

try {
        FileOutputStream fs = new FileOutputStream("college.foo");
        ObjectOutputStream os = new ObjectOutputStream(fs);
        os.writeObject(college);
        os.writeInt(1); //adding the id !
        os.writeInt(75019); //adding the zip !
        os.writeObject("name"); //adding the name !
        os.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

当然,阅读字段的顺序必须是写作期间的相同

在您的评论后更新:

事实上,您已经通过City方法在College课程中阅读了readObject()的自定义值,因此这些行无用:

int id = ois.readInt();
int population = ois.readInt();
String name = (String)ois.readObject();   // remove them all

为什么???因为他们只是抛出java.io.EOFException,因为我之前解释过没有其他内容可读。

这是一个有效的全球代码:

class College implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private transient City city; //transient
    private String zipCode;

    College(String name, City city, String zipCode) {
        this.name = name;
        this.city = city;
        this.zipCode = zipCode;
    }

    private void readObject(ObjectInputStream os) {
        try {
            os.defaultReadObject();
            int code = os.readInt();
            int population = os.readInt();
            String name = (String) os.readObject();
            this.city = new City(code, name, population);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void writeObject(ObjectOutputStream os) {
        try {
            os.defaultWriteObject();
            os.writeInt(1);
            os.writeInt(200);
            os.writeObject("thename");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }
}

class City {
    // the primitive & String fields
    private int code;
    private String name;
    private int population;

    City(int code, String name, int population) {
        this.code = code;
        this.name = name;
        this.population = population;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    // getter/setters
}

public class Launcher {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        City city = new City(375, "New York", 380897);
        College college = new College("NYU", city, "10289");
        College readCollege = null;
        City readCity = null;

        System.out.println("Before serialization -- City: [" + city.toString() + "], College: [" + college.toString() + "]");

        try {
            FileOutputStream fs = new FileOutputStream("college.foo");
            ObjectOutputStream os = new ObjectOutputStream(fs);
            os.writeObject(college);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            FileInputStream fis = new FileInputStream("college.foo");
            ObjectInputStream ois = new ObjectInputStream(fis);
            readCollege = (College) ois.readObject();
            readCity = readCollege.getCity();

            ois.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("After serialization -- City: [" + readCity.toString() + "], College: [" + readCollege.toString() + "]");
    }

}