You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现byte[]与Java对象属性互转?Unsafe类导入遇阻

嘿,我来帮你搞定这个字节数组和对象互转的需求!你想要的unmarshall(targetObject, byte[])和反向打包的功能,其实有好几种靠谱的实现方式,而且完全可以避开sun.misc.Unsafe的坑——毕竟那个类是JDK内部黑科技,兼容性和安全性都不太友好。下面给你分场景推荐方案:

方案1:手动用ByteBuffer实现(适合已知固定字节结构)

这个方法完全依赖JDK自带API,不需要额外依赖,还能精准控制每个字段的字节偏移、大小端细节,完美匹配你“已知byte[]结构”的场景。

首先,先定义和byte[]结构一一对应的目标对象:

public class ProtocolMessage {
    // 假设byte[]结构是:
    // 前4字节:int类型消息ID(大端)
    // 接下来8字节:long类型时间戳(大端)
    // 最后2字节:short类型状态码(大端)
    private int messageId;
    private long timestamp;
    private short statusCode;

    // 记得加构造器、getter和setter
}

然后封装unmarshall和marshall方法:

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteConverter {
    // 把byte[]解析到目标对象
    public static void unmarshall(ProtocolMessage target, byte[] bytes) {
        // 根据实际字节结构设置大小端(这里用大端示例)
        ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
        target.setMessageId(buffer.getInt());
        target.setTimestamp(buffer.getLong());
        target.setStatusCode(buffer.getShort());
    }

    // 把对象打包成byte[]
    public static byte[] marshall(ProtocolMessage source) {
        // 分配刚好容纳所有字段的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(4 + 8 + 2).order(ByteOrder.BIG_ENDIAN);
        buffer.putInt(source.getMessageId());
        buffer.putLong(source.getTimestamp());
        buffer.putShort(source.getStatusCode());
        return buffer.array();
    }
}

用的时候就很简单:

// 反序列化:把byte[]填充到对象
ProtocolMessage msg = new ProtocolMessage();
ByteConverter.unmarshall(msg, receivedBytes);

// 序列化:把对象转成byte[]
byte[] sendBytes = ByteConverter.marshall(msg);

如果你的byte[]里有字符串、嵌套结构这类复杂类型,也可以通过ByteBuffer截取字节段后手动转换(比如用new String(buffer.array(), offset, length)转字符串),完全可控。

方案2:用第三方序列化库(适合复杂对象)

如果你的对象结构复杂,手动写ByteBuffer太繁琐,推荐用成熟的序列化框架,比如Protostuff(基于Protobuf但不需要写proto文件),性能高还省心。

首先添加Maven依赖(Gradle用户对应调整):

<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.7.4</version>
</dependency>
<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.7.4</version>
</dependency>

然后封装通用的转换方法:

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

public class ProtostuffConverter {
    // 获取对象的序列化Schema
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        return RuntimeSchema.getSchema(clazz);
    }

    // 反序列化:byte[]转对象
    public static <T> void unmarshall(T target, byte[] bytes) {
        Schema<T> schema = getSchema((Class<T>) target.getClass());
        ProtostuffIOUtil.mergeFrom(bytes, target, schema);
    }

    // 序列化:对象转byte[]
    public static <T> byte[] marshall(T source) {
        Schema<T> schema = getSchema((Class<T>) source.getClass());
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            return ProtostuffIOUtil.toByteArray(source, schema, buffer);
        } finally {
            buffer.clear();
        }
    }
}

使用起来超级简单,不管对象多复杂都能自动处理:

// 反序列化
ProtocolMessage msg = new ProtocolMessage();
ProtostuffConverter.unmarshall(msg, receivedBytes);

// 序列化
byte[] sendBytes = ProtostuffConverter.marshall(msg);

如果需要精准控制字段的字节顺序或者映射关系,还可以用Protostuff的@Tag注解给字段指定顺序,完全满足定制需求。

方案3:解决sun.misc.Unsafe的导入问题(不推荐)

如果你非要用Unsafe,先提醒你:这个类是JDK内部API,不同版本兼容性差,还容易出内存安全问题,除非有极端性能需求,否则不建议用。不过还是给你解决导入问题的方法:

  • Java 8及更早版本:可以直接导入sun.misc.Unsafe,但IDE可能会报警告(因为是内部API),忽略即可。
  • Java 9及之后版本:需要给JVM添加启动参数--add-exports java.base/sun.misc=ALL-UNNAMED,才能允许访问这个内部类。

然后获取Unsafe实例的方式(因为Unsafe的构造器是私有的,只能通过反射获取):

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeHelper {
    public static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }
}

之后的内存操作就需要你手动处理每个字段的偏移量和类型,代码可读性极差,这里给个简单示例:

public class UnsafeConverter {
    public static void unmarshall(ProtocolMessage target, byte[] bytes) throws Exception {
        Unsafe unsafe = UnsafeHelper.getUnsafe();
        // 获取messageId字段的内存偏移量
        long messageIdOffset = unsafe.objectFieldOffset(ProtocolMessage.class.getDeclaredField("messageId"));
        // 从byte[]中读取int值,写入对象的对应字段
        unsafe.putInt(target, messageIdOffset, unsafe.getInt(bytes, (long) unsafe.arrayBaseOffset(byte[].class)));
        // 其他字段同理,需要手动处理每个字段的偏移和类型
    }

    public static byte[] marshall(ProtocolMessage source) throws Exception {
        Unsafe unsafe = UnsafeHelper.getUnsafe();
        byte[] bytes = new byte[4 + 8 + 2]; // 总字节数和对象字段匹配
        long byteBaseOffset = unsafe.arrayBaseOffset(byte[].class);
        // 把对象的messageId字段值写入byte[]
        long messageIdOffset = unsafe.objectFieldOffset(ProtocolMessage.class.getDeclaredField("messageId"));
        unsafe.putInt(bytes, byteBaseOffset, unsafe.getInt(source, messageIdOffset));
        // 其他字段同理
        return bytes;
    }
}

再次强调:除非万不得已,别用Unsafe,前面两个方案足够覆盖绝大多数场景。


内容的提问来源于stack exchange,提问作者The Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 06:52:53