1. Масив типу String
Поговорімо коротко про масив типу String.
Як ми вже казали, масив може бути будь-якого типу. А отже, можна створити масив типу String. Ось як виглядав би код, якби нам треба було написати програму, яка «зчитує з клавіатури 10 рядків і виводить їх на екран у зворотному порядку».
String[] array = new String[10]; // Створюємо обʼєкт-масив на 10 елементів
for (int i = 0; i < 10; i++) // Цикл від 0 до 9
{
array[i] = console.nextLine(); // Читаємо рядок з клавіатури та зберігаємо його в елемент масиву
}
for (int i = 9; i >= 0; i--) // Цикл від 9 до 0
{
System.out.println(array[i]); // Виводимо на екран черговий елемент масиву
}
Код майже не змінився! Під час створення масиву ми лише замінили тип int на String. В іншому — усе те саме.
Знайомство з null
Ось загадка — що міститься в комірках кожного з цих масивів одразу після створення?
int[] numbers = new int[10];
String[] strings = new String[10];
User[] users = new User[10];
Якщо подумати, то, швидше за все, комірки numbers містять 0. Правильно.
Тоді, за аналогією, може здатися, що комірки strings заповнені порожніми рядками — "". Але ні.
А чим заповнені комірки масиву users? Порожніми обʼєктами типу User?
Ось де підступ: не для кожного типу даних існує «значення за замовчуванням». Тому творці Java придумали спеціальну константу — null. null — це порожнє посилання. Коли створюється змінна для типу обʼєкта, її стартове значення — null: посилання ні на що, відсутність посилання.
String name; // name містить null
name = "Alex"; // name містить посилання на обʼєкт/рядок "Alex"
name = null; // name містить null
Робота з null
Не можна викликати методи в обʼєкта, якщо посилання на нього null — обʼєкта ж немає! У такому коді програма завершиться з помилкою:
String name; // name містить null
String upperName = name.toUpperCase(); // Виникне помилка NullPointerException: name == null
Що ж до нашої «загадки», відповідь буде такою:
int[] numbers = new int[10]; // {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
String[] strings = new String[10]; // {null, null, null, null, null, null, null, null, null, null}
User[] users = new User[10]; // {null, null, null, null, null, null, null, null, null, null}
Лише примітивні типи мають «значення за замовчуванням». Усі інші типи за замовчуванням мають значення null. Стартове значення String — це не порожній рядок. Таке життя.
2. Масив типу String у памʼяті
Картинка 1. Як обʼєкт String розташовується в памʼяті:

Зверніть увагу, що текст рядка не зберігається безпосередньо в змінній: для нього виділяється окремий блок памʼяті. А в змінній типу String зберігається адреса (посилання) на обʼєкт із текстом.
Картинка 2. Як масив цілих чисел розташовується в памʼяті:

Цю картинку ви також уже бачили.
Картинка 3. Як у памʼяті розташовується масив рядків:

Ліворуч бачимо змінну-масив типу String[] (вона зберігає адресу обʼєкта-масиву).
Посередині — обʼєкт-масив типу String[].
А праворуч — обʼєкти-рядки, що зберігають якісь тексти.
У комірках обʼєкта-масиву типу String[] зберігаються не самі рядки (тексти), а їхні адреси (посилання). Точно так само, як в змінних типу String зберігаються адреси рядків (текстів).
3. Швидка ініціалізація масиву в Java
Масиви — дуже корисна річ, тож розробники Java постаралися зробити роботу з ними максимально зручною. І перше, що вони зробили, — спростили ініціалізацію масиву, тобто занесення в нього стартових значень.
Часто, окрім даних, які програма десь зчитує, для роботи потрібні ще й внутрішні дані. Наприклад, нам треба зберігати в масиві тривалості всіх місяців. Ось як може виглядати цей код:
int[] months = new int[12];
months[0] = 31; // січень
months[1] = 28; // лютий
months[2] = 31; // березень
months[3] = 30; // квітень
months[4] = 31; // травень
months[5] = 30; // червень
months[6] = 31; // липень
months[7] = 31; // серпень
months[8] = 30; // вересень
months[9] = 31; // жовтень
months[10] = 30; // листопад
months[11] = 31; // грудень
Але є коротший спосіб — завдяки творцям Java:
// тривалості місяців року
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
Можна просто перерахувати через кому всі значення масиву!
Виявляється, компілятор може визначити тип контейнера (обʼєкта-масиву) на основі типу змінної-масиву. А щоб визначити довжину масиву — банально підрахувати кількість елементів, записаних у фігурних дужках.
Тому цей код можна записати ще коротше:
// тривалості місяців року
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
Хіба не краса? 🙂 Такий запис зветься «швидка ініціалізація масиву». Вона, до речі, працює не лише для типу int...
// назви місяців року
String[] months = { "Січень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень" };
4. Цикл for-each
Перебирання всіх елементів масиву — така часта операція, що для неї створили спеціальний цикл — for-each. Записується він так:
for (int score : scores)
{
System.out.println("Бали: " + score);
}
Тут цикл сам перебирає елементи — жодного ручного індексу. Але не можна змінювати елементи напряму (у змінній циклу зберігається лише копія значення), зате дуже зручно просто «пройтися» масивом.
Компілятор перетворить код циклу for-each на наведений нижче код:
for (int i = 0; i < scores.length; i++)
{
int score = scores[i];
System.out.println("Бали: " + score);
}
Отже, ніякої магії. Зате тепер зрозуміло, чому ви не можете змінити елементи масиву через змінну score.
Напишімо ще приклад: знайдемо суму всіх балів.
int sum = 0;
for (int score : scores)
{
sum += score;
}
System.out.println("Сума всіх балів: " + sum);
Коли використовувати який цикл?
- Якщо потрібен індекс або треба змінювати елементи — цикл for.
- Якщо просто пройтися по значеннях — for-each швидше й чистіше.
5. Зміна елементів масиву
А що, якщо нам треба збільшити кожну оцінку на 1 бал (наприклад, за гарну поведінку)? Тут уже потрібен класичний for з індексом, адже лише так можна змінити значення в масиві.
Приклад: збільшити кожен елемент на 1
for (int i = 0; i < grades.length; i++) {
grades[i] = grades[i] + 1;
}
Після виконання цього коду в масиві grades будуть нові значення.
Чому не можна так зробити через for-each?
for (int grade : grades) {
grade = grade + 1; // Не працює!
}
Цей код не змінить масив, тому що grade — це копія значення з масиву, а не посилання на сам елемент.
6. Обчислення на основі масиву
Масиви часто використовують для розрахунків: сума, середнє, максимум, мінімум тощо.
Приклад: сума всіх елементів масиву
int sum = 0;
for (int i = 0; i < grades.length; i++) {
sum += grades[i]; // те саме, що sum = sum + grades[i];
}
System.out.println("Сума оцінок: " + sum);
Приклад: пошук максимального значення
int max = grades[0]; // почнемо з першого елемента
for (int i = 1; i < grades.length; i++) {
if (grades[i] > max) {
max = grades[i];
}
}
System.out.println("Максимальна оцінка: " + max);
Приклад: пошук мінімального значення
int min = grades[0]; // почнемо з першого елемента
for (int i = 1; i < grades.length; i++) {
if (grades[i] < min) {
min = grades[i];
}
}
System.out.println("Мінімальна оцінка: " + min);
Приклад: обчислення середнього арифметичного
int sum = 0;
for (int i = 0; i < grades.length; i++) {
sum += grades[i];
}
double average = (double) sum / grades.length; // обовʼязково перетворити до double!
System.out.println("Середня оцінка: " + average);
Зверніть увагу: щоб отримати не ціле, а дробове число, потрібно явно перетворити суму до типу double перед діленням.
7. Практичні завдання
Введення масиву з клавіатури
Часто треба заповнити масив значеннями, які вводить користувач. Для цього використовуємо клас Scanner і цикл.
Scanner console = new Scanner(System.in);
int n = 5; // розмір масиву
int[] numbers = new int[n];
System.out.println("Введіть " + n + " чисел:");
for (int i = 0; i < n; i++) {
numbers[i] = console.nextInt();
}
System.out.println("Ви ввели:");
for (int i = 0; i < n; i++) {
System.out.println(numbers[i]);
}
Виведення масиву у зворотному порядку
Іноді потрібно вивести елементи масиву з кінця (наприклад, щоб дізнатися, хто останнім зайшов до кімнати).
for (int i = grades.length - 1; i >= 0; i--) {
System.out.println("Оцінка №" + (i + 1) + ": " + grades[i]);
}
8. Типові помилки під час роботи з одномірними масивами
Помилка № 1: вихід за межі масиву
Найчастіша проблема — спроба звернутися до неіснуючого елемента. У Java це призводить до винятку ArrayIndexOutOfBoundsException. Памʼятайте: останній допустимий індекс — arr.length - 1.
int[] arr = new int[5];
System.out.println(arr[5]); // Помилка! Індекси від 0 до 4.
Помилка № 2: забули ініціалізувати масив
Оголосили змінну, але масив не створили:
int[] arr;
arr[0] = 5; // Помилка! Масив не створено.
Потрібно обовʼязково створити масив за допомогою new:
arr = new int[10];
Помилка № 3: спроба змінити елементи масиву через for-each
У циклі for-each змінна — це копія значення, а не посилання на елемент:
for (int x : arr) {
x = 100; // Не змінює масив!
}
Щоб змінити значення, використовуйте звичайний for з індексом.
Помилка № 4: неправильне використання довжини масиву
Іноді плутають довжину масиву з останнім індексом:
for (int i = 0; i <= arr.length; i++) { // Помилка! Має бути i < arr.length
// ...
}
Такий код призведе до виходу за межі масиву.
Помилка № 5: неявне перетворення типів
Якщо масив типу int, не можна напряму присвоїти йому значення типу double:
int[] arr = new int[3];
arr[0] = 3.14; // Помилка! 3.14 — це double.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ