此代码有效
int h;
byte r;
h=r;
但这些不是
int[] h;
byte[] r;
h=r;
或者说
int[] h =new byte[4];
我想知道为什么?
答案 0 :(得分:22)
从byte
到int
进行隐式转换,但不从byte[]
转换为int[]
。这很有意义--JIT编译器知道要获取int[]
中的值,它只需要将索引乘以4并将其添加到数据的开头(在验证之后,并且假设没有额外的填充,当然)。如果您可以为byte[]
变量分配int[]
引用,则该功能无效 - 表示不同。
该语言本来可以设计为允许转换,但是要创建一个 new int[]
,其中包含所有字节的副本,但这样做会非常令人惊讶。 Java的其余部分的设计,其中赋值运算符只是将一个值从运算符的右侧复制到左侧的变量。
或者,我们可以对VM施加限制,每个数组访问都必须查看相关数组对象的实际类型,并找出如何正确获取元素......但这本来是可怕的(甚至比参考型阵列协方差的当前肮脏更糟糕。)
答案 1 :(得分:10)
这就是设计。当您将byte
分配给更宽int
时,这没关系。但是当你声明new byte[4]
时,那是一个[“连续”]内存的一部分,粗略地说,等于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
}
}
}
答案 2 :(得分:9)
差异首先是由于原始类型和引用类型之间的行为差异。
如果您不熟悉它,原始类型具有“价值语义”。这意味着当a = b;
和a
为基本类型b
,byte
,short
,{{1}时int
时long
},float
,double
,boolean
或char
)复制numeric / boolean值。例如:
int a = 3;
int b = a; // int value of a is copied to b
a = 5;
System.out.println(b); // outputs: 3
但是数组是对象,对象具有“引用语义”。这意味着当a = b;
a
和b
都被声明为数组类型时,所引用的数组对象将变为共享。在某种意义上,值仍然被复制,但是这里“值”只是指向位于内存中其他位置的对象的指针。例如:
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中(它是a "widening" primitive conversion)。
但int[]
和byte[]
都是对象类型,因此当您执行int[] = byte[]
时,您要求对象(数组)共享(没有复制)。
现在你要问,为什么int数组和字节数组不能共享数组内存?如果他们这样做会是什么意思呢?
Ints是字节大小的4倍,因此如果int和byte数组具有相同数量的元素,则会导致各种无意义。如果您尝试以内存有效的方式实现它,那么在访问int数组的元素以查看它们是否实际是字节数组时,将需要复杂(且非常慢)的运行时逻辑。从字节数组内存中读取int必须读取并加宽字节值,并且int存储必须要么丢失高3字节,要么抛出一个异常,说明没有足够的空间。 或,您可以通过填充所有字节数组以快速但内存浪费的方式执行此操作,以便每个元素有3个浪费的字节,以防有人想要将字节数组用作int阵列。
另一方面,也许你想要为每个int打包4个字节(在这种情况下,共享数组将不具有相同数量的元素,具体取决于您用来访问它的变量的类型)。不幸的是,这也会导致废话。最大的问题是它不能跨CPU架构移植。在little-endian PC上,b[0]
将引用i[0]
的低字节,但在ARM设备上b[0]
可能指向i[0]
的高字节(当程序运行时它甚至可以改变,因为ARM具有可切换的字节序)。访问数组长度属性的开销也会变得更复杂,如果字节数组的长度不能被4整除,会发生什么?!
您可以在C中执行此操作,但这是因为C数组没有明确定义的长度属性,并且因为C不会尝试保护您免受其他问题的影响。 C不关心你是否超出数组边界或混淆字节序。但Java确实关心,因此在Java中共享阵列内存是不可行的。 (Java没有unions。)
这就是为什么int[].class
和byte[].class
都分别扩展了类Object
,但两个类都没有扩展另一个类。您不能在声明为指向int数组的变量中存储对字节数组的引用,就像您无法在List
类型的变量中存储对String
的引用一样};他们只是不兼容的课程。
答案 3 :(得分:1)
简单地说,类型byte []不会扩展int []
答案 4 :(得分:1)
当你说
时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中,你总是可以说&#34;我想把这个特殊字节视为一个int。&#34;但你不能说:&#34;我想要处理一些包含字节的数据结构(如数组或类实例),就好像它包含整数一样。&#34;
答案 5 :(得分:-1)
你不能因为它的大元素将被存储在较小的元素中.Integer不能存储在byte.Its我们的内存设计谁决定这些类型的分配