Этот код действителен
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];
Я хотел бы знать, почему?
Разница, во-первых, обусловлена различием в поведении между примитивными типами и ссылочными типами.
Если вы не знакомы с ним, примитивные типы имеют "семантику значений". Это означает, что когда вы выполняете 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
Но массивы - это объекты, а объекты имеют "ссылочную семантику". Это означает, что когда вы 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, потому что числовое значение будет скопировано (поскольку они оба являются примитивными типами), а также потому, что любое возможное значение байта типа может быть безопасно сохранено в int (это "расширяющееся" примитивное преобразование).
Но int[] и byte[] - оба типа объектов, поэтому, когда вы выполняете int[] = byte[], вы запрашиваете, чтобы объект (массив) был общим (не копируется).
Теперь вы должны спросить: почему массив int и байтовый массив не разделяют память массива? А что если это будет означать, если бы они это сделали?
Ints в 4 раза больше байтов, поэтому, если массивы int и byte должны иметь одинаковое количество элементов, тогда это вызывает всевозможные глупости. Если вы попытались реализовать его в эффективном режиме памяти, тогда при доступе к элементам массивов int потребуется сложная (и очень медленная) логика времени выполнения, чтобы определить, были ли они фактически байтовыми массивами. Int считывает из памяти массива байтов, чтобы читать и расширять значение байта, а int-магазины должны либо потерять верхние 3 байта, либо выбросить исключение, заявив, что недостаточно места. Или вы могли бы сделать это быстро, но с потерей памяти, заполнив все байтовые массивы, чтобы было 3 потерянных байта на элемент, на всякий случай кто-то хочет использовать массив байтов как массив int.
С другой стороны, возможно, вы хотите упаковать 4 байта на int (в этом случае общий массив не будет иметь одинаковое количество элементов в зависимости от типа переменной, которую вы используете для доступа к ней). К сожалению, это также приводит к бессмыслию. Самая большая проблема заключается в том, что он не переносится по архитектуре процессора. На мало-endian ПК 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 в переменной типа String; они просто несовместимые классы.
Там подразумевается преобразование от byte до int, но не от byte[] до int[]. Это имеет большой смысл - компилятор JIT знает, что для получения значения в int[] ему просто нужно умножить индекс на 4 и добавить его к началу данных (после проверки и без дополнительного дополнения, конечно). Это не сработает, если вы можете назначить ссылку byte[] переменной int[] - представления различны.
Язык мог быть разработан таким образом, чтобы это преобразование, но создало новый int[], в котором содержалась копия всех байтов, но это было бы довольно неожиданно с точки зрения дизайна остальной части Java, где оператор присваивания просто копирует значение из правой части оператора в переменную слева.
В качестве альтернативы мы могли бы наложить ограничение на виртуальную машину, чтобы каждый доступ к массиву должен был смотреть на фактический тип рассматриваемого объекта массива и определять, как правильно обращаться к элементу... но это могло бы иметь был ужасен (даже хуже нынешней гадкости ковариации массива ссылочного типа).
Это дизайн. Когда вы назначаете byte более широкому int, это нормально. Но когда вы объявляете new byte[4], что [ "непрерывная" ] часть памяти, которая, грубо говоря, равна 4 * 8 бит (или 4 байта). И один int - 32 бита, поэтому технически весь размер массива byte равен размеру одного int. В C, где у вас есть прямой доступ к памяти, вы можете сделать магию указателя и получить указатель byte, присвоенный указателю int. В Java вы не можете и не будете в безопасности.
В любом случае, зачем вам это нужно? Отказ от ответственности: приведенный ниже код считается крайне маловероятным, если вы не найдете места, кроме своей игровой площадки. Хорошо, я получил свой пример с небезопасной работой. Посмотрите на идеон: 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
}
}
}
Просто, тип byte [] не расширяет int []
Когда вы говорите
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". Но вы не можете сказать: "Я хочу обрабатывать некоторую структуру данных (например, массив или экземпляр класса), который содержит байты, как если бы он содержал ints."
вы не можете, потому что его, как большой элемент, будет храниться в меньшем. Невозможно сохранить содержимое в байтах. Это наша конструкция памяти, которая решает этот тип размещения