此代码有效
int h;
byte r;
h=r;
Run Code Online (Sandbox Code Playgroud)
但这些都不是
int[] h;
byte[] r;
h=r;
Run Code Online (Sandbox Code Playgroud)
或者说
int[] h =new byte[4];
Run Code Online (Sandbox Code Playgroud)
我想知道为什么?
Jon*_*eet 23
这里有一个隐式转换从byte到int,但不能从byte[]到int[].这很有意义--JIT编译器知道要获取一个值int[],它只需要将索引乘以4并将其添加到数据的开头(在验证之后,当然假设没有额外的填充,当然).如果您可以byte[]为int[]变量分配引用,那将无效- 表示形式不同.
该语言本来可以设计为允许转换但是使它创建一个包含所有字节副本的新 语言int[],但就Java的其余部分的设计而言,这将是非常令人惊讶的,其中赋值运算符只是复制一个从操作员右侧到左侧变量的值.
或者,我们可以对VM施加限制,每个数组访问都必须查看相关数组对象的实际类型,并找出如何恰当地获取元素...但这可能会很糟糕(甚至比当前参考型阵列协方差的糟糕性更差).
tkr*_*man 11
这就是设计.当你分配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
}
}
}
Run Code Online (Sandbox Code Playgroud)
差异首先是由于原始类型和引用类型之间的行为差异.
如果您不熟悉它,原始类型具有"价值语义".这意味着,当这样做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
Run Code Online (Sandbox Code Playgroud)
但是数组是对象,对象具有"引用语义".这意味着当您执行a = b;where 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
Run Code Online (Sandbox Code Playgroud)
因此可以这样做,int = byte因为数值将被复制(因为它们都是基本类型),并且因为任何可能的字节类型值都可以安全地存储在int中(它是"扩展"基元转换).
但是,int[]和byte[]都是对象类型,所以当你做int[] = byte[]你所要求的对象(阵列)要共享(不复制).
现在你要问,为什么int数组和字节数组不能共享它们的数组内存?如果他们这样做会是什么意思呢?
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并且byte[].class两者都单独扩展类Object,但两个类都没有扩展另一个类.您不能在声明指向int数组的变量中存储对字节数组的引用,就像您不能List在类型变量中存储对a的引用一样String; 他们只是不兼容的课程.
| 归档时间: |
|
| 查看次数: |
2211 次 |
| 最近记录: |