Spec-Zone.ru › Java Language Specification 24

Глава 10. Массивы

Оглавление

10.1. Типы массивов
10.2. Переменные массивов
10.3. Создание массивов
10.4. Доступ к элементам массива
10.5. Исключение ArrayStoreException
10.6. Инициализаторы массивов
10.7. Члены массива
10.8. Class Объекты для массивов
10.9. Массив символов не является String

В языке программирования Java массивы являются объектами (§4.3.1), создаются динамически и могут быть присвоены переменным типа Object (§4.3.2). Все методы класса Object могут быть вызваны для массива.

Объект массива содержит ряд переменных. Количество переменных может быть нулевым, в этом случае массив считается пустым. Переменные, содержащиеся в массиве, не имеют имен; вместо этого к ним обращаются с помощью выражений доступа к массиву, использующих целочисленные индексы, неотрицательные целые значения. Эти переменные называются компонентами массива. Если массив имеет n компонентов, то n называется длиной массива; к компонентам массива обращаются с помощью целочисленных индексов от 0 до n - 1 включительно.

Все компоненты массива имеют один и тот же тип, который называется типом компоненты массива. Если тип компоненты массива T, то тип самого массива записывается как T[].

Тип компоненты массива сам по себе может быть типом массива. Компоненты такого массива могут содержать ссылки на подмассивы. Если, начиная с любого типа массива, рассматривать его тип компоненты, а затем (если это также тип массива) тип компоненты этого типа и так далее, в конечном итоге необходимо достичь типа компоненты, который не является типом массива; это называется типом элемента исходного массива, а компоненты на этом уровне структуры данных называются элементами исходного массива.

Есть некоторые ситуации, в которых элемент массива может быть массивом: если тип элемента является Object или Cloneable или java.io.Serializable, то некоторые или все элементы могут быть массивами, так как любой объект массива может быть присвоен любой переменной этих типов.

10.1. Типы массивов

Типы массивов используются в объявлениях и в выражениях приведения типов (§15.16).

Тип массива записывается как имя типа элемента, за которым следует некоторое количество пар пустых квадратных скобок []. Количество пар скобок указывает глубину вложенности массива.

Каждая пара скобок в типе массива может быть аннотирована анотациями типов (§9.7.4). Анотация применяется к паре скобок (или эллипсису, в объявлении параметра с переменным числом аргументов), которая следует за ней.

Тип элемента массива может быть любым типом, примитивным или ссылочным. В частности:

  • Разрешены массивы с типом интерфейса в качестве типа элемента.

    Элементом такого массива может быть ссылка null или экземпляр любого типа, реализующего интерфейс.

  • Разрешены массивы с типом класса abstract в качестве типа элемента.

    Элементом такого массива может быть ссылка null или экземпляр любого подкласса класса abstract, который сам по себе не является abstract.

Длина массива не является частью его типа.

Супертипы типа массива указаны в §4.10.3.

Отношение супертипа для типов массивов не такое же, как отношение суперкласса. Прямой супертип Integer[] — это Number[] в соответствии с §4.10.3, но прямой суперкласс Integer[] — это Object согласно Class объекту для Integer[] (§10.8). Это не имеет практического значения, потому что Object также является супертипом всех типов массивов.

10.2. Переменные массивов

Переменная типа массива хранит ссылку на объект. Объявление переменной типа массива не создаёт объект массива или не выделяет место для компонентов массива. Оно создаёт только саму переменную, которая может содержать ссылку на массив. Однако часть инициализатора декларатора (§8.3, §9.3, §14.4.1) может создать массив, ссылка на который затем становится начальным значением переменной.

Пример 10.2-1. Объявления переменных массивов


int[]     ai;        // array of int
short[][] as;        // array of array of short
short     s,         // scalar short
          aas[][];   // array of array of short
Object[]  ao,        // array of Object
          otherAo;   // array of Object
Collection<?>[] ca;  // array of Collection of unknown type

Приведённые выше объявления не создают объектов массивов. Ниже приведены примеры объявлений переменных массивов, которые создают объекты массивов:


Exception ae[]  = new Exception[3];
Object aao[][]  = new Exception[2][3];
int[] factorial = { 1, 1, 2, 6, 24, 120, 720, 5040 };
char ac[]       = { 'n', 'o', 't', ' ', 'a', ' ',
                    'S', 't', 'r', 'i', 'n', 'g' };
String[] aas    = { "array", "of", "String", };


Тип массива переменной зависит от пар скобок, которые могут быть частью типа в начале объявления переменной, или в части декларатора переменной, или и то, и другое. В частности, при объявлении поля, формального параметра, локальной переменной или компонента записи (§8.3, §8.4.1, §9.3, §9.4, §14.4.1, §14.14.2, §15.27.1, §8.10.1), тип массива переменной определяется следующим образом:

  • тип элемента, который появляется в начале объявления; затем,

  • любые пары скобок, которые следуют за Идентификатором переменной в деклараторе (не применимо для параметра с переменной арностью или компонента записи); затем,

  • любые пары скобок, которые появляются в типе в начале объявления (где многоточие параметра с переменной арностью или компонента записи с переменной арностью обрабатывается как пара скобок).

Тип результата метода (§8.4.5) может быть типом массива. Точный тип массива зависит от пар скобок, которые могут появляться в части типа в начале объявления метода или после списка формальных параметров метода, или и то, и другое. Тип массива определяется следующим образом:

  • тип элемента, который появляется в Результате; затем,

  • любые пары скобок, которые следуют за списком формальных параметров; затем,

  • любые пары скобок, которые появляются в Результате.

Не рекомендуется использовать «смешанную запись» в объявлениях переменных массивов, где пары скобок появляются как в типе, так и в деклараторах; также не рекомендуется использовать в объявлениях методов, где пары скобок появляются как до, так и после списка формальных параметров.

Пример 10.2-2. Переменные массивов и типы массивов

Выражение объявления локальной переменной:

byte[] rowvector, colvector, matrix[];

эквивалентно:

byte rowvector[], colvector[], matrix[][];

поскольку тип массива каждой локальной переменной не меняется. Аналогично, выражение объявления локальной переменной:

int a, b[], c[][];

эквивалентно серии операторов объявления:

int a;
int[] b;
int[][] c;

Скобки разрешены в деклараторах как дань традициям C и C++. Однако общие правила объявления переменных позволяют скобкам появляться как в типе, так и в деклараторах, так что выражение объявления локальной переменной:

float[][] f[][], g[][][], h[];  // Yechh!

эквивалентно серии объявлений:

float[][][][] f;
float[][][][][] g;
float[][][] h;

Из-за того, как формируются типы массивов, следующие объявления параметров имеют одинаковый тип массива:

void m(int @A [] @B []  x) {}
void n(int @A [] @B ... y) {}

И, возможно, неожиданно, следующие объявления полей имеют одинаковый тип массива:

int @A [] f @B [];
int @B [] @A [] g;

После создания объекта массива его длина никогда не изменяется. Чтобы сделать переменную массива ссылкой на массив другой длины, переменной необходимо присвоить ссылку на другой массив.

Одна переменная типа массива может содержать ссылки на массивы различной длины, потому что длина массива не является частью его типа.

Если переменная массива v имеет тип A[], где A — тип ссылки, то v может содержать ссылку на экземпляр любого типа массива B[], при условии, что B можно присвоить A (§5.2). Это может привести к исключению во время выполнения на позднем присваивании; см. §10.5 для обсуждения.

10.3. Создание массивов

Массив создаётся с помощью выражения создания массива (§15.10.1) или инициализатора массива (§10.6).

Выражение создания массива указывает тип элемента, количество уровней вложенных массивов и длину массива по крайней мере для одного из уровней вложенности. Длина массива доступна как final переменная экземпляра length.

Инициализатор массива создаёт массив и предоставляет начальные значения для всех его компонентов.

10.4. Доступ к элементам массива

Компонент массива доступен через выражение доступа к массиву (§15.10.3), которое состоит из выражения, значение которого является ссылкой на массив, за которым следует выражение индексации, заключённое в [ и ], как в A[i].

Все массивы имеют 0 нумерацию. Массив с длиной n может быть индексирован целыми числами 0 до n-1.

Пример 10.4-1. Доступ к элементам массива

class Gauss {
    public static void main(String[] args) {
        int[] ia = new int[101];
        for (int i = 0; i < ia.length; i++) ia[i] = i;
        int sum = 0;
        for (int e : ia) sum += e;
        System.out.println(sum);
    }
}

Эта программа выводит:

5050

Программа объявляет переменную ia, которая имеет тип массива int, то есть int[]. Переменная ia инициализируется ссылкой на вновь созданный объект массива, созданный выражением создания массива (§15.10.1). Выражение создания массива указывает, что массив должен иметь 101 компонентов. Длина массива доступна с помощью поля length, как показано. Программа заполняет массив целыми числами от 0 до 100, суммирует эти целые числа и выводит результат.


Массивы должны индексироваться int значениями; short, byte или char значения также могут использоваться в качестве значений индекса, поскольку они подвергаются числовому преобразованию с приведением к int значениям.

Попытка доступа к компоненту массива с long значением индекса приводит к ошибке компиляции.

Все обращения к массиву проверяются во время выполнения; попытка использования индекса, который меньше нуля или больше или равен длине массива, вызывает исключение ArrayIndexOutOfBoundsException (§15.10.4).

10.5. Исключение Array Store Exception

Для массива, тип которого A[], где A — тип ссылки, присвоение элементу массива проверяется во время выполнения, чтобы убедиться, что присваиваемое значение совместимо с типом элемента.

Если тип присваиваемого значения не совместим с типом элемента (§5.2), выбрасывается исключение ArrayStoreException.

Если тип элемента массива не был бы реифицируемым (§4.7), виртуальная машина Java не могла бы выполнить проверку сохранения, описанную в предыдущем абзаце. Вот почему выражение создания массива с нереифицируемым типом элемента запрещено (§15.10.1). Можно объявить переменную типа массива с нереифицируемым типом элемента, но присвоение результата выражения создания массива переменной неизбежно вызовет предупреждение об unchecked-преобразовании (§5.1.9).

Пример 10.5-1. ArrayStoreException

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Эта программа выводит:

true
java.lang.ArrayStoreException: Point

Переменная pa имеет тип Point[], а переменная cpa имеет значение ссылки на объект типа ColoredPoint[]. Ссылка на объект типа ColoredPoint может быть присвоена переменной типа Point; поэтому значение cpa может быть присвоено pa.

Ссылка на этот массив pa, например, проверка того, является ли pa[1] объектом типа null, не приведет к ошибке во время выполнения. Это потому, что элемент массива типа ColoredPoint[] является объектом типа ColoredPoint, а каждый объект типа ColoredPoint может быть приведён к типу Point, так как Point — это суперкласс класса ColoredPoint.

С другой стороны, присвоение элементу массива pa может привести к ошибке во время выполнения. На этапе компиляции присвоение элементу массива pa проверяется, чтобы убедиться, что присваиваемое значение является объектом типа Point. Но так как pa содержит ссылку на массив объектов типа ColoredPoint, присвоение является допустимым только в том случае, если тип присваиваемого значения во время выполнения — это, более конкретно, объект типа ColoredPoint.

Виртуальная машина Java проверяет это условие во время выполнения, чтобы убедиться, что присвоение является корректным; если нет, выбрасывается исключение ArrayStoreException.


10.6. Инициализаторы массивов

Инициализатор массива может быть указан в объявлении поля (§8.3, §9.3) или объявлении локальной переменной (§14.4), или как часть выражения создания массива (§15.10.1), для создания массива и предоставления некоторых начальных значений.

ArrayInitializer:
{ [Список инициализаторов переменных] [,] }
VariableInitializerList:
Инициализатор переменной {, Инициализатор переменной}

Следующая продукция из §8.3 показана здесь для удобства:

VariableInitializer:
Выражение
Инициализатор массива

Инициализатор массива записывается как список выражений, разделенных запятыми, заключенные в фигурные скобки { и }.

Запятая после последнего выражения в инициализаторе массива может быть опущена и игнорируется.

Каждый инициализатор переменной должен быть совместим с типом элемента массива (§5.2), в противном случае возникает ошибка компиляции.

Если тип элемента массива, который инициализируется, не является реифицируемым (§4.7), возникает ошибка компиляции.

Длина создаваемого массива равна числу инициализаторов переменных, заключенных в фигурные скобки инициализатора массива. Выделяется память для нового массива указанной длины. Если памяти недостаточно для выделения массива, выполнение инициализатора массива прерывается с выбрасыванием исключения OutOfMemoryError. В противном случае создается одномерный массив заданной длины, и каждый элемент массива инициализируется своим значением по умолчанию (§4.12.5).

Затем инициализаторы переменных, заключенные в фигурные скобки инициализатора массива, выполняются слева направо в текстовом порядке их появления в исходном коде. n'й инициализатор переменной задаёт значение n-го компонента массива. Если выполнение инициализатора переменной прерывается, то выполнение инициализатора массива прерывается по той же причине. Если все выражения инициализаторов переменных завершаются успешно, инициализатор массива завершается успешно, возвращая значение инициализированного массива.

Если тип элемента — это тип массива, то инициализатор переменной, задающий элемент, сам может быть инициализатором массива; то есть инициализаторы массивов могут быть вложены. В этом случае выполнение вложенного инициализатора массива создаёт и инициализирует массив-объект с рекурсивным применением алгоритма выше и присваивает его элементу.

Пример 10.6-1. Инициализаторы массивов

class Test {
    public static void main(String[] args) {
        int[][] ia = { { 1, 2 }, null };
        for (int[] ea : ia) {
            for (int e: ea) {
                System.out.println(e);
            }
        }
    }
}

Эта программа выводит:

1
2

что приводит к NullPointerException при попытке обратиться ко второму элементу массива ia, который является null-ссылкой.


10.7. Члены массивов

Членами типа массива являются следующие:

  • Поле public final length, которое содержит количество элементов массива. length может быть положительным или нулевым.

  • Метод public clone, который переопределяет метод с тем же именем в классе Object и не выбрасывает исключения. Тип возвращаемого значения метода clone типа массива T[] — T[].

    Клон многомерного массива является поверхностным, то есть создаёт только один новый массив. Подмассивы разделяются.

  • Все члены, унаследованные от класса Object; единственный метод класса Object, который не унаследован, — это его метод clone.

    См. §9.6.4.4 для другой ситуации, где разница между аннотированными и неаннотированными методами класса Object требует особого внимания.

Таким образом, массив имеет те же поля и методы, что и следующий класс:


class A<T> implements Cloneable, java.io.Serializable {
    public final int length = X;
    public T[] clone() {
        try {
            return (T[])super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}

Обратите внимание, что приведение к типу T[] в приведенном выше коде сгенерирует предупреждение об unchecked-преобразовании (§5.1.9), если массивы действительно реализованы таким образом.

Пример 10.7-1. Массивы могут быть клонированы

class Test1 {
    public static void main(String[] args) {
        int[] ia1 = { 1, 2 };
        int[] ia2 = ia1.clone();
        System.out.print((ia1 == ia2) + " ");
        ia1[1]++;
        System.out.println(ia2[1]);
    }
}

Эта программа выводит:

false 2

показывая, что компоненты массивов, на которые ссылаются ia1 и ia2, являются различными переменными.


Пример 10.7-2. Разделяемые подмассивы после клонирования

Факт, что подмассивы разделяются при клонировании многомерного массива, демонстрируется в этой программе:

class Test2 {
    public static void main(String[] args) throws Throwable {
        int[][] ia = { { 1, 2 }, null };
        int[][] ja = ia.clone();
        System.out.print((ia == ja) + " ");
        System.out.println(ia[0] == ja[0] && ia[1] == ja[1]);
    }
}

Эта программа выводит:

false true

показывая, что массив int[], который ia[0], и массив int[], который ja[0], — это один и тот же массив.


10.8. Class Объекты для массивов

Каждый массив имеет связанный с ним Class объект, общий для всех других массивов с одинаковым типом компонентов.

Хотя тип массива не является классом, Class объект каждого массива ведет себя так, как будто:

  • Прямым суперклассом каждого типа массива является Object.

  • Каждый тип массива реализует интерфейсы Cloneable и java.io.Serializable.

Пример 10.8-1. Class Объект массива

class Test1 {
    public static void main(String[] args) {
        int[] ia = new int[3];
        System.out.println(ia.getClass());
        System.out.println(ia.getClass().getSuperclass());
        for (Class<?> c : ia.getClass().getInterfaces())
            System.out.println("Superinterface: " + c);
    }
}

Эта программа выводит:

class [I
class java.lang.Object
Superinterface: interface java.lang.Cloneable
Superinterface: interface java.io.Serializable

где строка "[I" — это сигнатура типа во время выполнения для Class объекта "массив с типом компонентов int".


Пример 10.8-2. Объекты массивов Class являются общими

class Test2 {
    public static void main(String[] args) {
        int[] ia = new int[3];
        int[] ib = new int[6];
        System.out.println(ia == ib);
        System.out.println(ia.getClass() == ib.getClass());
    }
}

Эта программа выводит:

false
true

Хотя ia и ib ссылаются на разные массивы, результат сравнения Class объектов демонстрирует, что все массивы, компоненты которых имеют тип int, являются экземплярами одного и того же типа массива (а именно int[]).


10.9. Массив символов не является String

В языке программирования Java, в отличие от C, массив char не является String, и ни String, ни массив char не завершается '\u0000' (символом NUL).

Объект String является неизменяемым, то есть его содержимое никогда не меняется, в то время как массив char имеет изменяемые элементы.

Метод toCharArray в классе String возвращает массив символов, содержащий ту же последовательность символов, что и String. Класс StringBuffer реализует полезные методы для изменяемых массивов символов.

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API