有没有直接的方法来计算原始字段的大小?

时间:2016-07-11 08:21:44

标签: java serialization reflection primitive-types

我正在编写一个小的序列化实用程序,我想计算将一个对象写入字节数组需要保留多少字节。 让我们说我有一个像

这样的字段/ getter的课程
int age;
String name;
Colour colour;
boolean active;
...

我认为我可以使用反射来迭代字段并查看它们的大小,但我能做的就是检查它们是否是原始的。这是一个开始,因为我会使用short作为Objects的偏移量。好的,我可以用

if(field.getType() == int.class) {
    size = Integer.BYTES;
}

或使用地图。它不会那么难,因为我只需要知道原语的大小。但出于好奇:有没有直接的方式?

编辑澄清: 我正在使用它来序列化数据,基本上就像DataOutput / InputStream一样,但我自己想要一些调整。

API类如下所示:

public final void write(int i) {
    checkBounds(Integer.BYTES);
    PrimitiveWriteUtil.write(i, data, marker);
    advance(Integer.BYTES);
}

实际工作由实用程序类完成:

public static void write(int i, byte[] target, int pos) {
    target[pos] =       (byte) ((i >>> 24) & 0xFF);
    target[pos + 1] =   (byte) ((i >>> 16) & 0xFF);
    target[pos + 2] =   (byte) ((i >>>  8) & 0xFF);
    target[pos + 3] =   (byte) ((i >>>  0) & 0xFF);
}

所以,我想知道我的字节数组所需的大小,所以我可以避免以后检查和调整大小的工作。

3 个答案:

答案 0 :(得分:1)

基元的大小为defined in JLS 4.2

byte为1个字节,shortchar为2,intfloat为32,double和{{1}是64。

答案 1 :(得分:1)

嗯,你已经知道了BYTES字段,问题是,这些字段是在相应的包装器类型中声明的,并且没有简单的方法来获取基本类型的包装器类型。在代码大小方面,获取它的最简单方法是通过get方法读取字段,这意味着包含原始值:

public class FieldSize {
    static final int BOOLEAN_SIZE = 1; // adjust to your storage method
    static final int REF_SIZE = 2;     // dito
    private static int getSize(Field f, Object o) {
        try {
        return f.getType().isPrimitive()? f.getType()==boolean.class? BOOLEAN_SIZE:
               f.get(o).getClass().getField("BYTES").getInt(null): REF_SIZE;
        } catch(ReflectiveOperationException ex) {
            throw new AssertionError(ex);
        }
    }
    boolean z;
    byte b;
    short s;
    char c;
    int i;
    long l;
    float f;
    double d;
    String str;

    public static void main(String[] args) {
        System.out.println("total: "+calculateSize(new FieldSize())+" size");
    }
    static int calculateSize(Object o) {
        int total=0;
        for(Class cl=o.getClass(); cl!=null; cl=cl.getSuperclass()) {
            for(Field f: cl.getDeclaredFields()) {
                if(Modifier.isStatic(f.getModifiers())) continue;
                int size=getSize(f, o);
                System.out.println(f.getName()+" ("+f.getType()+"): "+size);
                total+=size;
            }
        }
        return total;
    }
}

然而,就底层操作而言,它不是最简单的解决方案,如果您不喜欢它,我完全理解。

在这种情况下,答案是没有简单/直接的方法来获取这些数字,所以要么这样做,从类型到数字的映射或线性探测这八种可能性,是不可避免的,因此,也是最简单的(可用)解决方案。

答案 2 :(得分:-1)

如果您需要将其用于任何目的而不仅仅是理论练习,那么依靠精确的数字可能会很危险。

由于您希望使用它来分配字节数组的大小以进行序列化,请考虑使用ByteArrayOutputStream。

ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytesOut);

out.writeObject(new MyObject(12, "JackDaniels", true));
out.close();
System.out.println("Number of bytes for serialized object: " + bytesOut.toByteArray().length);

Codiva online compiler IDE处使用MyObject助手类的完整工作代码。

这不能直接回答你的问题,但我相信这是实现你想要做的事情的正确方法。

您可以通过实现readObject和writeObject方法来修改序列化的发生方式。例如,请查看http://www.byteslounge.com/tutorials/java-custom-serialization-example