为什么可以';在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