此代码有效
int h;
byte r;
h=r;
但这些不是
int[] h;
byte[] r;
h=r;
或者说
int[] h =new byte[4];
我想知道为什么?
此代码有效
int h;
byte r;
h=r;
但这些不是
int[] h;
byte[] r;
h=r;
或者说
int[] h =new byte[4];
我想知道为什么?
byte
有从to的隐式转换int
,但不是from byte[]
to int[]
。这很有意义 - JIT 编译器知道要获得 an 中的值int[]
,它只需将索引乘以 4 并将其添加到数据的开头(当然,在验证之后,并假设没有额外的填充)。如果您可以分配对变量的byte[]
引用,那将不起作用-表示方式不同。int[]
该语言本可以设计为允许这种转换,但使其创建一个包含所有字节副本的新语言,但就 Java 其余部分的设计而言,这将是非常令人惊讶的,其中赋值运算符 只是复制一个运算符右侧的值到左侧的变量。int[]
或者,我们可以对 VM 施加限制,即每个数组访问都必须查看相关数组对象的实际类型,并确定如何适当地获取元素……但这会很可怕(甚至比当前的引用类型数组协方差更糟糕)。
这就是设计。当您分配byte
给更宽int
时,没关系。但是当您声明 时new byte[4]
,这是内存的 ["continuous"] 部分,粗略地说,等于 4 * 8 位(或 4 个字节)。一个int
是 32 位,所以,从技术上讲,所有byte
数组的大小都等于一个的大小int
。在 C 中,您可以直接访问内存,您可以做一些指针魔术并将byte
指针转换为int
指针。在 Java 中,你不能,那是安全的。
无论如何,你为什么想要那个?免责声明:下面的代码被认为是极不可能在任何地方看到的,除了一些性能敏感的库/应用程序中最关键的部分。ideone:http: //ideone.com/e14Omr
评论已经足够解释了,我希望。
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
/* too lazy to run with VM args, use Reflection */
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
/* get array address */
Unsafe unsafe = (Unsafe)f.get(null);
byte four_bytes[] = {25, 25, 25, 25};
Object trash[] = new Object[] { four_bytes };
long base_offset_bytes = unsafe.arrayBaseOffset(Object[].class);
long four_bytes_address = unsafe.getLong(trash, base_offset_bytes); // <- this is it
long ints_addr = unsafe.allocateMemory(16); // allocate 4 * 4 bytes, i.e. 4 ints
unsafe.copyMemory(four_bytes_address + base_offset_bytes, ints_addr, 4); // copy all four bytes
for(int i = 0; i < 4; i++) {
System.out.println(unsafe.getInt(ints_addr + i)); //run through entire allocated int[],
// get some intestines
}
System.out.println("*****************************");
for(int i = 0; i < 16; i++) {
System.out.println(unsafe.getByte(ints_addr + i)); //run through entire allocated int[],
// get some intestines
}
}
}
差异首先是由于原始类型和引用类型之间的行为差异。
如果您不熟悉它,原始类型具有“值语义”。这意味着当您执行a = b;
whena
和isb
原始类型(byte
、short
、int
、long
、float
、double
、boolean
或char
)时,将复制数字/布尔值。例如:
int a = 3;
int b = a; // int value of a is copied to b
a = 5;
System.out.println(b); // outputs: 3
但是数组是对象,对象具有“引用语义”。这意味着当您执行a = b;
wherea
并且b
都声明为数组类型时,所引用的数组对象将变为shared。从某种意义上说,值仍然被复制,但这里的“值”只是指向位于内存中其他位置的对象的指针。例如:
int[] a = new int[] { 3 };
int[] b = a; // pointer value of a is copied to b, so a and b now point at the same array object
a[0] = 5;
System.out.println(b[0]); // outputs: 5
a = null; // note: 'a' now points at no array, although this has no effect on b
System.out.println(b[0]); // outputs: 5
因此可以这样做,int = byte
因为将要复制数值(因为它们都是原始类型),而且因为任何可能的 byte 类型值都可以安全地存储在 int 中(这是一种“扩大”的原始转换)。
但是int[]
andbyte[]
都是对象类型,所以当你这样做时,int[] = byte[]
你要求共享(而不是复制)对象(数组)。
现在你要问了,为什么 int 数组和 byte 数组不能共享它们的数组内存?如果他们这样做意味着什么?
Int 是字节大小的 4 倍,因此如果 int 和 byte 数组具有相同数量的元素,那么这会导致各种胡说八道。如果您尝试以内存高效的方式实现它,那么在访问 int 数组的元素以查看它们是否实际上是字节数组时将需要复杂(并且非常慢)的运行时逻辑。从字节数组内存中读取的 int 必须读取并扩大字节值,并且 int 存储必须要么丢失高 3 个字节,要么抛出异常,说明空间不足。或者,您可以通过填充所有字节数组以使每个元素浪费 3 个字节,以一种快速但浪费内存的方式来完成,以防万一有人想将字节数组用作 int 数组。
另一方面,也许您想为每个 int 打包 4 个字节(在这种情况下,共享数组不会有相同数量的元素,具体取决于您用来访问它的变量的类型)。不幸的是,这也会导致胡说八道。最大的问题是它不能跨 CPU 架构移植。在小端PC 上,b[0]
它指的是 的低字节i[0]
,但在 ARM 设备上b[0]
可能指向 的高字节i[0]
(并且它甚至可以在程序运行时改变,因为 ARM 具有可切换的字节序)。访问数组的长度属性的开销也会变得更加复杂,如果字节数组的长度不能被 4 整除,会发生什么?!
您可以在 C 中执行此操作,但这是因为 C 数组没有明确定义的长度属性,并且 C 不会尝试保护您免受其他问题的影响。C 不在乎您是否超出了数组边界或混淆了字节序。但是Java确实关心,所以在Java中共享数组内存是不可行的。(Java 没有联合。)
这就是为什么int[].class
andbyte[].class
都分别扩展了 class Object
,但没有一个 class 扩展了另一个。您不能将对字节数组的引用存储在声明为指向 int 数组的变量中,就像您不能List
在类型变量中存储对 a 的引用一样String
;它们只是不兼容的类。
当你说
int[] arr = new byte[5];
你复制参考。右侧是对字节数组的引用。本质上,这看起来像:
|__|__|__|__|__|
0 1 2 3 4 offset of elements, in bytes
^
|
reference to byte array
左侧是对 int 数组的引用。然而,这看起来应该是这样的:
|________|________|________|________|________|
0 4 8 12 16
^
|
reference to int array
因此,简单地复制参考是不可能的。因为,要获得 arr[1],代码将查看起始地址 +4(而不是起始地址 +1)。
实现您想要的唯一方法是创建一个具有相同数量元素的 int[] 并在那里复制字节。
不自动执行此操作的理由是:
结论:在 Java 中,你总是可以说“我想把这个特殊的字节当作一个 int 对待”。但是你不能说:“我想把一些包含字节的数据结构(如数组或类实例)当作包含整数。”
简单地说,类型 byte[] 不扩展 int[]
你不能,因为它像大元素一样将存储在较小的元素中。整数不能存储在字节中。决定这些类型分配的是我们的内存设计