有没有办法将可序列化对象读/写到RandomAccessFile?

时间:2009-01-24 06:49:52

标签: java serialization

如何在Java中读取/编写可序列化对象实例到RandomAccessFile?我想能够像在c ++中通过结构一样做到这一点。在java中只有ObjectInputStreams / ObjectOutputStreamscan可以读/写对象。我很惊讶Java没有已经实现的东西。

3 个答案:

答案 0 :(得分:5)

通过ObjectOutputStream将数据写入ByteArrayOutputStream,然后将byte []放入随机访问文件中。你可以用同样的方式做反向。

但是,你为什么要这样做呢?我怀疑有些产品会为你做这些,比如可以持久保存到磁盘的缓存。在这个原因中,您可以只使用一个Map来放置对象,而库可以处理剩下的事情。

答案 1 :(得分:4)

序列化Java对象与使用fwrite写出的C结构之间存在差异:序列化java对象可能没有固定大小(以字节为单位)。

  

我很惊讶Java没有   已经实施的东西。

确实如此。较新版本的Java附带Java DB,这是一个嵌入式数据库,可以满足您的需求,而且可以提供更多功能。 (请注意,这种类型的数据库基本上是随机访问文件,它们处理数据类型,如字符串和数字而不是字节)。这有一个缺点,你必须编写一些JDBC样板。

如果您不介意包含外部依赖项,那么对象数据库(如db4o)非常有趣并且需要最少的代码。

答案 2 :(得分:4)

我可以看到你为什么要这样做来阅读遗留文件格式。在这种情况下,默认的Java序列化机制是一个障碍,而不是帮助。在某种程度上,你可以使用反射来读/写类似结构的类。

示例代码:

public static class MyStruct {
    public int foo;
    public boolean bar = true;
    public final byte[] byteArray = new byte[3];
}

public static void main(String[] args) throws IOException {
    LegacyFileHandler handler = new LegacyFileHandler();
    MyStruct struct = new MyStruct();

    RandomAccessFile file = new RandomAccessFile("foo", "rw");
    try {
        for (int i = 0; i < 4; i++) {
            struct.foo = i;
            handler.write(file, struct);
        }

        struct = readRecord(file, handler, 2);
        System.out.println(struct.foo);
    } finally {
        file.close();
    }
}

private static MyStruct readRecord(RandomAccessFile file,
        LegacyFileHandler handler, int n) throws IOException {
    MyStruct struct = new MyStruct();

    long pos = n * handler.sizeOf(struct);
    file.seek(pos);

    handler.read(file, struct);

    return struct;
}

处理程序类;可以处理原始类型和字节数组,但没有别的:

public class LegacyFileHandler {

    private final Map<Class<?>, Method> readMethods = createReadMethodMap();

    private final Map<Class<?>, Method> writeMethods = createWriteMethodMap();

    private Map<Class<?>, Method> createReadMethodMap() {
        Class<DataInput> clazz = DataInput.class;
        Class<?>[] noparams = {};
        try {
            Map<Class<?>, Method> map = new HashMap<Class<?>, Method>();
            map.put(Boolean.TYPE, clazz.getMethod("readBoolean", noparams));
            map.put(Byte.TYPE, clazz.getMethod("readByte", noparams));
            map.put(Character.TYPE, clazz.getMethod("readChar", noparams));
            map.put(Double.TYPE, clazz.getMethod("readDouble", noparams));
            map.put(Float.TYPE, clazz.getMethod("readFloat", noparams));
            map.put(Integer.TYPE, clazz.getMethod("readInt", noparams));
            map.put(Long.TYPE, clazz.getMethod("readLong", noparams));
            map.put(Short.TYPE, clazz.getMethod("readShort", noparams));
            return map;
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException(e);
        }
    }

    private Map<Class<?>, Method> createWriteMethodMap() {
        Class<DataOutput> clazz = DataOutput.class;
        try {
            Map<Class<?>, Method> map = new HashMap<Class<?>, Method>();
            map.put(Boolean.TYPE, clazz.getMethod("writeBoolean",
                    new Class[] { Boolean.TYPE }));
            map.put(Byte.TYPE, clazz.getMethod("writeByte",
                    new Class[] { Integer.TYPE }));
            map.put(Character.TYPE, clazz.getMethod("writeChar",
                    new Class[] { Integer.TYPE }));
            map.put(Double.TYPE, clazz.getMethod("writeDouble",
                    new Class[] { Double.TYPE }));
            map.put(Float.TYPE, clazz.getMethod("writeFloat",
                    new Class[] { Float.TYPE }));
            map.put(Integer.TYPE, clazz.getMethod("writeInt",
                    new Class[] { Integer.TYPE }));
            map.put(Long.TYPE, clazz.getMethod("writeLong",
                    new Class[] { Long.TYPE }));
            map.put(Short.TYPE, clazz.getMethod("writeShort",
                    new Class[] { Integer.TYPE }));
            return map;
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException(e);
        }
    }

    public int sizeOf(Object struct) throws IOException {
        class ByteCounter extends OutputStream {
            int count = 0;

            @Override
            public void write(int b) throws IOException {
                count++;
            }
        }

        ByteCounter counter = new ByteCounter();
        DataOutputStream dos = new DataOutputStream(counter);
        write(dos, struct);
        dos.close();
        counter.close();
        return counter.count;
    }

    public void write(DataOutput dataOutput, Object struct) throws IOException {
        try {
            Class<?> clazz = struct.getClass();
            for (Field field : clazz.getFields()) {
                Class<?> type = field.getType();

                if (type == byte[].class) {
                    byte[] barray = (byte[]) field.get(struct);
                    dataOutput.write(barray);
                    continue;
                }

                Method method = writeMethods.get(type);
                if (method != null) {
                    method.invoke(dataOutput, field.get(struct));
                    continue;
                }

                throw new IllegalArgumentException("Type "
                        + struct.getClass().getName()
                        + " contains unsupported field type " + type.getName()
                        + " (" + field.getName() + ")");
            }
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException(e);
        }
    }

    public void read(DataInput dataInput, Object struct) throws IOException {
        try {
            Class<?> clazz = struct.getClass();
            Object[] noargs = {};
            for (Field field : clazz.getFields()) {
                Class<?> type = field.getType();

                if (type == byte[].class) {
                    byte[] barray = (byte[]) field.get(struct);
                    dataInput.readFully(barray);
                    continue;
                }

                Method method = readMethods.get(type);
                if (method != null) {
                    Object value = method.invoke(dataInput, noargs);
                    field.set(struct, value);
                    continue;
                }

                throw new IllegalArgumentException("Type "
                        + struct.getClass().getName()
                        + " contains unsupported field type " + type.getName()
                        + " (" + field.getName() + ")");
            }
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException(e);
        }
    }

}

此代码仅经过粗略测试。

尝试概括这样的读/写数据存在问题。

  • 处理任何对象类型都很棘手,因为您必须自己将它们转换为字节数组并确保它们保持固定大小。
  • 最大的问题可能是保持所有字符串的正确长度 - 你需要了解unicode以了解字符数和编码字节大小之间的关系。
  • Java使用网络字节顺序,并没有真正的无符号类型。如果您正在阅读遗留数据,则可能需要进行一些操作。

如果我需要处理二进制格式,我可能会将这些功能封装在专门的类中,这些类能够在I / O源上写入/读取它们的状态。您仍然会遇到上述列表中的问题,但这是一种更加面向对象的方法。

如果我有自由选择文件格式,我会选择waqas suggested并使用数据库。