代码之家  ›  专栏  ›  技术社区  ›  Igor Zelaya

是否有方法将可序列化对象读/写到RandomaccesFile?

  •  2
  • Igor Zelaya  · 技术社区  · 16 年前

    如何读取/写入可序列化的对象实例到Java中的Access Access文件?我希望能够做到这一点,就像你在C++中通过结构实现的一样。在Java中,只有ObjistIsPosivs/ObjutOutsStutsSCAN可以读取/写入对象。我很惊讶Java没有已经实现的东西。

    3 回复  |  直到 16 年前
        1
  •  4
  •   Community CDub    7 年前

    我可以理解为什么要这样做来读取旧文件格式。在这种情况下,默认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;
    }
    

    handler类;可以处理基元类型和字节数组,但不能处理其他内容:

    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 并使用数据库。

        2
  •  5
  •   Peter Lawrey    16 年前

    通过objectOutputstream将数据写入bytearrayOutputstream,然后可以将byte[]放入随机访问文件中。你也可以用同样的方法做相反的事情。

    但是,你为什么要这么做?我怀疑有一些产品可以为您这样做,例如可以持久保存到磁盘的缓存。在这个原因中,你可以有一个地图,你可以把对象放进去,而图书馆负责剩下的。

        3
  •  4
  •   waqas    16 年前

    序列化的Java对象之间存在区别,而用FWORD编写的C结构:序列化的Java对象可能没有固定大小的字节。

    我很惊讶Java没有 一些已经实现的东西。

    当然可以。Java版本的新版本 Java DB 一个嵌入式数据库,它可以满足您的需要,还有更多。 (请注意,这种类型的数据库基本上是随机访问文件,用于字符串和数字等数据类型,而不是字节)。这样做的缺点是您必须编写一些JDBC样板文件。

    如果你不介意包括外部依赖,对象数据库(比如 db4o )非常有趣,只需要最少的代码。