Опубликован: 04.12.2009 | Доступ: свободный | Студентов: 8231 / 585 | Оценка: 4.30 / 3.87 | Длительность: 27:27:00
Лекция 7:

Важнейшие объектные типы

Аннотация: Массивы. Коллекции, списки, итераторы. Работа со строками в Java. Строки как объекты. Классы String, StringBuffer и StringBuilder. Работа с графикой. Исключительные ситуации. Обработка исключительных ситуаций. Иерархия исключительных ситуаций. Объявление типа исключительной ситуации и оператор throw. Объявление метода, который может возбуждать исключительную ситуацию. Зарезервированное слово throws. Работа с файлами и папками.
Ключевые слова: массив, array, доступ, индекс, Java, переменная, адрес, объект, объектная переменная, примитивный тип, ссылочный тип, класс, объектный тип, создание массива, тип массива, базовый тип, ячейка, число элементов в массиве, нумерация, длина, поле, значение, ссылка, присваивание, Окружность, быстродействие, двумерный массив, одномерный массив, clone, копирование, исключительная ситуация, сравнение массивов, сортировка, равенство, массивы объектов, интерфейс, AWT, ArrayList, vectorizer, Hashtable, итератор, ITERATE, IsEmpty, IndexOf, sublist, frequency, константы, ABCD, управляющие, управляющая последовательность, строковый, символьные типы, ASCII, операции, выражение, эквивалентное выражение, передача параметров, строковый тип, вывод, компилятор, массив символов, ISO-8859, UTF-16, LastIndexOf, trim, представление, хэш-код, сложение, конкатенация, ожидаемое значение, операнд, сложение строк, строковая функция, обратное преобразование, short integer, decode, JDK, функция, высота, координаты, observation, 'clip', деструктор, класс приложений, время выполнения, вызов метода, графическая система, перехват данных, тип исключения, проверяемое исключение, совместимость типов, классы исключений, объявление функции, непроверяемое исключение, тело метода, родительский класс, возбуждение исключительной ситуации, атрибут файла, поток ввода-вывода, относительная адресация, prefix, suffix, вложенная папка, палитра компонентов, экранная форма, Swing, переменная класса, архивация, абстрактный класс, абстрактный метод, skip, flush, reader, seek, pos, запись в файл, возврат каретки, файловая переменная, зарезервированное слово, ENUM, enumerate, перечисление, ordinal, Spring, идентификатор переменной, MARS, APR, операторы

7.1. Массивы

Массив (array) – это упорядоченный набор одинаково устроенных ячеек, доступ к которым осуществляется по индексу. Например, если у массива имя a1, то a1[i] – имя ячейки этого массива, имеющей индекс i.

В Java массивы являются объектами, но особого рода – их объявление отличается от объявления других видов объектов. Переменная типа массив является ссылочной – в ней содержится адрес объекта, а не сам объект, как и для всех других объектных переменных в Java. В качестве элементов (ячеек) массива могут выступать значения как примитивных типов, так и ссылочных типов, в том числе – переменные типа массив.

Тип ячейки массива называется базовым типом для массива.

Для задания массива, в отличие от объектов других типов, не требуется предварительно задавать класс, и иметь специальное имя для данного объектного типа. Вместо имени класса при объявлении переменной используется имя базового типа, после которого идут пустые квадратные скобки.

Например, объявление

int[] a1;

задает переменную a1 типа массив. При этом размер массива (число ячеек в нем) заранее не задается и не является частью типа.

Для того, чтобы создать объект типа массив, следует воспользоваться зарезервированным словом new, после чего указать имя базового типа, а за ним в квадратных скобках число ячеек в создаваемом массиве:

a1=new int[10];

Можно совместить объявление типа переменной и создание массива :

int[] a1=new int[10];

После создания массивы Java всегда инициализированы – в ячейках содержатся нули. Поэтому если базовый тип массива примитивный, элементы массива будут нулями соответствующего типа. А если базовый тип ссылочный – в ячейках будут значения null.

Ячейки в массиве имеют индексы, всегда начинающиеся с нуля. То есть первая ячейка имеет номер 0, вторая – номер 1, и так далее. Если число элементов в массиве равно n, то последняя ячейка имеет индекс n-1. Такая своеобразная нумерация принята в языках C и C++, и язык Java унаследовал эту не очень привлекательную особенность, часто приводящую к ошибкам при организации циклов.

Длина массива хранится в поле length, которое доступно только по чтению – изменять его путем присваивания нового значения нельзя.

Пример работы с массивом:

int[] a=new int[100];
for(int i=0;i<a.length;i++){
  a[i]=i+1;
};

Если у нас имеется переменная типа массив, и ей сопоставлен массив заданной длины, в любой момент этой переменной можно сопоставить новый массив. Например,

a1=new int[20];

При этом прежний объект-массив, находящийся в динамической области памяти, будет утерян и превратится в мусор.

Переменные типа массив можно присваивать друг другу. Например, если мы задали переменную

int[] a2;

то сначала в ней хранится значение null (ссылка направлена "в никуда"):

Массив с ячейками типа int

Рис. 7.1. Массив с ячейками типа int

Присваивание

a2=a1;

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

Массив с ячейками типа int

Рис. 7.2. Массив с ячейками типа int

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

В качестве элементов массивов могут выступать объекты. В этом случае доступ к полям и методам этих объектов производится через имя ячейки массива, после которого через точку указывается имя поля или метода. Например, если у нас имеется класс Circle ("окружность"), у которого имеются поля x, y и r, а также методы show() и hide(), то массив circles из 10 объектов такого типа может быть задан и инициализирован, например, так

int n=10;
Circle[] circles=new Circle[n];
for(int i=0;i<n;i++){
 circles[i]=new Circle();
 circles[i].x=40*i;
 circles[i].y= circles[i].x/2;
 circles[i].r=50;
 circles[i].show();
};

В такого рода программах для повышения читаемости часто применяется использование вспомогательной ссылки, позволяющей избежать многократного обращения по индексу. В нашем случае мы будем использовать в этих целях переменную circle. На скорости работы программы это почти не сказывается (хотя и может чуть повысить быстродействие), но делает код более читаемым:

int n=10;
Circle[] circles=new Circle[n];
Circle circle;
for(int i=0;i<n;i++){
 circle=new Circle();
 circle.x=40*i;
 circle.y= circles[i].x/2;
 circle.r=50;
 circle.show();
 circles[i]= circle;
};

С помощью переменной circle мы инициализируем создаваемые объекты и показываем их на экране, после чего присваиваем ссылку на них ячейкам массива.

Двумерный массив представляет собой массив ячеек, каждая из которых имеет тип "одномерный массив". Соответствующим образом он и задается. Например, задание двумерного массива целых чисел будет выглядеть так:

int[][] a=new int[10][20];

Будет задана ячейка типа "двумерный массив", а также создан и назначен этой ссылочной переменной массив, имеющий по первому индексу 10 элементов, а по второму 20. То есть мы имеем 10 ячеек типа "одномерный массив", каждая из которых ссылается на массив из 20 целых чисел. При этом базовым типом для ячеек по первому индексу является int[], а для ячеек по второму индексу int.

Рассмотрим работу с двумерными массивами на примере заполнения двумерного массива случайными числами:

int m=10;//10 строк
int n=20;//20 столбцов
int[][] a=new int[m][n];
for(int i=0;i<m;i++){ //цикл по строкам
    for(int j=0;j<n;j++){ //цикл по столбцам
        a[i][j]=(int)(100*Math.random());
        System.out.print(a[i][j]+" ");
    };
    System.out.println();//перевод на новую строку после вывода строки матрицы
};

Многомерные массивы задаются аналогично двумерным – только указывается необходимое количество прямоугольных скобок. Следует отметить, что массивы размерности больше 3 используют крайне редко.

Обычно в двумерных и многомерных массивах задают одинаковый размер всех массивов, связанных с ячейками по какому-либо индексу. Такие массивы называют регулярными. В Java, в отличие от большинства других языков программирования, можно задавать массивы с разным размером массивов, связанных с ячейками по какому-либо индексу. Такие "непрямоугольные" массивы называют иррегулярными. Обычно их используют для экономии памяти. При работе с иррегулярными массивами следует быть особенно аккуратными, так как разный размер "вложенных" массивов часто приводит к ошибкам при реализации алгоритмов.

Пример задания иррегулярного двумерного массива треугольной формы:

int n=9;
int[][] a=new int[n][];
for(int i=0;i<a.length;i++){ //цикл по строкам
    a[i]=new int[i+1]; //число элементов в строке равно i+1
    for(int j=0;j<a[i].length;j++){ //цикл по столбцам
        a[i][j]=100*i+j;
        System.out.print(a[i][j]+" ");
    };
    System.out.println();//перевод на новую строку после вывода строки матрицы
};

После создания массива требуется его инициализировать – записать нужные значения в ячейки. До сих пор мы делали это путем задания значений в цикле по некоторой формуле, однако часто требуется задать конкретные значения. Конечно, можно это сделать в виде

int[] a=new int[4];
a[0]=2;
a[1]=0;
a[2]=0;
a[3]=6;

Но гораздо удобнее следующий вариант синтаксиса:

int[] a=new int[] {2,0,0,6};

При этом приходится задавать массив без указания его размера непосредственно с помощью указания значений в фигурных скобках:

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

Если мы хотим присвоить новые значения, приходится либо присваивать поэлементно, либо создавать новый объект:

a=new int[] {2,0,0,6};

При инициализации двумерных и многомерных массивов используют вложенные массивы, задаваемые с помощью фигурных скобок. Например, фрагмент кода

int[][] b= new int[][]
{
  {2,0,0,0}, //это b[0]
  {2,0,0,1}, //это b[1]
  {2,0,0,2}, //это b[2]
  {1,0,0,0}, //это b[3]
  {2,0,0,0}, //это b[4]
  {3,0,0,0}, //это b[5]
};

приведет к заданию целочисленного двумерного массива b, состоящего из 6 строк и 4 столбцов, т.е. int[6][4]. Таким образом можно задавать как регулярные, так и иррегулярные массивы. Но следует помнить, что в таком варианте синтаксиса проверки правильности размера массива по индексам не делается, что может привести к ошибкам. Например, следующий код при компиляции не выдаст ошибки, а будет создан иррегулярный массив:

int[][] b= new int[][]
{
  {2,0,0,0}, //это b[0]
  {2,0,0,1}, //это b[1]
  {2,0,0,2}, //это b[2]
  {1,0,0,0}, //это b[3]
  {2,0,0,0}, //это b[4]
  {3,0,0}, //это b[5] – массив из трех элементов
};

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

a=new int[] {2,0,0,6};
int[] a1=a.clone();

Напомним, что присваивание

int[] b=a;

не приведет к копированию массива – просто переменная b станет ссылаться на тот же объект-массив. Копирование массивов можно осуществлять в цикле, но гораздо быстрее использовать метод System.arraycopy.

int[] b=new int[a.length+10];
System.arraycopy(a,index1a,b, index1b,count);

Из a в b копируется count элементов начиная с индекса index1a в массиве a. Они размещаются в массиве b начиная с индекса index1b. Содержимое остальных элементов b не меняется. Для использования метода требуется, чтобы массив b существовал и имел необходимую длину - при выходе за границы массивов возбуждается исключительная ситуация.

Быстрое заполнение массива одинаковыми значениями может осуществляться методом Arrays.fill(массив, значение). Класс Arrays расположен в пакете java.util.

Поэлементное сравнение массива следует выполнять с помощью метода Arrays.equals(a,a1). Заметим, что у любого массива имеется метод equals, унаследованный от класса Object и позволяющий сравнивать массивы. Но, к сожалению, метод не переопределен, и сравнение идет по адресам объектов, а не по содержимому. Поэтому a.equals(a1) это то же самое, что a==a1. Оба сравнения вернут false, так как адреса объектов, на которые ссылаются переменные a и a1, различаются. Напротив, сравнения a.equals(a3) и a==a3 вернут true, так как a и a3 ссылаются на один и тот же объект-массив.

Сортировка (упорядочение по значениям) массива a производится методами Arrays.sort(a) и Arrays.sort(a,index1,index2). Первый из них упорядочивает в порядке возрастания весь массив, второй – часть элементов (от индекса index1 до индекса index2 ). Имеются и более сложные методы сортировки. Элементы массива должны быть сравниваемы (поддерживать операцию сравнения).

Arrays.deepEquals(a1,a2) – сравнение на равенство содержимого массивов объектов a1 и a2 путем глубокого сравнения (на равенство содержимого, а не ссылок – на произвольном уровне вложенности).

Также в классе Arrays содержится большое число других полезных методов.

Максим Старостин
Максим Старостин

Код с перемещением фигур не стирает старую фигуру, а просто рисует новую в новом месте. Точку, круг.

Александр Мухаметов
Александр Мухаметов
Казахстан