为什么可以';在java中,t字节数组可以存储在整数数组中
此代码有效为什么可以';在java中,t字节数组可以存储在整数数组中,java,Java,此代码有效 int h; byte r; h=r; 但事实并非如此 int[] h; byte[] r; h=r; 或者说 int[] h =new byte[4]; 我想知道为什么?有一个从byte到int的隐式转换,但不是从byte[]到int[]。这很有意义——JIT编译器知道,要获得int[]中的值,只需将索引乘以4并将其添加到数据的开头(在验证之后,当然,假设没有额外的填充)。如果您可以为int[]变量指定一个byte[]引用,那么这将不起作用-表示方式是不同的 该语言本来可以设
int h;
byte r;
h=r;
但事实并非如此
int[] h;
byte[] r;
h=r;
或者说
int[] h =new byte[4];
我想知道为什么?有一个从
byte
到int
的隐式转换,但不是从byte[]
到int[]
。这很有意义——JIT编译器知道,要获得int[]
中的值,只需将索引乘以4并将其添加到数据的开头(在验证之后,当然,假设没有额外的填充)。如果您可以为int[]
变量指定一个byte[]
引用,那么这将不起作用-表示方式是不同的
该语言本来可以设计为允许这种转换,但它会创建一个新的int[]
,其中包含所有字节的副本,但就Java其余部分的设计而言,这将是非常令人惊讶的,其中赋值运算符仅将运算符右侧的值复制到左侧的变量
或者,我们可以对VM施加一个限制,即每个数组访问都必须查看所讨论的数组对象的实际类型,并确定如何适当地访问元素。。。但那将是可怕的(甚至比当前引用类型数组协方差的糟糕)。简单地说,类型byte[]不扩展int[]这就是设计。当您将
byte
分配给更宽的int
时,就可以了。但是当您声明新字节[4]时,这是内存的一部分,大致上等于4*8位(或4字节)。一个int
是32位,所以从技术上讲,所有字节
数组的大小都等于一个int
的大小。在C语言中,您可以直接访问内存,您可以执行一些指针魔术,将字节
指针转换为int
指针。在Java中,你不能这样做,这是安全的
不管怎样,你为什么要这样?
免责声明:以下代码被认为是极不可能在任何地方看到,除了你的游乐场。
好吧,我举了一个不安全工作的例子。看看ideone:
我希望,这些评论足以说明问题
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
}
}
}
导入sun.misc.Unsafe;
导入java.lang.reflect.Field;
公共班机{
publicstaticvoidmain(字符串[]args)抛出NoSuchFieldException、IllegalAccessException、instanceionexception{
/*由于太懒,无法使用VM参数运行,请使用反射*/
字段f=不安全的.class.getDeclaredField(“不安全的”);
f、 setAccessible(true);
/*获取数组地址*/
不安全=(不安全)f.get(null);
字节4_字节[]={25,25,25,25};
对象垃圾[]=新对象[]{4_字节};
long base_offset_bytes=unsafe.arrayBaseOffset(Object[].class);
long four_bytes_address=unsafe.getLong(垃圾桶、基偏移量_bytes);//这种差异首先是由于基本类型和引用类型之间的行为差异造成的
如果您不熟悉它,则基本类型具有“值语义”。这意味着当您执行a=b;
时a
和b
是基本类型(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
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
但是数组是对象,并且对象具有“引用语义”。这意味着,当执行a=b;
时,如果a
和b
都声明为数组类型,则引用的数组对象将变为共享的。从某种意义上说,该值仍被复制,但此处的“值”只是指向内存中其他位置的对象的指针。例如:
int a = 3;
int b = a; // int value of a is copied to b
a = 5;
System.out.println(b); // outputs: 3
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[]
和byte[]
都是对象类型,因此当您执行int[]=byte[]
时,您要求对象(数组)被共享(不复制)
现在你必须问,为什么一个int数组和一个字节数组不能共享它们的数组内存呢
int是字节大小的4倍,因此,如果int和byte数组具有相同数量的元素,那么这会导致各种各样的胡说八道。如果您试图以内存效率高的方式实现它,那么它将非常复杂(而且非常慢)在访问int数组的元素时,需要运行时逻辑来查看它们是否实际上是字节数组。从字节数组内存中读取int必须读取并加宽字节值,int存储必须丢失上面的3个字节,或者抛出一个异常,说没有足够的空间。或者,您可以使用快速的内存执行此操作y-浪费方式,通过填充所有字节数组,使每个元素有3个浪费的字节,以防有人想将字节数组用作int数组
另一方面,您可能希望每int打包4个字节(在这种情况下,共享数组的元素数将不相同,具体取决于您用于访问它的变量的类型)。不幸的是,这也会导致无意义。最大的问题是它不能跨CPU体系结构移植。在PC上,b[0]
指的是i[0]
的低位字节,但在ARM设备上b[0]
可能指向i[0]
的高位字节(甚至在程序运行时可能会发生变化,因为ARM具有可切换的端点)。访问数组长度属性的开销也会变得更加复杂,如果字节数组的长度不能被4整除,会发生什么
您可以在C中执行此操作,但这是因为C数组没有正确定义的长度
|________|________|________|________|________|
0 4 8 12 16
^
|
reference to int array