如何在Java中初始化字节数组?

199

我需要在Java中以字节数组形式存储一些常量值(UUID),我想知道初始化这些静态数组的最佳方法是什么。下面是我目前的做法,但是我感觉肯定有更好的方式。

private static final byte[] CDRIVES = new byte[] { (byte)0xe0, 0x4f, (byte)0xd0,
    0x20, (byte)0xea, 0x3a, 0x69, 0x10, (byte)0xa2, (byte)0xd8, 0x08, 0x00, 0x2b,
    0x30, 0x30, (byte)0x9d };
private static final byte[] CMYDOCS = new byte[] { (byte)0xba, (byte)0x8a, 0x0d,
    0x45, 0x25, (byte)0xad, (byte)0xd0, 0x11, (byte)0x98, (byte)0xa8, 0x08, 0x00,
    0x36, 0x1b, 0x11, 0x03 };
private static final byte[] IEFRAME = new byte[] { (byte)0x80, 0x53, 0x1c,
    (byte)0x87, (byte)0xa0, 0x42, 0x69, 0x10, (byte)0xa2, (byte)0xea, 0x08,
    0x00, 0x2b, 0x30, 0x30, (byte)0x9d };
...
and so on

有没有什么我可以用的东西,可能不太高效,但看起来更干净? 例如:

private static final byte[] CDRIVES =
    new byte[] { "0xe04fd020ea3a6910a2d808002b30309d" };

由于它们被声明为“static final”,这可能已经是最合适的方式;被接受的答案完全忽略了这些关键字,甚至无法使用它们。 - Martin Zeitler
11个回答

151

您可以使用一个实用函数将熟悉的十六进制字符串转换为byte[]

当用于定义final static常量时,性能成本是无关紧要的。

自Java 17起

现在有java.util.HexFormat,让您可以执行以下操作:

byte[] CDRIVES = HexFormat.of().parseHex("e04fd020ea3a6910a2d808002b30309d");

这个实用类允许您指定一个格式,如果您发现其他格式更易于阅读或从参考源中复制黏贴时非常方便:

byte[] CDRIVES = HexFormat.ofDelimiter(":")
    .parseHex("e0:4f:d0:20:ea:3a:69:10:a2:d8:08:00:2b:30:30:9d");

Java 17之前

我建议您使用Dave L在使用Java将十六进制转储的字符串表示转换为字节数组的函数中定义的函数。

byte[] CDRIVES = hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d");

我在这里插入它以获得最大的可读性:

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

1
在Java 17中,现在可以使用java.util.HexFormat.of().parseHex("e04fd020ea3a6910a2d808002b30309d") - Dave L.
@DaveL,谢谢。正在更新答案。 - Denys Séguret

120
byte[] myvar = "Any String you want".getBytes();

字符串字面值可以被转义以提供任意字符:

byte[] CDRIVES = "\u00e0\u004f\u00d0\u0020\u00ea\u003a\u0069\u0010\u00a2\u00d8\u0008\u0000\u002b\u0030\u0030\u009d".getBytes();

52
这是否会将字符串 "0000" 转换为 {0x30,0x30,0x30,0x30}(ASCII),而不是帖子作者想要的 {0x00,0x00,0x00,0x00}(二进制)? - jww
6
看一下问题的标题,再看看这个回答。现在告诉我,它有什么问题?它可能不能解决问题提问者特定的问题,但它确实解决了我的问题。我需要将一个字符串转换成字节数组,以用作伪随机数发生器的种子,这样做非常成功。 - e18r
@e18r 它确实生成字节,但您不知道它使用的是哪种字符集,因为这取决于默认字符集。请至少使用.getBytes(desiredEncoding)。 - quant
@petmez 问个蠢问题:在JAVA中,像"".getBytes(UTF_8));(对空字符串执行getBytes)是安全的吗?它是否“合法”?还是我可以只做:= new byte[0];? - Robert Achmann
1
@RobertAchmann "".getBytes("UTF-8") 应该返回一个空数组,这是完全合法的。 - Jazzepi

38

在Java 6中,有一个方法可以完全满足你的需求:

private static final byte[] CDRIVES = javax.xml.bind.DatatypeConverter.parseHexBinary("e04fd020ea3a6910a2d808002b30309d")

或者您可以使用Google Guava

import com.google.common.io.BaseEncoding;
private static final byte[] CDRIVES = BaseEncoding.base16().lowerCase().decode("E04FD020ea3a6910a2d808002b30309d".toLowerCase());

如果你使用的是小数组,Guava方法可能会过度复杂。但是Guava也有可以解析输入流的版本。当处理大型十六进制输入时,这是一个不错的特性。


Guava的示例并不能完全按照文档中所写运行 - 如果你使用小写十六进制数字,那么需要使用base16().lowerCase().decode(...)。具体请参考:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/BaseEncoding.html#lowerCase()。 - Peter DeGlopper
@PeterDeGlopper 很好的发现,我已经更新了答案,所以代码现在可以处理包含大小写字符的字符串。 - stefan.schwetschke
4
很遗憾,Java 9 中删除了 javax.xml.bind - randomdude999

7
您可以使用Java UUID类来存储这些值,而不是字节数组:
UUID

public UUID(long mostSigBits,
            long leastSigBits)

使用指定的数据构造一个新的UUID。mostSigBits用于UUID的最高有效64位,leastSigBits成为UUID的最低有效64位。


7

在编译时可以使用无符号十六进制数赋值的最小内部类型是char,如下所示:

private static final char[] CDRIVES_char = new char[] {0xe0, 0xf4, ...};

为了得到一个等价的字节数组,可以使用以下转换方法:
public static byte[] charToByteArray(char[] x)
{
    final byte[] res = new byte[x.length];
    for (int i = 0; i < x.length; i++)
    {
        res[i] = (byte) x[i];
    }
    return res;
}

public static byte[][] charToByteArray(char[][] x)
{
    final byte[][] res = new byte[x.length][];
    for (int i = 0; i < x.length; i++)
    {
        res[i] = charToByteArray(x[i]);
    }
    return res;
}

4
没有使用任何库,返回动态长度,使用无符号整数解释(不使用补码)的解决方案。
    public static byte[] numToBytes(int num){
    if(num == 0){
        return new byte[]{};
    }else if(num < 256){
        return new byte[]{ (byte)(num) };
    }else if(num < 65536){
        return new byte[]{ (byte)(num >>> 8),(byte)num };
    }else if(num < 16777216){
        return new byte[]{ (byte)(num >>> 16),(byte)(num >>> 8),(byte)num };
    }else{ // up to 2,147,483,647
        return new byte[]{ (byte)(num >>> 24),(byte)(num >>> 16),(byte)(num >>> 8),(byte)num };
    }
}

3
您可以使用此实用函数:
public static byte[] fromHexString(String src) {
    byte[] biBytes = new BigInteger("10" + src.replaceAll("\\s", ""), 16).toByteArray();
    return Arrays.copyOfRange(biBytes, 1, biBytes.length);
}

与Denys Séguret和stefan.schwetschke的变体不同,它允许在输入字符串中插入分隔符符号(空格、制表符等),使其更易读。

使用示例:

private static final byte[] CDRIVES
    = fromHexString("e0 4f d0 20 ea 3a 69 10 a2 d8 08 00 2b 30 30 9d");
private static final byte[] CMYDOCS
    = fromHexString("BA8A0D4525ADD01198A80800361B1103");
private static final byte[] IEFRAME
    = fromHexString("80531c87 a0426910 a2ea0800 2b30309d");

2

就保持代码的整洁性而言,您可以使用ByteArrayOutputStream对象...

ByteArrayOutputStream bObj = new ByteArrayOutputStream();
bObj.reset();

//使用以下方法逐一将所有值写入bObj

bObj.write(byte value)

// 完成后,您可以使用以下代码获取byte[]

CDRIVES = bObj.toByteArray();

//接着你也可以使用相似的方法来处理CMYDOCS和IEFRAME,

注意 如果你的数组非常小,则这不是一个高效的解决方案。


你可能是指 ´bObj.write(int value)´,它实际上写入一个字节并接受 ´unsigned´ 字节值。 - Sam Ginrich

2
在这种情况下,我建议使用org.apache.commons.codec.binary.Hex。它有很有用的API,可用于在String和十六进制或二进制之间进行转换。例如:
  1. Hex.decodeHex(char[] data) 将char数组解码为byte数组,如果数组中存在非十六进制字符或字符数为奇数,则会抛出DecoderException异常。

  2. Hex.encodeHex(byte[] data) 是上面解码方法的对应方法,将byte数组编码为char[]

  3. Hex.encodeHexString(byte[] data) 将byte数组转换回String

用法:Hex.decodeHex("dd645a2564cbe648c8336d2be5eafaa6".toCharArray())

2
你可以使用Bouncy Castle包, Maven导入,
  <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
    </dependency>

Java代码,

byte[] CDRIVES = Hex.decode("e04fd020ea3a6910a2d808002b30309d");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接