Spec-Zone.ru › Java Language Specification 17

Глава 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). Можно объявить переменную типа массива, тип элементов которого не реифицируем, но присваивание результата выражения создания массива переменной неизбежно вызовет предупреждение о неявном преобразовании (§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-1-го компонента массива. Если выполнение инициализатора переменной завершается аварийно, выполнение инициализатора массива завершается аварийно по той же причине. Если все выражения инициализатора переменной завершаются нормально, инициализатор массива завершается нормально со значением только что инициализированного массива.

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

Пример 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 для другой ситуации, где различие между public и не-public методами 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[] в приведенном выше коде сгенерирует предупреждение о неявном преобразовании (§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