在Java中查找系统的字节序

17

我发现检查机器是大端还是小端的算法(int C)是

int is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

我该如何在*Java中找到这种东西?因为这是一道面试题,我不想使用内置的库,我想在Java中自己找到它。


6
据我所知,Java 在字节顺序方面严格遵循大端序。没有办法(也没有必要)在不调用本地代码的情况下找出底层体系结构的字节顺序。 - biziclop
2
吹毛求疵:这个 union 的使用会触发未定义行为吗?我会改用将 char const * 转换成 uint32_t - Fred Foo
好的。同意Java不允许您查找机器的字节序。但是,我如何使用Java代码找出JVM本身的字节序?这只是为了面试目的。 - user93796
1
我能想到的唯一原因是为了创建二进制文件以供非Java应用程序使用,或者在Java中构建编译器。相当晦涩。 - DNA
如果我从一个小端机器得到了一串字节流,现在我想将其转换为大端。那么反转字节数组是否可以完成这个任务? - user93796
显示剩余8条评论
6个回答

45

我并没有这个方案的功劳,但你可以尝试一下:

import java.nio.ByteOrder;

if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)) {
  System.out.println("Big-endian");
} else {
  System.out.println("Little-endian");
}

1
@user93796,这是一个“系统”库,而不是第三方库,在我看来,是公平的游戏。这是最直接解决问题的方法 - 你为什么需要一个丑陋的解决方案呢? - Nim
3
这不是一个库,而是一个内置类。如果没有它们,你在Java中就无法深入学习。事实上,没有它们你什么也学不到。如果面试官不喜欢你的回答,那么你可以问问自己:我真的想为这样问愚蠢问题的公司工作吗? - biziclop
3
@user93796,哪个人说的?如果有人给我出一道测试题,而问题的关键不是实现排序算法,我会使用已经存在的实现。在我看来,其他任何方法都很愚蠢。 - Nim
3
如果有人编写自己的(可能存在错误)sort()方法而不使用经过测试且广为人知的内置方法,只要它能够完成工作,你会雇用这个人吗? - Peter Lawrey
2
我非常怀疑实际上没有其他方法可以做到这一点。Java被设计为跨平台的,因此几乎所有的方法在大端或小端机器上都会给出相同的结果。 - Louis Wasserman
显示剩余3条评论

4
我不想使用内置库作为面试题,如何在Java中找到类似的东西?
你不能在纯Java中做到这一点而不调用库。这是因为:
- Java认为如果可能的话,你不应该关心这些事情。 - Java相对来说作为一种语言功能较少,依靠其库来完成许多其他语言中可能是语言特性的事情。 - 大多数人不区分语言做了哪些事情和什么是内置库,因为这种区分很少有用。 - 字节码/虚拟机没有大端或小端,只有真正的实现才有。
旧的库只支持大端(大多数处理器使用,Intel是一个明显的例外),新的库可以设置为其中之一。
你很少需要知道如何重新编写内置功能,如果确实需要,你会阅读已经完成的代码,即使你知道如何做也一样。
我已经看到很多人重新开发有错误、难以使用、行为出乎意料并且比内置库性能差的内置功能。完全可能写出比JDK中更快或更定制化的东西,但从头脑中知道如何做很少有用。
我偶尔会得到这样的问题,然后在回答问题之前指出不做这些事情的所有原因。

目前为止,最佳答案老兄,指出你不应该这样做的原因 - El Developer
即使你不应该这样做,但你仍然需要展示你能够做到。我会说,看看已有的代码是一个合理的答案。最近我不得不创建一个自定义的 TreeMap,它使用 int 作为键,并且为该键使用多个值并回收所有节点。当然,我从 TreeMap 中复制了很多好的代码,并尽可能保持名称相同。 - Peter Lawrey
@PeterClark 如果你需要这样做,使用专为此目的设计的内置库,例如 Integer/Long reverseBytes https://docs.oracle.com/javase/8/docs/api/java/lang/Long.html#reverseBytes-long- - Peter Lawrey
@PeterLawrey 感谢您让我了解这些实用方法 - 我仍然需要知道数据是否需要首先反转(因此仍然需要确定字节序)。 - Peter Clark
ARM和AArch64 CPU通常在小端模式下运行,据我所知,有些可能不支持大端模式。英特尔是最早制造小端CPU的公司之一,但随着x86的广泛使用,它变得更加普遍。即使PowerPC和MIPS CPU也可以支持双端模式,尽管我不知道在小端模式下运行它们有多常见。 - Peter Cordes
显示剩余2条评论

3
System.out.println(ByteOrder.nativeOrder());

1

对于有 sun.misc.Unsafe 类,并在静态实例变量中存储 Unsafe 单例的 JVM,可以使用以下方法。我在 Oracle JVM 和 openjdk 上成功测试了此代码。该代码的工作原理是将一个短的(2 字节)值写入内存,然后读取第一个字节。

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

public class Endianness {
    private static Unsafe getUnsafe() {
        Unsafe unsafe = null;
        try {
            Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
        } catch (Exception e) {}
        return unsafe;
    }

    public static void main(String[] args) {
        Unsafe unsafe = getUnsafe();
        long address = unsafe.allocateMemory(2);
        short number = 1;
        unsafe.putShort(address, number);
        if (unsafe.getByte(address) == 0)
            System.out.println("Big Endian");
        else
            System.out.println("Little Endian");
        unsafe.freeMemory(address);
    }
}

-3
public static void getEndian(){
    int a = 0;
    // 0x0....01 
    int b = 1;
    //0x0..0,0.01,0..0,0..0
    int combine = (b<<16) | a;
    System.out.println(combine);
    if(combine == 65536){
        System.out.println("LittleEndian");
    }else{
        System.out.println("BigEndian");
    }
}

我的方法是将数字进行移位,然后比较它是否符合小端机制的预期。


2
据我所知,这应该不起作用。无论系统如何在内存中存储,按位或运算将使用正确的位顺序。在任何系统中,65536 | 0 都将等于 65536。 - Boris
1
字节序处理的是内存表示,而不是值。尝试运行你的代码,它总是返回小端字节序。 - phuclv

-6

JVM和Java严格遵循大端字节序,无论主机平台如何。

然而,在字符编码方面,可以提取有关系统默认编码的信息,该编码可能具有字节序(实际上,很可能会有)。

这里有一些代码供您参考:

boolean is_big_endian()
{
    return true;
}

编辑:

请参考这个带有参考资料的答案:Java虚拟机的字节序

公平地说,可以调用本地代码,在那里你可以获得本地字节顺序的内容。


1
@user93796:那又怎样?这难道会神奇地使JVM的工作失效吗? - Marcin
这是Stackoverflow上最愚蠢的答案之一,而且Java中没有“bool”类型,Java方法通常采用驼峰命名法编写。 - Koray Tugay
1
@PeterClark 真的吗?如果是这样,那么它不符合此答案撰写时的JVM规范。 - Marcin
1
@Marcin 我深入研究了一下代码,发现我正在使用的代码调用本地(c/c++)代码(bitmap.copyPixelsToBuffer)来进行实际的内存复制。这可能是我得到的缓冲区为什么是小端序的原因(它肯定是,因为它是一个每个颜色通道1字节的图像,我得到的是BGRA而不是ARGB)。我不确定JVM本身是否使用小端序,如果JVM规范明确说明,则可能不会。ByteOrder.nativeOrder()文档提到该常量存在是因为可以在本地运行性能敏感的代码。 - Peter Clark
抱歉在我的评论中使用了“谎言”这个词 - 措辞不当。同时,如果您觉得有必要,可以保留此答案,并添加一个链接到字节顺序的注释,以澄清至少一些性能敏感的代码可以在本地硬件上运行,因此使用本地字节顺序。感谢您的再次确认,我差点忘记留下以上细节。 - Peter Clark
显示剩余7条评论

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