Java — В JAVA что с массивом, если его ссылка ссылается на дургой


Содержание

Занятие 2

Методы

Понятия функции и метода

Функции используются в программировании, чтобы уменьшить его сложность:

  1. Вместо того, чтобы писать непрерывную последовательность команд, в которой вскоре перестаешь ориентироваться, программу разбивают на подпрограммы, каждая из которых решает небольшую законченную задачу, а потом большая программа составляется из этих подпрограмм (этот прием называется декомпозицией).
  2. Уменьшается общее количество кода, потому что, как правило, одна функция используется в программе несколько раз.
  3. Написанная однажды и всесторонне проверенная функция, может быть включена в библиотеку функций и использоваться в других программах (при этом не надо вспоминать, как была запрограммирована эта функция, достаточно знать, что она делает). Существует множество полезных библиотек функций, которыми могут пользоваться все программисты, а некоторые библиотеки поставляются «в комплекте» с языком программирования (например, все, кто программировал на Паскале, пользовались библиотечной функцией writeln() для вывода на экран, а в Java для этих целей доступен метод System.out.println() , входящий в одну из стандартных библиотек).

В языке Java вся программа состоит только из классов и функции могут описываться только внутри них. Именно поэтому все функции в языке Java являются методами.

Суть понятия метод рассматривается на следующем занятии. А пока мы можем использовать его как синоним знакомого (по другим языкам программирования) понятия функция.

Объявление метода

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

При объявлении метода необходимо указать тип значения, которое будет возвращено после выполнения метода в программу. Если значение возвращать не нужно, указывается ключевое слово void . Затем идет произвольный идентификатор — имя метода. После имени метода в круглых скобках указывается список параметров (может быть пустым), а затем — в фигурных скобках — команды, составляющие тело метода.

Например, метод, рисующий круг, должен получить радиус и координаты центра круга. (Можно, конечно, было бы разработать метод без параметров, который рисует круг единичного радиуса с центром в начале координат, но он был бы значительно менее полезен).

Описание каждого параметра аналогично объявлению переменной (тип, а затем идентификатор — имя параметра). Параметры перечисляются через запятую.

В теле метода, возвращающего значение, должна быть команда return , после которой через пробел указывается выражение соответствующего типа. Эта команда заканчивает работу метода и передает указанное выражение в качестве возвращаемого значения основной программе — в то место, откуда метод был вызван * .

В качестве примера создадим метод для вычисления суммы квадратов двух целых чисел. Как и в случае с программой, важно в первую очередь определить входные и выходные данные. Входные данные — это параметры метода. Выходные данные — это его возвращаемое значение.

Замечание: в объектно-ориентированной концепции акцент несколько иной. Методы могут получать данные для работы, обращаясь к атрибутам своего класса, а результат их работы может заключаться в изменении этих атрибутов. Однако в рамках данного раздела мы рассматриваем методы как классические функции — подпрограммы.

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

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

long squearSum( int x, int y)

Перед возвращаемым типом указывается одно или несколько ключевых слов модификаторов (которые будут изучены позднее). Нам необходимо добавить к объявлению метода squearSum() модификатор static , поскольку мы собираемся обращаться к нему из метода main() , имеющего такой модификатор.

Описание метода squearSum() должно находиться внутри того единственного класса, из которого состоит наша простая программа, но не внутри метода main() , а на одном уровне с ним. То есть:

В результате в классе MyClass теперь два метода, один из которых, main() , выполняется при старте программы. Чтобы выполнился второй метод, его необходимо вызвать.

Вызов метода

Чтобы вызвать метод из другого метода того же класса, необходимо указать его имя, а затем в скобках список фактических параметров (если метод не требует параметров, скобки все равно ставятся).

Например, мы можем обратиться к описанному нами методу squearSum(), передав ему в качестве параметров два целых числа 10 и 20 следующим образом:

В консоль будет выведено число 501.

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

Чтобы вызвать метод другого класса, необходимо иметь объект этого класса * . Имя метода указывается через точку после имени объекта.

Например, в классе String (строка) имеется метод length() , возвращающий длину строки. Обратиться к этому методу можно только через объект класса String (что вполне логично, метод вызывается для той строки, длину которой мы хотим узнать).

String S = «Привет» ; // Создание объекта класса String, подробнее см. ниже int x = S.length(); // Вызов метода length() для объекта S. В результате x = 6

Подробнее о вызове методов других классов будет рассказано позже.

В дальнейшем, рассказывая о каких-либо методах, мы будем называть их не только именем, но и для наглядности указывать в скобках параметры метода, например сharAt(int i) . В результате становится легко объяснить назначение метода: «Метод charAt(int i) возвращает символ строки с индексом i ». Тип возвращаемого значения мы будем указывать только при необходимости.

Упражнение 1

Напишите метод, toUSD( int rur, double course) , переводящий рубли в доллары по заданному курсу. Вызовите его дважды в методе main() c любыми параметрами, результат напечатейте в консоль.

Массивы

Определения

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

Создание массива происходит в два этапа.

1. Объявление массива. На этом этапе создается переменная типа ссылка на массив, с помощью которой мы сможем потом к массиву обращаться. Для этого указывается тип элементов массива, затем квадратные скобки (они показывают, что мы имеем дело с массивом, а не с обычным типом данных) и идентификатор — имя массива. Идентификаторов может быть несколько (как и в случае с переменными простых типов).

int [] a; // Создается ссылка на массив типа int double [] b, c; // Создаются две ссылки на массивы типа double

2. Создание массива. Создать массив — значит выделить в памяти место, достаточное для хранения всех его элементов. Для этого надо указать длину массива — количество элементов в нем. Кроме того, переменная-ссылка, объявленная на предыдущем этапе, теперь будет «указывать» не в пустоту (в Java эта «пустота» называется null ), а на конкретный массив, с элементами которого можно работать.

Массив создается операцией new , которая выделяет участок памяти и возвращает указатель на этот участок. После ключевого слова new должен быть указан тип данных массива и его длина в квадратных скобках:

a = new int [5]; // В памяти выделяется место под массив из пяти целочисленных элементов, переменная a будет указывать на этот массив b = new double [4]; // В памяти выделяется место под массив из четырех действительных элементов, на него указывает переменная b

При этом элементам массива присваиваются значения по умолчанию * . Можно сразу проинициализировать массив нужными значениями, если перечислить их через запятую в фигурных скобках (длина массива при этом не указывается):

c = new double []<2.3, 1.02, 8>; // В памяти выделяется место под массив из трех действительных элементов, на него указывает переменная с, элементы массива сразу получают нужные значения

Работа с массивом

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

a[0] = 5; // Первому элементу массива a, присваивается значение 5 a[3] = 17; // Четвертому элементу массива a, присваивается значение 17 a[1] = a[0] – a[3]; // Второму элементу массива a присваивается значение -12

Объявление, создание и инициализацию массива можно соместить. Например:

int [] a = new int [5];

int [] a = new int [] <5, -12, 0, 17, 0>; // длина массива не указывается

Подчеркнем еще раз, что переменные a , b и c являются не собственно массивами, а только ссылками на массивы. Это значит, что можно заставить ссылку показывать на другой массив (если он соответствующего типа). Это делается командой присваивания:

В результате выполнения такой команды переменные b и c будут ссылаться (указывать) на один и тот же массив. Т.е., например, b[0] и c[0] — это теперь один и тот же элемент. А тот массив, на который раньше указывала переменная b , больше недоступен (поскольку на него не указывает ни одна ссылка) и будет удален из памяти так называемым сборщиком мусора Java.

Можно присвоить ссылке «пустое значение» null и тогда она не будет ссылаться ни на какую область памяти:

Обратите внимание, что с массивом, на который показывала переменная b , ничего не случится, просто обратиться к нему теперь можно будет только через переменную c . А вот если бы мы присвоили null переменной a , ее целочисленный массив был бы уничтожен.

Длину массива можно определить, используя запись:

Например, a.length будет равняться 5.

Очень удобно перебирать все элементы массива в цикле типа for . При этом обычно используется следующая форма:

for ( int i = 0; i // здесь можно что-нибудь сделать с элементом a[i] >

Например, следующий код присваивает всем элементам массива b числа от 1 до 4 (поскольку в массиве b четыре элемента):

for ( int i = 0; i b[i] = i; >

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

a[5] = 8; // Нельзя, в массиве a только 5 элементов: a[0], a[1], a[2], a[3], a[4]

Многомерные массивы

Элементами массивов в Java могут быть другие массивы. Например, можно объявить:

Затем выделяется область памяти для внешнего массива:

Размеры внутренних массивов при этом указывать не нужно, это делается на следующем шаге:

d[0] = new char[3]; d[1] = new char[3]; d[2] = new char[2];

Теперь можно обращаться к элементам многомерного массива, используя два индекса: сначала индекс внешнего, а потом внутреннего массива: d[1][2] , d[0][0] , d[0][1] и т.д.

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

Упражнение 2

Напишите метод, увеличивающий элементы массива на 10%.

Подсказка: подумайте, какие у этого метода входные и выходные данные. Решите сами, должен ли метод изменять исходный массив, или в результате работы будет создаваться новый. Обоснуйте свое решение преподавателю. Напишите к методу комментарии.

Работа со строками

Создание строк

Строки тоже являются переменными ссылочного типа, а точнее — ссылками на объекты одного из нескольких строковых классов Java. Мы рассмотрим класс String .

Самый распространенный способ создать строку — это организовать ссылку типа String на строку-константу:

String s = «Это строка»

Можно просто сначала объявить переменную (которая получит значение null ), а потом заставить ее ссылаться на строку-константу, другую строку или воспользоваться командой new , чтобы явным образом выделить память для строки:

String s1, s2, s3; // Объявление трех переменных, которые пока не связаны ни с какой строкой s1 = «Да здравствует день танкиста»; // Переменная s1 теперь ссылается на область памяти, в которой хранится строка «Да здравствует день танкиста» s2 = s1; // Теперь обе переменные s1 и s2 ссылаются на одно и то же место памяти s3 = new String(); // s3 ссылается на место в памяти, где хранится пустая строка

Объединение (сцепление) строк

Сцепление строк производится командой +. Ей соответствует оператор +=, который часто бывает очень удобен:

String S = «Привет» ; String S1 = «мир» ; S += «, » + S1 + «!» ; // Теперь S ссылается на строку “Привет, мир!”

Длина строки

Определить длину строки можно методом length() :

int x = S.length(); // Переменная x получит значение 12

Обратите внимание, String является классом (подробнее классы рассматриваются на следующем занятии), а length() — его методом, и поэтому указывается через точку после имени переменной. Аналогично записываются и другие методы класса String .

Получение отдельных символов строки

Метод charAt(int i) возвращает символ строки с индексом i . Индекс первого символа строки — 0 (т.е. символы строки индексируются (нумеруются) аналогично элементам массива. Например:

char ch = S.charAt(2); // Переменная ch будет иметь значение ‘и’

Метод toCharArray() преобразует строку в массив символов:

char [] ch = S.toCharArray(); // ch будет иметь представлять собой массив

Метод getChars(int begin, int end, char dst[], int i) берет символы строки, имеющие индексы от begin до end-1 включительно, и помещает их в массив dst , начиная с индекса i , и выдает получившийся массив в качестве результата. Например:

char [] ch = < 'М' , 'а' , 'с' , 'с' , 'и' , 'в' , '.' >; ch = S.getChars(1, 4, ch, 3); // Теперь ch =

Замена отдельного символа

Метод replace(int old, int new) возвращает новую строку, в которой все вхождения символа old заменены на символ new .

String SS = S.replace( ‘и’ , ‘ы’ ); // SS = «Прывет, мыр!»

Получение подстроки

Метод substring(int begin, int end) возвращает фрагмент исходной строки от символа с индексом begin до символа с индексом end-1 включительно. Если не указывать end , будет возвращен фрагмент исходной строки, начиная с символа с индексом begin и до конца:

String S2 = S.substring(4,7); // S2 = «ет,» S2 = S.substring(4); // S2 = «ет, мир!»

Разбиение строки на подстроки

Метод split(String regExp) разбивает строку на фрагменты, используя в качестве разделителей символы, входящие в параметр regExp , и возвращает ссылку на массив, составленный из этих фрагментов. Сами разделители ни в одну подстроку не входят.

String parts[] = S.split( » » ); // Разбили строку S на отдельные слова, используя пробел в качестве разделителя, в результате получили массив parts, где parts[0] = «Привет,», а parts[1] = «мир!» String parts[] = S.split( » и» ); // Разбили строку S на отдельные слова, используя в качестве разделителя пробел и букву и, в результате parts[0] = «Пр», parts[1] = «вет,», parts[2] = «м», parts[3] = «р!»

Сравнение строк

Если сравнивать строки, используя логическую операцию == , то ее результатом будет true только в том случае, если строковые переменные указывают (ссылаются) на один и тот же объект в памяти.

Если же необходимо проверить две строки на совпадение, следует использовать стандартный метод equals(Object obj) . Он возвращает true , если две строки являются полностью идентичными вплоть до регистра букв, и false в противном случае. Его следует использовать следующим образом:

S1.equals(S2); // Вернет true, если строки S1 и S2 идентичны S2.equals(S1); // Абсолютно то же самое boolean b = S.equals( «Привет, мир!» ); // b = true

Метод equalsIgnoreCase(Object obj) работает аналогично, но строки, записанные в разных регистрах, считает совпадающими.

Поиск подстроки

Метод indexOf(int ch) возвращает индекс первого вхождения символа ch в исходную строку. Если задействовать этот метод в форме indexOf(int ch, int i) , то есть указать два параметра при вызове, то поиск вхождения начнется с символа с индексом i . Если такого символа в строке нет, результатом будет -1.

int pos = S.indexOf( ‘в’ ); // pos = 3 pos = «Вася» .indexOf( ‘с’ ); // pos = 2 pos = «Корова» .indexOf( ‘о’ , 2); // pos = 3 pos = «Корова» .indexOf( ‘К’ , 2); // pos = -1, поиск ведется с учетом регистра

Последнее вхождение символа можно найти с помощью метода lastIndexOf(int ch) или lastIndexOf(int ch, int i) , который работает аналогично, но просматривает строку с конца.

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

pos = «Корова» .indexOf( «ор» ); // pos = 1 pos = «Барабанщик барабанил в барабан» .indexOf( «барабан» , 5); // pos = 11 pos = «Корова» .indexOf( «Вася» ); // pos = -1

Изменение регистра символов в строке

Метод toLowerCase() возвращает новую строку, в которой все буквы сделаны строчными. Метод toUpperCase() возвращает новую строку, в которой все буквы сделаны прописными.

S = S.toUpperCase(); // S = «ПРИВЕТ, МИР!»

Упражнение 3

Напишите метод, заменяющий в строке все вхождения слова «бяка» на «вырезано цензурой».

Подсказка: есть очень простой способ.

Заголовок метода main()

Теперь мы можем, наконец, понять большую часть описания метода main() (за исключением ключевых слов public и static .

Заголовок public static void main(String[] args) означает, что метод main() не возвращает значения (и действительно, мы ни разу не использовали в его теле команду return ), а в качестве единственного параметра принимает массив строк args .

В качестве параметра args методу main() передаются так называемые аргументы командной строки. Дело в том, что каждую программу можно запустить не просто щелкнув мышкой по ее значку. Можно ввести имя исполняемого файла программы в командной строке (нажмите комбинацию Windows + R , чтобы увидеть командную строку Windows, если вы работаете в этой операционной системе), а после имени через пробел указать один или несколько дополнительных параметров (аргументов командной строки).

Таким образом, в методе main() можно обратиться к элементам массива args и увидеть, какие дополнительные аргументы пользователь указал при запуске программу. Можно научить программу на некоторые из этих аргументов реагировать.

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

Дополнительная литература

1. Вязовик Н.А. Программирование на Java. (глава 9)

2. Хабибуллин И.Ш. Самоучитель Java 2. (главы 1, 5)

Задание

Создайте новый проект (назовите его MainTask). В вашей рабочей области должно быть два проекта. В одном вы будете пробовать примеры и выполнять упражнения во время изучения новой темы. В проекте MainTask вы будете работать над вашим основным заданием.

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

Приступайте к программированию этого метода параллельно с дальнейшим изучением пособия. Вы должны отчитаться по достигнутому результату на пятом занятии.

Для примера сформулируем требования к методу, с помощью которого решается задача определения суммы выигрыша на тотализаторе (см. задание №3):

Написать метод для подсчета суммы выигрыша на тотализаторе. В качестве параметров метод принимает массив строк (имена лошадей), упорядоченный по результатам забега и перечень ставок, заданный с помощью трех массивов равной длины. Первый массив — имя игрока, второй — кличка лошади, на которую он поставил и третий — сумма, которую игрок поставил на эту лошадь (то есть, игрок gamers[i] поставил на лошадь horses[i] сумму sums[i]). Метод должен возвращать массив строк, где каждая строка содержит имя игрока-победителя и выигранную им сумму.

Java — В JAVA что с массивом, если его ссылка ссылается на дургой?

Последнее изменение: 1 сентября 2007г.

Объекты и ссылки

Речь у нас пойдет о ссылках на объекты. Это то, что вызывает определенные трудности у многих рабработчиков при переходе на Java с С. Я, к счастью, этого избежал, ибо С знал в то время не очень хорошо. Знал, что есть объекты, есть указатели на них, и есть ссылки, но вот в чем разница между двумя последними – увы. Да сейчас это и неважно, наверное, ибо на С я не писал уже как минимум 6 лет.

Цукерберг рекомендует:  IT-тренды из 2020 в 2020

Итак, вернемся к нашим. гм. ссылкам. В Java, как вы знаете, легко можно создать переменную типа объект. Вопрос: а что содержит эта переменная? А содержит она ссылку на объект. В спецификации Java сказано следующее:

An object is a class instance or an array.

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

Почти дословный перевод (почти – потому как дословно это не переводится):

Объект – это экземпляр класса или массив

Значения ссылок (чаще просто ссылки) – это указатели на эти объекты, и специальная ссылка null, которая означает отсутствиет объекта.

Итак – ссылки в Java это, фактически, указатели. Однако, в отличие от С, арифметика указателей в Java отсутствует. И хорошо – ничего хорошего, кроме плохого, я от этой возможности не видел. А что вообще можно делать с ссылками? Какие операции над ними можно производить?

  • Присваивание. Ну это естественно, без этой операции ссылки бы не имели смысла.
  • Доступ к полям объекта
  • Вызов методов
  • Операция приведения типа
  • Конкатенация строк (оператор ‘+’)
  • Проверка принадлежности к определенному типу – оператор instanceof
  • Операции сравнения ссылки – ‘==’ и ‘!=’
  • Условный оператор ? :

Начнем с начала. Присваивание. Тут есть одна тонкость. У каждого объекта есть счетчик ссылок на него. Присваивание новой ссылке указателя на объект увеличивает этот счетчик. Замещение значения ссылки на другое или null – соответственно, уменьшает. Когда на объект не остается ссылок – он становится доступен для сборщика мусора.

Еще один момент. При присваивании контролируется тип объекта по ссылке. Делается это на этапе компиляции.

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

Оператор конкатенации требует, чтобы один из операндов был строковым. В этом случае логика его работы со вторым операндом (ссылкой) такова: он конвертирует объект по ссылке в строку путем вызова его метода toString() (если ссылка равна null или же вызов toString() возвращает null – используется строковый литерал «null»), после чего создает новый объект типа String, который является конкатенацией двух полученных строк.

Отсюда вытекает очень интересный пример. Выполните следующий код:

Что выдает этот код на консоль?

Любопытно, не правда ли? В любом случае, знать это стоит.

Пойдем дальше. Операция instanceof. С ней в принципе тоже все понятно. Хочу упомянуть только два момента. Первый – если ссылка variable имеет значение null , то результат операции ( variable instanceof SomeType ) всегда false , что в общем-то логично – у пустой ссылки невозможно определить тип. И второе – операция instanceof является реализацией принципа наследования is a, т.е. результатом операции экземпляр_дочернего_класса instanceof родительский_класс будет true .

О сравнении (‘==’ и ‘!=’) и условном операторе тоже много не расскажешь. Единственный момент – если в операторе ? value1 : value2 типы value1 и value2 разные, то один тип должен быть приводим у другому. Т.е. если тип value1 – T1 , а value2 – T2 , то либо T1 является одним из родителей T2 , либо наоборот. И тогда тип результата условного оператора – тот из двух, который является родительским. Если же T1 и T2 не состоят в отношениях наследования – дело окончится ошибкой компиляции.

Еще один момент, вытекающий из самой сути ссылок – передача объектов в качестве параметров при вызове функций. А именно – как это делается. А делается это вот как. При вызове функции в ее фрейм копируется ссылка на объект. Рассмотрим следующий фрагмент кода:

Вопрос номер раз. Сколько ссылок на созданный объект будет в точке 1.? Правильный ответ – две! Одна ссылка – это переменная obj метода method1 . Вторая – ссылка во фрейме вызова метода method2 , которую мы знаем по имени – param .

Вопрос номер два. Что происходит при присваивании param.x = 2 ? А происходит вот что. По ссылке param берется объект, переменной которого устанавливается значение. Поскольку param ссылается на тот же самый объект, что и obj в методе method1 – в методе method1 БУДУТ видны изменения состояния объекта obj ВНУТРИ другого метода. Это первые из граблей, на которые часто наступают.

Вопрос номер три. Что происходит после точки 2.? А происходит вот что. Создается новый объект. Ссылка на него присваивается переменной param , т.е. записывается во фрейм вызова. И ТОЛЬКО во фрейм, ссылка obj в method1 остается неизменной! Таким образом, с этого момента obj и param указывают на РАЗНЫЕ объекты. Что подтверждается очень легко: несмотря на присваивание param.x = 3; в точке 0. на консоль будет выведено obj.x=2 . Это еще одни грабли для начинающих: по необъяснимой для меня причине зачастую считают, что если параметр передается по ссылке, то эту ссылку можно изменить. ВНУТРИ метода – можно. Что я и сделал, создав новый экземпляр MyClass и присвоив ссылку на него переменной param . Но ссылку вовне изменить нельзя никак.

Еще одни грабли – модификатор final . Что будет, если в объявлении method2 заменить MyClass param на final MyClass param ? Я встречал мнение, что в этом случае нельзя будет менять состояние объекта внутри метода method2 . Т.е. присваивание param.x=2 закончится неудачей. Это не так. Модификатор final в данном случае будет означать, что нельзя поменять значение переменной param (то, что я делал после точки 2.) Иначе говоря, param ВСЕГДА будет ссылаться на экземпляр класса, созданного до вызова метода method2 . И попытка присвоить этой переменной другое значение приведет к ошибке компиляции. Я не буду сейчас объяснять, зачем нужна такая конструкция, речь не об этом. Важно, что присваивание param.x = 2; по прежнему будет давать результат.

Все вышесказанное верно как для объектов, так и для массивов. Массивы тоже являются объектами, потому любое изменение их содержимого внутри какого-либо метода будет отражаться и вовне него.

Особенно важно об этом помнить вот почему. Допустим, у вас в объекте есть ArrayList , содержащий другие объекты. И для каких-то целей вам нужно возвращать этот ArrayList целиком – например, для того, чтобы пройтись по всем объектам. Так вот, если вы вернете ссылку непосредственно на тот ArrayList , который есть внутри вашего объекта – никто не сможет помешать изменить этот ArrayList как вздумается. Добавить что угодно. Удалить что угодно. И если содержимое этого ArrayList -а входит в состояние объекта – у вас будут серьезные проблемы.

Решением в данной ситуации может служить либо возвращение копии (т.е. создали новый ArrayList , скопировали в него содержимое исходного, вернули новый), либо возвращение неизменяегого List , например, используя Collections.unmodifiableList(List) . Однако, ни то ни другое не гарантирует неизменности состояния самих объектов, содержащихся в возвращаемых списках.

Несколько слов о вопросе, который вызывает много споров. А именно – как передаются параметры-объекты, по значению или по ссылке. С одной стороны – содержимое объекта не копируется, потому вроде как передача происходит по ссылке. Но с другой стороны – сама ссылка (значение области памяти, занимаемой переменной типа ссылка) КОПИРУЕТСЯ во фрейм вызова, как я уже упоминал выше. Т.е. вроде как передается по значению. Это порождает некую путаницу.

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

Мне ближе мышление именно на уровне объектов. Да, я знаю всю эту кухню, но думать о ней каждую секунду мне совсем необязательно. Я передаю ссылку на объект. И с таким представлением проблем у меня пока не было. А все эти споры по поводу действительного способа передачи – по большей части религиозные. Каждый может представлять себе процесс так, как наиболее близко ему. И если это не мешает работе – то и Бог с ним!

Пока, наверное, все. Основные грабли я вроде как описал, а больше и не припомню. Всем спасибо, все свободны. Пишите аккуратно и вдумчиво!

Еще одно добавление. Начиная с Java 5.0 для ссылок определены еще и операции сложения, вычитания, умножения, деления и сравнения. Эти операции имеют смысл для ссылок на объектные оболочки численных примитивных типов. В этом случае производится unboxing, в результате чего в операции участвуют не ссылки на объекты, а реальные значения, взятые из этих объектов. Я, однако, рекомендовал бы очень осторожно использовать эти возможности (в смысле, autoboxing/unboxing), ибо они иногда порождают ощутимую путаницу. Я об этом упоминал вот тут: Сравнение объектов: практика -> Java 5.0. Autoboxing/Unboxing: ‘==’, ‘>=’ и ‘<=' для объектных оболочек..

Java: работаем с массивами

В этой статье вы познакомитесь с Java массивами:

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

Но есть более элегантный способ работать со значениями – их можно просто сгруппировать при помощи массива.

Массив – это контейнер, в котором содержится определённое количество значений конкретного типа данных. В приведённом выше примере числа из лотереи можно сгруппировать в массив int :

Расценивайте массив как ряд коробок. Количество коробок в массиве изменить нельзя. В каждой коробке находится значение того же типа данных, что и в остальных коробках. Можно увидеть значение внутри или заменить содержимое коробки другим значением. В контексте массивов такие « коробки » называются элементами.

Объявление и инициализация массива

Оператор объявления массива в Java похож на любую другую переменную. Он содержит тип данных, после которого следует название массива. Единственное отличие заключается в использовании квадратных скобок рядом с типом данных:

Приведённые выше операторы объявления сообщают компилятору, что intArrayvariable – это массив целых чисел, floatArrayis – массив чисел с плавающей запятой, а charArrayis – массив символов.

Как и другие переменные, их нельзя использовать до инициализации и установки значения. В случае с массивом указание значения должно определять размер массива:

Число внутри скобок указывает, сколько элементов содержится в массиве. Приведённый выше код создает массив целых чисел, состоящий из 10 элементов.

Объявление и установку размера массива можно сделать в одном выражении:

Массивы не ограничиваются примитивными типами данных. Также можно создавать Java массив строк или объектов:

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

Как только массив инициализирован, можно задавать элементам значения при помощи индекса. Он определяет позицию каждого элемента внутри массива. Первый элемент занимает позицию 0 , второй – 1 и так далее.

Важно отметить, что индекс первого элемента – 0 . Обычно путаница возникает тогда, когда начинаешь думать, что раз в массиве 10 элементов, то их индексы будут перечисляться с 1 до 10 . Но на самом деле счет идет от 0 до 9 . Например, возвращаясь к примеру с лотерей, можно создать массив из 6 элементов, и задать им числа из лотереи:

Сокращённый способ заполнения массива:

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

Чтобы получить значение элемента, используется его индекс:

Чтобы узнать в Java длину массива, используется поле length :

Примечание : распространена ошибка при использовании метода length , когда значение length используется как индексная позиция. Это всегда выдаст ошибку, так как индексные позиции массива 0 , в то время как позиция length – 1 .

Многомерные массивы

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

Индекс многомерных массивов содержит два числа:

При этом длина массивов, находящихся внутри многомерного массива Java , не обаятельно должна быть одинаковой:

Копируем массив

Чтобы скопировать массив, проще всего использовать arraycopymethod из класса System . Метод принимает пять параметров: исходный массив, позиция в индексе ( с которой необходимо начать копирование элементов ), новый массив, позиция в индексе для начала вставки и количество копируемых элементов:

В примере мы создаём новый массив, в котором содержатся последние четыре элемента из массива int :

Так как массивы имеют фиксированную длину, arraycopymethod позволяет удобно изменять длину массива.

Чтобы лучше познакомиться с массивами, можете прочитать о том, как управлять массивами при помощи класса Arrays . А также о создании в Java динамических массивов ( то есть, массивов с нефиксированным количеством элементов ) при помощи класса ArrayList .

Данная публикация представляет собой перевод статьи « Working With Arrays in Java » , подготовленной дружной командой проекта Интернет-технологии.ру

Ссылка (Java)

Ссылки в Java — указатели на объекты. Другими словами, ссылка — это переменная, содержащая адрес ячейки памяти, в которой хранится объект. Кроме того, ссылка может быть инициализирована как null — нулевая ссылка, не указывающая ни на какой объект в памяти (именно это значение является значением по умолчанию). Внутри класса в нестатическом контексте также может быть использована ссылка this, указывающая на текущий объект, и ссылка super, указывающая на текущий объект суперкласса.

В Java различают переменные примитивного и ссылочного типа. К примитивным относят 8 типов — пять целочисленных, два для чисел с плавающей точкой и логический тип boolean. Все остальные типы данных относятся к ссылочным типам. Это значит, что объявленная переменная этого типа хранит не состояние объекта, а ссылку на него, сам же объект хранится в определённой области памяти (куче). Создание нового класса, абстрактного класса, интерфейса или перечисления равносильно определению нового ссылочного типа данных. В отличие от создания объектов, объявление ссылок типа абстрактный класс либо интерфейс не запрещено.

В спецификации Java не содержит конкретной информации о данных, хранящихся в ссылке. Во многих JVM ссылка реализована исключительно как указатель и занимает 32 бита в 32-битных JVM и 64 бита — в 64-битных. Тем не менее, существуют реализации JVM (например, Zing Virtual Machine от Azul), в целях оптимизации помещающие в ссылку также информацию о типе данных. Другие (например, Java HotSpot Virtual Machine), наоборот, используют технологию «Compressed Oops», позволяющую использовать ссылки размером в 32 бита на 64-битных операционных системах. [1]

Содержание

[править] Действия над ссылками

Среди действий над ссылками можно выделить следующие [2] :

Присваивание (оператор ‘=’) — позволяет определить новое значение ссылки. Важно, что при присваивании на этапе компиляции контролируется тип объекта по ссылке. таким образом, в отличие от слабо типизированных и нетипизированных языков, Java не позволит инстанциировать ссылку объектом, тип которого не является подтипом ссылки. Чтобы сделать это, необходимо воспользоваться явным приведением типа.

Непосредственно при присвоении новый объект не создаётся. Недопустимо присваивать новые значения ссылкам this и super.

Обращение к полям и методам объекта осуществляется с помощью точки. Следует помнить, что в соответствии с принципом полиморфизма вызываемый нестатический метод определяется не типом ссылки на этапе компиляции, а типом объекта на этапе выполнения. Если ссылка имеет значение null, попытка обращения к члену класса вызовет исключительную ситуацию NullPointerException.

Операция приведения типа позволяет определить новую ссылку другого типа, указывающую на тот же самый объект. Может пригодится, например, в следующей ситуации: тип B является подтипом типа А и расширяет его интерфейс (то есть, например, класс Beaver унаследован от класса Аnimal и определяет дополнительные методы). На ссылке типа A вызывать методы, отсутствующие в типе А, запрещено компилятором, поскольку он не может гарантировать, что эта ссылка действительно в момент выполнения будет указывать на объект типа B. Поэтому чтобы вызвать характерные для типа B методы, необходимо прибегнуть к операции приведения типа:

Если ссылка указывает на объект, который не может быть приведён к указанному типу, будет выброшено исключение ClassCastException.

Проверка принадлежности к определенному типу (оператор instanceof) позволяет определить, принадлежит ли объект, на который указывает ссылка, типу либо подтипу типа, переданного в качестве правого операнда. Может использоваться для последующей безопасной операции приведения типа, поскольку пройденная подобная проверка гарантирует отсутствие ClassCastException.

Если ссылка нулевая (null), оператор instanceof вернёт false.

Операции сравнения ссылок (‘==’ — «равно» и противоположный по смыслу оператор «не равно» ‘!=’) позволяют определить, указывают ли две ссылки на один и тот же объект в памяти. Важно понимать, что сравнение объектов по значению в этом случае не производится; при необходимости следует прибегнуть к методу equals().

Помимо этого для ссылок некоторых типов определены дополнительные операторы. Основные среди них:

  • Операция конкатенации (+) для объектов типа java.lang.String.
  • Арифметические операции для классов-оболочек примитивных типов (таких как java.lang.Integer, java.lang.Double и прочих).

В отличие от некоторых других языков программирования, например языка C, ссылки в Java не требуют разыменования, работа с ними автоматически имитирует работу с объектами, на который они указывают. Также в Java отсутствует аналог арифметики указателей. В целом, сравнивая с языком C, можно сказать, что ссылки в Java предоставляют меньше возможностей, но более безопасны. [3]

[править] Ссылки и модель памяти

Основные аспекты, касающиеся ссылок и модели памяти в Java:

  • Java-объекты хранятся в куче, общей для всех потоков; локальные переменные ссылочных типов, как и переменные примитивных типов, хранятся в стеке потока.
  • При вызове методов переданные в качестве параметров переменные ссылочных и примитивных типов копируются. Это значит, что происходит передача примитивных типов по значению и ссылочных типов по ссылке (передача ссылки по значению). Изменения состояния объекта внутри метода будут видны вне метода, но изменение самой ссылки (выполнение операции присваивания) не повлияет на ту ссылку, которая выступила параметром метода. [4]
  • Объекты, на которые существуют ссылки, не могут быть удалены сборщиком мусора. Объект, на который отсутствуют ссылки, доступен для удаления. При этом, в зависимости от алгоритмов сборщика, удалению могут быть доступны также те объекты, которые хранят ссылки друг на друга (так называемые циклические ссылки), но на которые нет ссылок извне. [5]

[править] Модификаторы

Различные модификаторы, применяемые к полям класса, аргументам функций или локальным переменным, как правило, влияют на ссылку, а не на объект, на который она указывает. Например:

  • Ссылка, помеченная модификатором final, не может быть изменена после присваивания. При этом объект, на который она указывает, может быть изменён.
  • Актуальное значение ссылки, помеченной модификатором volatile, в многопоточной среде всегда будет доступно всем потокам (то есть оно не будет кэшироваться из соображений оптимизации, что гарантированно позволит избежать ситуации, когда один поток изменил ссылку, после чего второй прочитал старое значение).

[править] Пакет java.lang.ref

Помимо обыкновенных ссылок, известных так же как Strong References (сильные ссылки), в Java представлен пакет java.lang.ref, содержащий реализации Soft (мягких), Weak (слабых) и Phantom (фантомных) ссылок, которые по-разному обрабатываются сборщиком мусора и предоставляют пользователю возможность более гибко работать с памятью.

[править] ReferenceQueue

Объект ReferenceQueue представляет собой очередь, хранящую ссылки на объекты, подготовленные сборщиком мусора для удаления, и предоставляющую методы по получению и удалению объектов. С помощью очереди имеется возможность отлавливать удаление объектов и выполнять некоторую логику (например, удаление ссылавшихся на собранные сборщиком мусора ссылки). [6]

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

Цукерберг рекомендует:  Обучение - Сайт и проблема

[править] Ссылка

Абстрактный класс java.lang.ref.Reference [7] предоставляет базу для ссылочных классов. В нём определены следующие методы:

  • get — метод, возвращающий сильную ссылку на объект, на который указывает ссылка.
  • clear — очищает ссылку.
  • isEnqueued и enqueue, отвечающие за взаимодействие ссылки и объекта ReferenceQueue.

[править] Мягкая ссылка

Мягкая ссылка — объект класса java.lang.ref.SoftReference. Особенность такой ссылки в том, что если она указывает на объект, на который не указывают сильные ссылки, этот объект может быть удалён сборщиком мусора в случае угрозы OutOfMemoryError. Конкретные правила удаления подобных объектов варьируются:

  • В JVM HotSpot Virtual Machine объект удаляется, если время, прошедшее с последнего обращения к нему, превосходит значение выражения F * MSPerMB, где F — количество свободных мегабайт в куче, а MSPerMB — коэффициент, который пользователь может задать с помощью ключа -XX:SoftRefLRUPolicyMSPerMB (по умолчанию его значение — 1000 мс/МБ).
  • Виртуальная машина IBM ориентируется не на время последнего обращения к объекту, а на количество запусков сборщика мусора, которые пережил объект. [8]

Несколько примеров использования мягких ссылок:

  • Кэширование. В частности, этот подход использован в классе java.lang. >[9]

Объявление мягкой ссылки:

[править] Слабая ссылка

Слабая ссылка — объект класса java.lang.ref.WeakReference. Особенность такой ссылки в том, что если она указывает на объект, на который не указывают сильные и мягкие ссылки, этот объект может быть удалён сборщиком мусора.

Несколько примеров использования слабых ссылок:

  • Ассоциация объектов. Например, имеется множество объектов Action, каждый из которых создаётся, запускаем на выполнение некоторую логику и после того, как она отработает, более не нужен. Пусть для каждого такого объекта создаётся уникальная строка-идентификатор. Сопоставить идентификатор и Action можно с помощью ассоциативного массива (Map). В этом случае в массиве будет храниться сильная ссылка на каждый Action, а значит, они не будут удаляться из памяти. Чтобы избежать этого, можно хранить в Map слабые ссылки на Action. Как только Action завершит работу и на него пропадут сильные ссылки, он будет удалён; до тех же пор к нему можно будет обращаться по сопоставленному идентификатору.
  • WeakHashMap — представитель Java Collections Framework, в основе которого лежат слабые ссылки. В отличие от описанного выше подхода, хранит слабые ссылки на ключи. Когда на ключ не остаётся сильных ссылок, он удаляется сборщиком мусора, затем с помощью ReferenceQueue определяется, какой ключ был удалён. Соответствующее ему значение также удаляется.
  • С помощью слабых ссылок можно обеспечить отсутствие дублирующихся объектов: в классе создаётся пул слабых ссылок на объекты и реализуется метод intern() (по аналогии с одноименным методом в классе java.lang.String). Вызванный на объекте, этот метод либо вернёт ссылку на объект из пула, если в нём хранится объект, равный данному, либо поместит этот объект в пул.

[править] Фантомная ссылка

Фантомная ссылка — объект класса java.lang.ref.PhantomReference. Объект, на который существуют только фантомные ссылки, доступен сборщику мусора. В отличие от описанных выше типов ссылок, фантомная ссылка не даёт пользователю возможности «спасти» объект, создав на него сильную ссылку: метод get на ней всегда возвращает null.

Зато с помощью фантомной ссылки можно определить, что объект собирается быть удалён. В силу этих особенностей фантомная ссылка бесполезна без объекта ReferenceQueue, поэтому для неё определён единственный конструктор:

Основное предназначение фантомных ссылок — замена методу finalize, имеющему большое количество недостатков. [10] С их помощью можно реализовать логику подготовки объекта к удалению, например освобождение захваченных им ресурсов, очистка сессии, сохранение состояния или логгирование. Фантомная ссылка, указывающая на удаляемый объект, попадает в ReferenceQueue, откуда она может быть извлечена. Поскольку базовая фантомная ссылка не предоставляет пользователю возможности определить, на какой именно объект она ссылается, стоит реализовывать собственного потомка класса PhantomReference, хранящего идентификатор объекта (не ссылку на сам объект, ибо это будет сильная ссылка — объект перестанет быть доступен сборщику мусора). В таком случае после обращения к идентификатору можно однозначно определить, какой объект был удалён, и выполнить все необходимые операции. [11]

Виды ссылок на объекты в Java

категория
Java
дата 05.02.2010
автор hkvd
голосов 35

Рассмотрим пример когда эти классы могут понадобиться.

Есть такая задача: необходимо реализовать загрузку объектов с серверной стороны на клиентскую сторону по требованию клиента. Для этого можно реализовать коллекцию объектов на клиенте, в которой будут сохраняться уже загруженные объекты, для того чтобы при повторном запросе этого же объекта не приходилось загружать его снова, а отдавать их из этой коллекции, т.е. реализация кеша на клиенте. С течением времени клиент загрузит много новых объектов, сохранит их в коллекцию, а ранее загруженные объекты становятся уже не актуальными и просто занимают место в памяти. Итого — необходим механизм очистки кеша.

Здесь можно пойти разными путями, рассмотрим некоторые:

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

2. Использовать механизм «слабых ссылок» в Java.

Что это за «слабые ссылки»?

В Java ссылки можно разделить по «силе». Далее представлены все 4 типа ссылок отсортированных по «силе» (от большей к меньшей):

1. сильные ссылки (strong reference). Это обычные ссылки которые мы всегда используем. Когда мы объявляем Rectangle rect = new Rectangle(), то объект на который ссылается rect не может быть удален сборщиком мусора из памяти до тех пор, пока на этот объект есть хотя бы одна сильная ссылка;

2. мягкие ссылки (SoftReference). Объявление мягкой ссылки выглядит так:

SoftReference rect = new SoftReference (new Rectangle());

Для получения самого объекта Rectangle можно воспользоваться методом rect.get(). Метод get() объявлен у класса Reference, от которого наследуются SoftReference, WeakReference, PhantomReference. Важно понимать, что метод get() может вернуть null. Это происходит в том случае, когда память вашей программы заполнена и появляется вероятность возникновения OutOfMemoryError, тогда сборщик мусора удаляет объекты на которые ссылаются мягкие ссылки. Именно этот вид ссылок удобно использовать для кеширования объектов.

3. слабые ссылки (WeakReference). Использование слабой ссылки схоже с использованием мягкой ссылки, объявление:

WeakReference rect = new WeakReference (new Rectangle());

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


4. фантомные ссылки (PhantomReference). Создание фантомной ссылки аналогично другим:

new PhantomReference (new Rectangle(), queue);

Разница в том, что здесь обязательно объявление объекта очереди queue — это представитель класса ReferenceQueue. Очереди используются также с SoftReference и WeakReference. Их функция в том, что когда объект доступный с помощью ссылки удаляется, то сам объект ссылки становится доступным в очереди ReferenceQueue. Этот механизм позволяет удалять пустые ссылки.

Метод get() вызванный у фантомной ссылки всегда возвращает null. Это связано с тем для чего они используются.

Дело в том, что слабая ссылка помещается в очередь перед тем, как объект на который она указывает будет финализирован (finalize()) и удален сборщиком мусора. Т.е. в методе finalize() удаляемого объекта можно назначить строгую ссылку на удаляемый объект, и таким образом «спасти» его от сборщика мусора. Но слабая ссылка уже будет в очереди и будет уничтожена.

Отличие фантомной ссылки в том, что она помещается в очередь только «по факту» удаления объекта на который она указывает — это и есть её основная функция.

Рассмотрим слабые ссылки на практическом примере.

Объекты на которые будут указывать наши ссылки представлены классом BigObject.

Для тестирования используются следующие объекты:

Для вывода результатов тестов будем использовать следующие методы:

Методы инициализации и загрузки памяти выглядят так:

Теперь проведем тестирование. Для первого теста используем следующий код:

В результате получаем:

Пояснение: Как ранее говорилось, фантомные ссылки всегда возвращают null. После вызова сборщика мусора были удалены объекты на которые ссылались фантомные ссылки (PhantomReference) и слабые ссылки (WeakReference).
Во втором тесте рассмотрим момент удаления объектов на которые ссылаются мягкие ссылки (SoftReference). Тест представлен кодом:

В результате получаем:

Пояснение: На первом этапе после вызова сборщика мусора были удалены объекты на которые указывали слабые ссылки (WeakReference). Далее происходит загрузка памяти и повторный вызов сборщика мусора. После этого удаляются объекты на которые указывали мягкие ссылки (SoftReference). В результате после этих действий остались объекты только с прямыми (сильными) ссылками на них.

Использование различных видов ссылок в Java позволяет уменьшить вероятность возникновения утечек памяти (memory leaks), а также повысить эффективность использования памяти. Для реализации кеша можно использовать класс WeakHashMap.

Берегите память!
Иван Каплин, февраль 2010.

Java Array. Массивы в Java. Java для начинающих

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

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

Создание и манипуляции одномерными массивами

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

Вначале объявляется тип Java array, который определяет тип значений, хранящихся в нем. Это может быть любой допустимый в Java тип данных. Далее идут имя массива и квадратные скобки, сообщающие компилятору, что данная переменная является массивом. Обратите внимание на важный факт. Квадратные скобки можно ставить как после базового типа массива, так и после имени массива. После знака равенства указывается оператор new, инициирующий выделение памяти под массив (так же, как и в случае с объектами), тип элементов, которые будут храниться в нем (должен быть совместим с базовым типом, объявленным ранее), и, наконец, их количество, указанное в квадратных скобках.

Нумерация элементов в Java array начинается с 0. Так, индекс первого элемента в данном массиве будет равен 0, а шестого – 5. Чтобы обратиться к конкретному элементу массива, например, пятому, достаточно указать имя массива и индекс элемента в квадратных скобках рядом с именем. Таким образом можно как присваивать значение элементу, так и извлекать его. Однако следует быть внимательным, поскольку если передать индекс, по которому не существует элемента, то возникнет ошибка.

Многомерные массивы в Java

Многомерные массивы представляют собой ряды одномерных, на которые ссылаются элементы других массивов. Иными словами, это массивы массивов. Наиболее простыми среди них являются двумерные. На их примере мы и попытаемся разобраться с понятием. Для наглядности на рисунке ниже приведены синтаксис и схема, описывающая структуру двумерного массива.

Как видим, синтаксис не особо отличается от одномерных массивов. Давайте разберем структуру. В первых скобках мы выделили место под 5 элементов. Эти элементы являются ничем иным как ссылками на отдельные массивы. При этом размер каждого из них определен числом во вторых скобках. По сути, аналогом двумерных массивов в математике являются матрицы. Обратите внимание, что помимо элементов, в памяти выделяется отдельное место, где хранится значение длины массива (length). Как правило, работа с многомерными массивами осуществляется посредством вложенных циклов for.

Нерегулярные массивы

Двумерный массив является массивом массивов. Это мы уже выяснили. Но могут ли массивы, содержащиеся в нем, иметь разную длину? Ответ – да, могут. Для этого в Java предусмотрена возможность объявлять двумерный массив специальным образом. К примеру, мы хотим создать двумерный массив, который хранил бы в себе три одномерных массива длиной 2, 3 и 4 соответственно. Объявляется он следующим образом:

Обратите внимание, что мы не указали число во вторых скобках. Определение размера массивов в arr делается так:

Обращаясь к элементу под индексом 0, указывающему на первый массив, мы объявляем его с размерностью 2. Под элементом с индексом 1 будет храниться массив размерностью 3, и так далее. Все довольно просто.

Альтернативный синтаксис объявления java array

Инициализировать массивы можно и непосредственно при их создании. Это довольно просто.

Обратите внимание на объявление массивов jerseyNumber и playerName.

В случае с двумерными массивами данное объявление выглядит так:

Для этого вместо оператора new открываются фигурные скобки, в которых через запятую идет перечисление всех элементов. Java в этом случае автоматически выделяет память под них и индексирует их соответствующим образом.

Вспомогательный класс Arrays

Для работы с такими сущностями, как массивы в Java, в пакете java.util имеется специальный класс Arrays, который предоставляет множество статических методов, значительно облегчающих операции с ними. Перечень основных методов представлен на рисунке ниже.

Разберем некоторые самые полезные Java array методы:

— copyOf (массив, длина) – возвращает копию переданного массива соответствующей длины. Если переданная длина больше оригинального массива, то все «лишние» элементы заполняются значением по умолчанию (0, если простой тип, и null , если ссылочный).

— copyOfRange (массив, первый индекс, последний индекс) – не указанный на рисунке, но полезный метод. Он копирует часть переданного массива, определенную соответствующими индексами, начиная с первого и заканчивая последним.

— sort (массив) – сортирует элементы массива по возрастанию.

— fill (массив, значение) – заполняет переданный массив соответствующим значением.

— binarySearch (массив, значение) – возвращает индекс, под которым элемент с соответствующим значением находится в переданном отсортированном массиве. Если же такой элемент отсутствует, то возвращается отрицательное число.

Поскольку методы статические, то для их вызова не требуется создавать экземпляр класса Arrays. Они вызываются напрямую из него: Arrays.sort(arr).

Заключение

Мы рассмотрели наиболее важные аспекты относительно массивов, и для тех, кто только приступает к изучению Java для начинающих, этого хватит для базового понимания такой сущности, как массив, и основных приемов работы с ним. Конечно, практика даст больше понимания работы данного инструмента. Поэтом не поленитесь сделать несколько упражнений, манипулируя массивами различными способами.

Вспомогательный класс Array Java используется уже в «боевых» условиях, поэтому для начала рекомендуется учиться производить все основные операции с массивами вручную.

Массив объектов в Java

  • написана командой Vertex Academy. Надеемся, что она Вам будет полезна. Приятного прочтения!
  • это одна из статей из нашего «Самоучителя по Java»
Что такое массив

Ранее на нашем сайте уже была размещена статья, посвященная массивам в Java. В ней мы рассказывали, что массив — это такой » контейнер «, который может вмещать в себя несколько однородных переменных.

У массивов есть несколько особенностей:

1. Они могут хранить переменные только одного типа ;

2. Размер массива нельзя поменять;

3. Массив имеет длину;

4. Элементы массивов имеют индексы, по которым к ним и обращаются в случае необходимости;

5. Номер первого элемента массива — ;

6. Можно создавать пустые массивы, и «положить» в них элементы потом .

А вот что такое «массивы объектов» мы ранее не рассматривали. Давайте разберемся что к чему.

Массив объектов

По сути, у массивов объектов очень много общего с обычными массивами, только у них другая «начинка». Тем не менее, как Вы знаете, объекты гораздо «сложнее», чем переменные, потому что у них есть больше составляющих.

Массив объектов объявляется так же, как и массив переменных:

Как вы видите, у нас не массив разных объектов (стульев, столов и жевательных резинок). Это массив объектов одного класса (массив стульев, например).

Тут тоже все начинается и заканчивается названием класса , а в конце в квадратных скобках указывается размер массива:

Распространенная ошибка

Массив объектов отличается одной особенностью — они состоят из ссылок на объекты:

То есть в массиве не содержатся все переменные и методы каждого объекта, как Вы могли бы подумать, а только ссылки на эти самые объекты. Но тут возникает одна опасность. Обычно переменные в массивах имеют значения по умолчанию — если не задать, например, значение переменной int, то она по умолчанию будет равна 0 (больше информации про значения по умолчанию можно найти здесь).

В случает с объектами, происходит по-другому. В массиве должна храниться ссылка на объект, но пока объекта не существует, — ссылки на него тоже быть не может .

Поэтому, в массиве хранится значение по умолчанию null. Если Вы попробуете вызвать метод не созданного объекта, возникнет распространенная ошибка NullPointerException . Обязательно создайте свои объекты перед тем, как обращаться к ним! ��

Пример

Давайте создадим массив объектов. Допустим, у нас есть класс — ObjMass, и все, что в нем хранится — это массив объектов Cat из 3 элементов:

9. Лекция: Массивы

9. Лекция: Массивы

Лекция посвящена описанию массивов в Java. Массивы издавна присутствуют в языках программирования, поскольку при выполнении многих задач приходится оперировать целым рядом однотипных значений. Массивы в Java – один из ссылочных типов, который, однако, имеет особенности при инициализации, создании и оперировании со своими значениями. Наибольшие различия проявляются при преобразовании таких типов. Также объясняется, почему многомерные массивы в Java можно (и зачастую более правильно) рассматривать как одномерные. Завершается классификация типов переменных и типов значений, которые они могут хранить. В заключение рассматривается механизм клонирования Java, позволяющий в любом классе описать возможность создания точных копий объектов, порожденных от него.

Массивы как тип данных в Java

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

Элементы не имеют имен, доступ к ним осуществляется по номеру индекса. Если массив имеет длину n, отличную от нуля, то корректными значениями индекса являются числа от 0 до n-1. Все значения имеют одинаковый тип и говорится, что массив основан на этом базовом типе. Массивы могут быть основаны как на примитивных типах (например, для хранения числовых значений 100 измерений), так и на ссылочных (например, если нужно хранить описание 100 автомобилей в гараже в виде экземпляров класса Car ).

Сразу оговоримся, что в Java массив символов char[] и класс String являются различными типами. Их значения могут легко конвертироваться друг в друга с помощью специальных методов, но все же они не относятся к идентичным типам.

Как уже говорилось, массивы в Java являются объектами (примитивных типов в Java всего восемь и их количество не меняется), их тип напрямую наследуется от класса Object, поэтому все элементы данного класса доступны у объектов-массивов.

Базовый тип также может быть массивом. Таким образом конструируется массив массивов, или многомерный массив.

Работа с любым массивом включает обычные операции, уже описанные для других типов, — объявление, инициализация и т.д. Начнем последовательно изучать их в приложении к массивам.

Объявление массивов

В качестве примера рассмотрим объявление переменной типа «массив, основанный на примитивном типе int «:

Как мы видим, сначала указывается базовый тип. Затем идет имя переменной, а пара квадратных скобок указывает на то, что используемый тип является именно массивом. Также допустима запись:

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

Переменная a имеет тип «двумерный массив, основанный на int «. Аналогично объявляются массивы с базовым объектным типом:

Создание переменной типа массив еще не создает экземпляры этого массива. Такие переменные имеют объектный тип и хранят ссылки на объекты, однако изначально имеют значение null (если они являются полями класса; напомним, что локальные переменные необходимо явно инициализировать). Чтобы создать экземпляр массива, нужно воспользоваться ключевым словом new, после чего указывается тип массива и в квадратных скобках – длина массива.

Цукерберг рекомендует:  Верстка - CSS Что я делаю не так

Point[] p = new Point[10];

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

int array[]=new int[5];

for (int i=0; i 31 -1, то есть немногим больше 2 млрд.

Продолжая рассматривать тип массива, подчеркнем, что в качестве базового типа может использоваться любой тип Java, в том числе:

* интерфейсы. В таком случае элементы массива могут иметь значение null или ссылаться на объекты любого класса, реализующего этот интерфейс;

* абстрактные классы. В этом случае элементы массива могут иметь значение null или ссылаться на объекты любого неабстрактного класса-наследника.

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

Object o = new int[4];

Это дает интересную возможность для массивов, основанных на типе Object, хранить в качестве элемента ссылку на самого себя:

Object arr[] = new Object[3];

Инициализация массивов

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

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

Рассмотрим создание массива на основе ссылочного типа. Предположим, это будет класс Point. При создании экземпляра массива с применением ключевого слова new не создается ни один объект класса Point, создается лишь один объект массива. Каждый элемент массива будет иметь пустое значение null. В этом можно убедиться на простом примере:

Point p[]=new Point[5];

Результатом будут лишь слова null.

Далее нужно инициализировать элементы массива по отдельности, например, в цикле. Вообще, создание массива длиной n можно рассматривать как заведение n переменных и работать с элементами массива (в последнем примере p[i] ) по правилам обычных переменных.

Кроме того, существует и другой способ создания массивов – инициализаторы. В этом случае ключевое слово new не используется, а ставятся фигурные скобки, и в них через запятую перечисляются значения всех элементов массива. Например, для числового массива явная инициализация записывается следующим образом:

// эквивалентно new int[0]

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

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

Point p=new Point(1,3);

p, new Point(2,2), null, p

// или String sarr[]= <

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

public class Parent <

private String[] values;

protected Parent(String[] s) <

public class Child extends Parent <

public Child(String firstName,

// требуется анонимное создание массива

В конструкторе класса Child необходимо осуществить обращение к конструктору родителя и передать в качестве параметра ссылку на массив. Теоретически можно передать null, но это приведет в большинстве случаев к некорректной работе классов. Можно вставить выражение new String[2], но тогда вместо значений firstName и lastName будут переданы пустые строки. Попытка записать <

приведет к ошибке компиляции, так можно только инициализировать переменные.

Корректное выражение выглядит так:

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

Многомерные массивы

Теперь перейдем к рассмотрению многомерных массивов. Так, в следующем примере

переменная i ссылается на двумерный массив, который можно представить себе в виде таблицы 3х5. Суммарно в таком массиве содержится 15 элементов, к которым можно обращаться через комбинацию индексов от (0, 0) до (2, 4). Пример заполнения двумерного массива через цикл:

int pithagor_table[][]=new int[5][5];

Метод выглядит абсолютно корректным, все потенциально ошибочные ситуации проверяются if -выражением. Однако следующий вызов этого метода все равно приводит к ошибке:

И это будет как раз ошибка ArrayStoreException.

Переменные типа массив и их значения

Завершим описание взаимосвязи типа переменной и типа значений, которые она может хранить.

Как обычно, массивы, основанные на простых и ссылочных типах, мы описываем раздельно.

Переменная типа массив примитивных величин может хранить значения только точно такого же типа, либо null.

Переменная типа «массив ссылочных величин» может хранить следующие значения:

значения точно такого же типа, что и тип переменной;

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

Все эти утверждения непосредственно следуют из рассмотренных выше особенностей приведения типов массивов.

Еще раз напомним про исключительный класс Object. Переменные такого типа могут ссылаться на любые объекты, порожденные как от классов, так и от массивов.

Сведем все эти утверждения в таблицу.

Таблица Табл. 9.1.. Тип переменной и тип ее значения.

Допустимые типы ее значения

Массив простых чисел

* в точности совпадающий с типом переменной

Массив ссылочных значений

* совпадающий с типом переменной

* массивы ссылочных значений, удовлетворяющих следующему условию: если тип переменной – массив на основе типа A, то значение типа массив на основе типа B допустимо тогда и только тогда, когда B приводимо к A

* любой ссылочный, включая массивы

Клонирование

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

должно быть истинным, как и выражение

также верно. Реализация такого метода clone() осложняется целым рядом потенциальных проблем, например:

* класс, от которого порожден объект, может иметь разнообразные конструкторы, которые к тому же могут быть недоступны (например, модификатор доступа private );

* цепочка наследования, которой принадлежит исходный класс, может быть довольно длинной, и каждый родительский класс может иметь свои поля – недоступные, но важные для воссоздания состояния исходного объекта;

* в зависимости от логики реализации возможна ситуация, когда не все поля должны копироваться для корректного клонирования; одни могут оказаться лишними, другие потребуют дополнительных вычислений или преобразований;

* возможна ситуация, когда объект нельзя клонировать, дабы не нарушить целостность системы.

Поэтому было реализовано следующее решение.

Класс Object содержит метод clone(). Рассмотрим его объявление:

protected native Object clone()

Именно он используется для клонирования. Далее возможны два варианта.

Первый вариант: разработчик может в своем классе переопределить этот метод и реализовать его по своему усмотрению, решая перечисленные проблемы так, как того требует логика разрабатываемой системы. Упомянутые условия, которые должны быть истинными для клонированного объекта, не являются обязательными и программист может им не следовать, если это требуется для его класса.

Второй вариант предполагает использование реализации метода clone() в самом классе Object. То, что он объявлен как native, говорит о том, что его реализация предоставляется виртуальной машиной. Естественно, перечисленные трудности легко могут быть преодолены самой JVM, ведь она хранит в памяти все свойства объектов.

При выполнении метода clone() сначала проверяется, можно ли клонировать исходный объект. Если разработчик хочет сделать объекты своего класса доступными для клонирования через Object.clone(), то он должен реализовать в своем классе интерфейс Cloneable. В этом интерфейсе нет ни одного элемента, он служит лишь признаком для виртуальной машины, что объекты могут быть клонированы. Если проверка не выполняется успешно, метод порождает ошибку CloneNotSupportedException.

Если интерфейс Cloneable реализован, то порождается новый объект от того же класса, от которого был создан исходный объект. При этом копирование выполняется на уровне виртуальной машины, никакие конструкторы не вызываются. Затем значения всех полей, объявленных, унаследованных либо объявленных в родительских классах, копируются. Полученный объект возвращается в качестве клона.

Обратите внимание, что сам класс Object не реализует интерфейс Cloneable, а потому попытка вызова new Object().clone() будет приводить к ошибке. Метод clone() предназначен скорее для использования в наследниках, которые могут обращаться к нему с помощью выражения super.clone(). При этом могут быть сделаны следующие изменения:

* модификатор доступа расширен до public ;

* удалено предупреждение об ошибке CloneNotSupportedException ;

* результирующий объект может быть модифицирован любым способом, на усмотрение разработчика.

Напомним, что все массивы реализуют интерфейс Cloneable и, таким образом, доступны для клонирования.

Важно помнить, что все поля клонированного объекта приравниваются, их значения никогда не клонируются. Рассмотрим пример:

public class Test implements Cloneable <

public Test(int x, int y, int z) <

public static void main(String s[]) <

Test t1=new Test(1, 2, 3), t2=null;

catch (CloneNotSupportedException e) <

System.out.println(«t2.p.x=» + t2.p.x + «, t2.height=» + t2.height);

Результатом работы программы будет:

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

А вот ссылочное поле было скопировано по ссылке, оба объекта ссылаются на один и тот же экземпляр класса Point. Поэтому изменения, происходящие с исходным объектом, сказываются на клонированном.

Этого можно избежать, если переопределить метод clone() в классе Test.

public Object clone() <

Test clone=null; try <

catch (CloneNotSupportedException e) <

throw new InternalError(e.getMessage());

Обратите внимание, что результат метода Object.clone() приходится явно приводить к типу Test, хотя его реализация гарантирует, что клонированный объект будет порожден именно от этого класса. Однако тип возвращаемого значения в данном методе для универсальности объявлен как Object, поэтому явное сужение необходимо.

Теперь метод main можно упростить:

public static void main(String s[]) <

Test t1=new Test(1, 2, 3);

Test t2=(Test) t1.clone();

То есть теперь все поля исходного и клонированного объектов стали независимыми.

Реализация такого «неглубокого» клонирования в методе Object.clone() необходима, так как в противном случае клонирование второстепенного объекта могло бы привести к огромным затратам ресурсов, ведь этот объект может содержать ссылки на более значимые объекты, а те при клонировании также начали бы копировать свои поля, и так далее. Кроме того, типом поля клонируемого объекта может быть класс, не реализующий Cloneable, что приводило бы к дополнительным проблемам. Как показано в примере, при необходимости дополнительное копирование можно добавить самостоятельно.

Клонирование массивов

Итак, любой массив может быть клонирован. В этом разделе хотелось бы рассмотреть особенности, возникающие из-за того, что Object.clone() копирует только один объект.

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

Разберем, что будет происходить в этих двух случаях. Начнем с того, что в первой строке создается двухмерный массив, состоящий из двух одномерных. Итого три объекта. Затем, на следующей строке при клонировании будет создан новый двухмерный массив, содержащий ссылки на те же самые одномерные массивы.

Теперь несложно предсказать результат обоих вариантов. В первом случае в исходном массиве меняется ссылка, хранящаяся в первом элементе, что не принесет никаких изменений для клонированного объекта. На консоли появится 1.

Во втором случае модифицируется существующий массив, что скажется на обоих двухмерных массивах. На консоли появится 0.

Обратите внимание, что если из примера убрать условие if-else, так, чтобы отрабатывал первый вариант, а затем второй, то результатом будет опять 1, поскольку в части второго варианта модифицироваться будет уже новый массив, порожденный в части первого варианта.

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

Заключение

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

Создать массив можно с помощью ключевого слова new, поскольку все массивы, включая определенные на основе примитивных значений, имеют объектный тип. Другой способ – воспользоваться инициализатором и перечислить значения всех элементов. В первом случае элементы принимают значения по умолчанию (0, false, null).

Особым образом в Java устроены многомерные массивы. Они, по сути, являются одномерными, основанными на массивах меньшей размерности. Такой подход позволяет единообразно работать с многомерными массивами. Также он дает возможность создавать не только «прямоугольные» массивы, но и массивы любой конфигурации.

Хотя массив и является ссылочным типом, работа с ним зачастую имеет некоторые особенности. Рассматриваются правила приведения типа массива. Как для любого объектного типа, приведение к Object является расширяющим. Приведение массивов, основанных на ссылочных типах, во многом подчиняется обычным правилам. А вот примитивные массивы преобразовывать нельзя. С преобразованиями связано и возникновение ошибки ArrayStoreException, причина которой – невозможность точного отслеживания типов в преобразованном массиве для компилятора.

В заключение рассматриваются последние случаи взаимосвязи типа переменной и ее значения.

Наконец, изучается механизм клонирования, существующий с самых первых версий Java и позволяющий создавать точные копии объектов, если их классы позволяют это делать, реализуя интерфейс Cloneable. Поскольку стандартное клонирование порождает только один новый объект, это приводит к особым эффектам при работе с объектными полями классов и массивами.

Java: С вычислительной точки зрения, что произойдет, если вы установите массив как ссылку на другой массив?

Результат этого кода равен 2.

Так что же здесь происходит?

Является ли это, что arr1 и arr2 находятся в разных ячейках памяти, но arr2 указывает на значения arr1?

Что случилось с исходными значениями arr2?

Доступны ли они или они были удалены сборкой мусора?

если они доступны, программист должен освободить память 2-го массива?

Следующее даст вам хорошую картину:

int [] arr1 = new int [] <4,6,3,2,7,8>; (Создайте новый массив, пусть arr1 укажет на него)

int [] arr2 = new int [] <6,7,5,3,2,9>; (Создайте новый массив, пусть arr2 укажет на него)

arr2 = arr1; (Пусть aar2 указывает на то, где aar1 указывает)

System.out.println(arr2 [3]); (aar1 & aar2 указывает на тот же массив)

Что случилось с исходными значениями arr2?

Когда исходный массив, на который указывает aar2, не был найден, он имеет право собирать сбор мусора. Собиратель мусора сам определит, когда его собрать.

Доступны ли они или они были удалены сборкой мусора?

Сбор мусора автоматически выполняется JVM, и вы не можете получить к нему доступ после его unreferencing.

arr1 и arr2 — обе ссылки.

Первоначально они относятся к разным массивам.

Когда вы пишете arr2 = arr1; , ничто не ссылается на второй массив в вашей программе, и поэтому он действительно имеет право на сбор мусора.

Эти переменные содержат ссылки. Так:

означает две вещи:

  • ссылка, указывающая на массив arr2 . отбрасывается
  • переменная arr2 теперь содержит ту же ссылку, что и arr1 содержит

Другими словами: после этой строки только один массив «остается». Поэтому arr2 [3] относится к тому же самому слоту матрицы, что и arr1 [3].

Этот «потерянный» массив все еще существует. Но сборщик мусора признает, что он больше не «доступен». Это означает: в случае, если GC решает собрать мусор, этот потерянный массив будет собран. Но учтите: тот факт, что объект имеет право на сбор мусора, не означает, что он будет собран в ближайшее время. Речь идет о том, должна ли/когда сборка должна произойти.

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

Является ли это, что arr1 и arr2 находятся в разных ячейках памяти, но arr2 указывает на значения arr1?

Ссылки находятся в разных местах, но содержат тот же адрес, который раньше был только адресом arr1.

Что случилось с исходными значениями arr2?

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

Доступны ли они или они были удалены сборкой мусора?

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

если они доступны, программист должен освободить память 2-го массива?

Java — В JAVA что с массивом, если его ссылка ссылается на дургой?

Массив (англ. Array) это объект, хранящий в себе фиксированное количество значений одного типа. Другими словами, массив — это нумерованный набор переменных. Переменная в массиве называется элементом массива, а ее позиция в массиве задается индексом. Например, нам нужно хранить 50 различных имен, согласитесь, неудобно для каждого имени создавать отдельную переменную, поэтому мы будем использовать массив. Нумерация элементов массива начинается с 0, а длинна массива устанавливается в момент его создания и фиксируется.

Для наглядности картинка, взятая мною с The Java Tutorial.

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

Объявление массива в Java

При создании массива в Java первым делом его нужно объявить. Это можно сделать следующим образом:

Можно также объявить массив так:

Однако, это не приветствуется соглашением по оформлению кода в Java, поскольку скобки обозначают то, что мы имеем дело с массивом и логичнее, когда они находятся рядом с обозначением типа.

Исходя из данного примера, мы объявили 2 массива с именами myFirstArray и mySecondArray. Оба массива будут содержать элементы типа int.

Подобным образом можно объявить массив любого типа:

Тип массива задается следующим образом type[], где type это тип данных содержащихся в нем элементов. Скобки являются специальным обозначением того, что переменные содержатся в массиве. Имя массива может быль любым, однако, оно должно соответствовать правилам именования переменных.

Массивы можно создавать не только из переменных базовых типов, но и из произвольных объектов.

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

Резервация памяти для массива и его инициализация.

Далее, для объявленного myFirstArray, зарезервируем память при помощи ключевого слова new.

В нашем примере мы создали массив из 15 элементов типа int и присвоили его ранее объявленной переменной myFirstArray.

Объявлять имя массива и резервировать для него память также можно на одной строке.

При создании массива с помощью ключевого слова new, все элементы массива автоматически инициализированы нулевыми значениями. Для того, чтобы присвоить элементам массива свои начальные значения, необходимо провести его инициализацию. Инициализацию можно проводить как поэлементно

так и в цикле, с помощью индекса проходя все элементы массива и присваивая им значения.

Как видно из предыдущих примеров, для того, чтобы обратиться к элементу массива, нужно указать его имя и, затем, в квадратных скобках — индекс элемента. Элемент массива с конкретным индексом ведёт себя также, как и переменная.

Рассмотрим создание и инициализацию массива на следующем примере. В нем мы создаем массив, содержащий цифры 0-9 и выводим значения на консоль.

Упрощенная форма записи

Для создания и инициализации массива можно также использовать упрощенную запись. Она не содержит слово new, а в скобках перечисляются начальные значения массива.

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

Определение размера массива

Размер массива не всегда очевиден, поэтому для того, чтобы его узнать следует использовать свойство length, которое возвращает длину массива.

Данный код поможет нам узнать, что длина массива myColor равна 3.

Пример: Задано 4 числа, необходимо найти минимальное

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих