Лабораторна робота 5
Робота з масивами та рядками Java. Створення класів
1 Завдання на лабораторну роботу
1.1 Індивідуальне завдання
Спроектувати та реалізувати два класи відповідно до індивідуального завдання. У першому з класів повинен бути описаний масив елементів другого класу. Класи повинні мати конструктори, приватні поля та відкриті методи, зокрема методи доступу (сеттери та геттери).
Слід окремо здійснити тестування кожного з класів, після чого окремими методами першого класу реалізувати основне завдання. У функції main()
першого з класів створити необхідний об'єкт та викликати для нього методи, які реалізують основне завдання. Вивести результати у консольне вікно.
Варіант завдання, який слід реалізувати у програмі, визначається залежно від номеру студента у списку групи.
№№ | Перший клас | Другий клас | Основне завдання: знайти та вивести такі дані |
||
---|---|---|---|---|---|
Сутність | Обов'язкові поля | Сутність | Обов'язкові поля | ||
1 | Погода | Сезон, коментар | День | Дата, температура, коментар | Середня температура, день з максимальною температурою, день з найдовшим коментарем |
2 | Навчальний курс | Назва, наявність іспиту | Практичне заняття | Дата, тема, кількість студентів | Середня кількість студентів, заняття з максимальною кількістю студентів, список тем з певним словом у назві |
3 | Трамвайна зупинка | Назва, список номерів маршрутів | Година | Кількість пасажирів, коментар | Загальна кількість пасажирів, година з найменшою кількістю пасажирів, найдовший коментар |
4 | Навчальний курс | Назва, прізвище викладача | Лекція | Дата, тема, кількість студентів | Лекція з мінімальною кількістю студентів, список тем з певним словом у назві, остання літера у прізвищі викладача |
5 | Погода | Рік, коментар | Вимір температури | Дата, температура, коментар | Виміри з мінімальною температурою, з найбільшою кількістю слів у коментарі до виміру, останнє слово коментаря до погоди |
6 | Конференція | Назва, місце проведення | Засідання | Дата, тема, кількість учасників | Середня кількість учасників на засіданні, засідання з найбільшою кількістю учасників, довжина назви |
7 | Виставка | Назва, прізвище художника | День | Кількість відвідувачів, коментар | Сумарна кількість відвідувачів, день з найменшою кількістю відвідувачів, список коментарів з певним словом |
8 | Станція метрополітену | Назва, рік відкриття | Година | Кількість пасажирів, коментар | Сумарна кількість пасажирів, години з найменшою кількістю пасажирів та найбільшою кількістю слів у коментарі |
9 | Лікар | Прізвище, фах | Прийом | День, зміна, кількість відвідувачів | Загальна кількість відвідувачів, прийом з мінімальною кількістю відвідувачів, довжина прізвища |
10 | Музичний гурт | Назва, прізвище керівника | Гастрольна поїздка | Місто, рік, кількість концертів | Гастрольна поїздка з максимальною кількістю концертів, список гастрольних поїздок у певне місто, остання літера в прізвищі керівника |
11 | Майстерня | Назва, адреса | Зміна | Кількість відремонтованих комп'ютерів | Сумарна кількість комп'ютерів, зміна з найбільшою кількістю відремонтованих комп'ютерів, довжина назви вулиці |
12 | Лікар | Прізвище, стаж | Прийом | День, кількість відвідувачів, коментар | Середня кількість відвідувачів, прийом з мінімальною кількістю відвідувачів, найдовшим коментарем |
13 | Трамвайний маршрут | Номер, середній інтервал руху | Зупинка | Назва, кількість пасажирів | Загальна кількість пасажирів, зупинки з найменшою кількістю пасажирів, найдовшою назвою |
14 | Цілодобовий кіоск | Назва, адреса | Година | Кількість покупців, коментар | Загальна кількість покупців, година з найменшою кількістю покупців, коментарями з певними словами |
15 | Виконавець | Прізвище, жанр | Концерт | Дата, кількість глядачів | Загальна кількість глядачів, концерт з максимальною кількістю глядачів, кількість слів у назві жанру |
16 | Поет | Прізвище, мова, кількість збірок | Виступ | Дата, місце, кількість слухачів |
Сумарна кількість слухачів, день з найбільшою кількістю слухачів, довжина прізвища |
17 | Навчальний курс | Назва курсу, назва кафедри | Лекція | Дата, група, кількість студентів | Середня кількість студентів, лекції з найбільшою кількістю студентів, кількість слів у назві кафедри |
18 | Виставка | Назва, прізвище скульптора | День | Кількість відвідувачів, коментар | Сумарна кількість відвідувачів, день з найбільшою кількістю відвідувачів, день з найбільшою кількістю слів у коментарі |
19 | Конференція | Назва, місце проведення | Засідання | Дата, тема, кількість учасників | Середня кількість учасників на засіданні, засідання з найменшою кількістю учасників, тема з найдовшою назвою |
20 | Письменник | Прізвище, мова, кількість книжок | Виступ | Дата, місце, кількість слухачів | Сумарна кількість слухачів, день з найменшою кількістю слухачів, довжина прізвища |
21 | Співробітник | ПІБ, посада | Робочій день | Дата, кількість годин, назва проекту | Середня кількість робочих годин за період; Кількість годин на проекті; Дні з максимальним навантаженням |
22 | Лікар | ПІБ, спеціальність | Робочій день | Дата, кількість пацієнтів, час початку роботи | Середня кількість пацієнтів в день за період; Кількість днів з максимальним навантаженням; Дні, коли починав приймати після зазначеного часу |
23 | Піцерія | Назва, адреса | Робочій день | Дата, кількість замовлень, піца дня | Середня кількість замовлень в день за період; Дні з максимальним відвідуванням; Сумарна кількість замовлень для днів з визначеною піцою дня |
24 | Басейн | Назва, адреса | Робочій день | Дата, кількість відвідувачів, кількість доступних доріжок | Середня кількість відвідувань в день за період; Дні з мінімальною кількістю доступних доріжок; Кількість днів, коли було доступно не менше зазначеної кількості доріжок |
25 | Бібліотека | Назва, адреса | Робочій день | Дата, кількість книг, що видано, кількість книг, що повернуто | Середній рух книжок в день за період; Кількість днів, коли було видано книг більше, ніж повернуто; Дні, коли видана парна кількість книг, а повернута – непарна |
26 | Сайт | Назва, URL | Відвідування | Дата, кількість унікальних хостів, кількість завантажених сторінок | Середня кількість хостів в день за період; Дні з максимальною кількістю завантажених сторінок; Кількість днів, коли співвідношення хостів до сторінок перевищує задане значення |
27 | Веб-аукціон | Назва, URL | Дані за добу | Дата, кількість актуальних лотів, сумарна вартість лотів | Середня кількість лотів в день за період; Дні, коли кількість актуальних лотів збільшувалась; Кількість днів з максимальною сумарною вартістю лотів |
28 | Почтовий акаунт | E-mail, ПІБ володаря | Спам | Дата, кількість спам повідомлень, загальна кількість повідомлень | Середня кількість спаму в день за період; Кількість днів, коли відсоток спам повідомлень був менший за задане значення; Дні, коли кількість спаму збільшувалась |
29 | Акція компанії на біржі | Назва компанії, код на біржі | Курс | Дата, курс відкриття, курс закриття | Середня вартість акцій по закриттю за період; Кількість днів, коли курс зростав протягом дня; Дні, коли зміна курсу за день перевищувала задане значення |
30 | Телефонний номер | Номер, оператор | Дзвінки | Дата, кількість хвилин розмов, кошти, що використано на розмови | Середня платня в день за період; Кількість днів, коли вартість хвилини розмови перевищувала задане значення; Дні, коли кількість хвилин розмов була парна |
1.2 Ератосфенове решето
Заповнити масив із трьохсот цілих чисел послідовними додатними значеннями. Замінити всі значення, що не є простими числами, деяким від'ємним значенням. Для цього послідовно виключати всі числа – дільники інших чисел. Вивести на екран додатні значення, що залишилися, (прості числа).
1.3 Сортування вибором (задача підвищеної складності)
Проініціалізувати одновимірний масив цілих чисел випадковими значеннями. Здійснити упорядкування масиву методом сортування вибором за таким алгоритмом:
- знаходимо індекс мінімального значення;
- здійснюємо обмін цього значення із значенням першої невідсортованої позиції (обмін не потрібний, якщо мінімальний елемент вже знаходиться на цій позиції);
- сортуємо решту масиву, виключивши з розгляду вже відсортовані елементи.
Результат вивести на екран.
1.4 Знаходження чисел Фібоначчі
Реалізувати функцію обчислення чисел Фібоначчі (до 92-го числа включно) з використанням допоміжного масиву (статичного поля). Параметр функції – номер числа Фібоначчі. Пошук чисел Фібоначчі здійснюється за таким правилом:
F(1) = F(2) = 1; F(n) = F(n - 2) + F(n - 1)
Під час першого виклику функції масив заповнюється до необхідного числа. Під час наступних викликів число або повертається з масиву, або обчислюється з використанням останніх двох чисел, що зберігаються у масиві з подальшим заповненням масиву. Використовувати тип long
для представлення чисел.
Здійснити тестування функції для різних значень номерів, що вводяться у довільному порядку.
1.5 Вирівнювання рядку
Прочитати аргумент командного рядка і додати в нього пробіли так, щоб довжина рядка дорівнювала заданому числу.
1.6 Абревіатура
Увести з клавіатури рядок з декількох слів. Скласти рядок з перших літер слів з переведенням цих літер у верхній регістр.
1.7 Квадратне рівняння
Спроектувати клас для розв'язання квадратного рівняння. Слід передбачити аналіз усіх можливих комбінацій коефіцієнтів та відповідних результатів (два корені, один корінь для випадку виродження у лінійне рівняння, немає розв'язків, безмежна кількість коренів). Метод безпосереднього знаходження коренів (solve()) повинен повертати кількість коренів (-1 якщо безмежна кількість коренів). Геттери для x1 і x2 повинні повертати корені тільки, якщо було визначено вихідні дані та здійснено пошук коренів.
2 Методичні вказівки
2.1 Посилання
У Java немає типів указівників. Імена змінних непримітивних типів по суті є іменами посилань на відповідні об'єкти. Усі непримітивні типи мають назву типів-посилань. Розіменування не потрібне: звернення до примітивних типів завжди здійснюється за значенням, а до непримітивних – за посиланням. Типи-посилання ніколи не можуть бути приведені до примітивних і навпаки. Java також не підтримує адресної арифметики.
Спеціальне ключове слово null
використовують для того, щоб показати, що змінна типу-посилання ні на що не посилається. Константа null
може бути присвоєна змінній будь-якого типу-посилання.
Об'єкти, на які вказують посилання, повинні бути розміщені в динамічній пам'яті за допомогою операції new
:
SomeType st = new SomeType();
Присвоювання значення одного посилання іншому не забезпечує копіювання об'єктів. Після присвоювання два посилання посилаються на один об'єкт.
SomeType a = new SomeType(); SomeType b = new SomeType(); a = b;
Об'єкт, на який раніше посилалося a
, загублений.
На відміну від С++, звільнення пам'яті від непотрібних об'єктів не потрібно. У Java немає операції delete
. Для звільнення пам'яті використовується спеціальний механізм, який має назву збирання сміття. Цей механізм базується на підрахунку посилань на об'єкти. Кожен об'єкт має свій лічильник посилань. Коли посилання копіюється в нову змінну типу-посилання, лічильник збільшується на одиницю. Коли посилання виходить з області видимості, чи перестає вказувати на об'єкт, лічильник зменшується на одиницю. Коли віртуальній машині Java не вистачає оперативної пам'яті, запускається збирач сміття. Він переглядає список об'єктів і видаляє з пам'яті всі об'єкти, для яких кількість посилань дорівнює 0.
Операція ==
, застосована до змінних-посилань здійснює порівняння адрес, а не вмісту об'єктів.
Аргументи функцій типу-посилання передаються у функції за посиланням. Всередині функції створюється нове посилання на той самий об'єкт. Цей об'єкт можна змінити у функції та після повернення з функції використовувати його значення.
2.2 Масиви
2.2.1 Опис масивів
Масив (array) – це набір комірок зберігання даних, кожна з яких має той же тип. Окрема комірка має назву елемента масиву (array item). На відміну від інших складених типів даних, масиви завжди розташовуються в цілісному блоці пам'яті. Оскільки всі елементи займають однакові за розміром комірки пам'яті, адреса конкретного елемента завжди може бути обчислена за його номером. Номери елементів називаються індексами. Звернення до конкретних елементів здійснюють через індекс.
Як і в С++, масиви в Java індексуються починаючи з нуля. Під час опису масиву квадратні дужки ставляться після імені типу, а не імені змінної. Розміщення квадратних дужок після імені змінної допускається, але не рекомендується.
int[] a;
У цьому прикладі описується посилання на масив, який буде створений пізніше. Розмір масиву не є частиною типу, як це реалізовано в C++. Це дозволяє визначити масив необхідного розміру під час виконання програми
int[] numbers; // посилання на масив цілих чисел довільної довжини numbers = new int[10]; // numbers складається з 10 елементів numbers = new int[20]; // numbers складається з 20 елементів
У Java підтримуються одно- і багатовимірні масиви (масиви масивів). Наприклад, так можна визначити посилання на двовимірний масив:
byte[][] scores;
Наступні приклади показують, як створити різні масиви:
int[] numbers = new int[5]; // одновимірний масив double[][] matrix = new double[5][4]; // прямокутний масив byte[][] scores = new byte[5][]; // двовимірний масив з різною довжиною рядків for (int i = 0; i < 5; i++) { scores[i] = new byte[i + 4]; }
Останній приклад показує як можна створити двовимірний масив з різною довжиною рядків. Розмір масиву може бути визначений як за допомогою констант, так і за допомогою змінних і виразів, що дають цілий результат.
У Java масиви містять елементи разом з їх кількістю. Кількість елементів масиву завжди можна отримати за допомогою спеціального поля length
. Це поле доступне тільки для читання:
int[] a = new int[10]; System.out.println(a.length); // 10
У Java передбачений простий спосіб ініціалізації масиву списком початкових значень:
int[] numbers = new int[] {1, 2, 3, 4, 5};
Можна опустити операцію new
:
int[] numbers = {1, 2, 3, 4, 5};
Аналогічно ініціалізуються багатовимірні масиви:
int[][] b = {{1, 2, 3}, {0, 0, 1}, {1, 1, 11}, {0, 0, 0}};
У наведеному прикладі створюється двовимірний масив з чотирьох рядків і трьох стовпців.
Так виглядає типовий цикл для обходу масиву:
for (int i = 0; i < a.length; i++) { a[i] = 0; }
Аналогічно обхід усіх елементів двовимірного масиву зазвичай здійснюється так:
for (int i = 0; i < c.length; i++) { for (int j = 0; j < c[i].length; j++) { c[i][j] = 0; } }
Як видно з останнього прикладу, для отримання розміру i-го рядка двовимірного масиву використовують конструкцію c[i].length
.
Альтернативна форма циклу for
(починаючи з JDK 1.5) дозволяє спростити повний обхід масивів. Наприклад, замість
int[] nums = {1, 2, 3, 4, 5, 6}; for (int i = 0; i < nums.length; i++) { System.out.println(nums[i]); }
можна написати
int[] nums = {1, 2, 3, 4, 5, 6}; for(int n : nums) { System.out.println(n); }
Альтернативна форма може бути застосована. лише для читання значень елементів, а не для їхньої модифікації.
Масиви читають з клавіатури поелементно. Наведений нижче приклад демонструє читання кількості та значень елементів з клавіатури.
package ua.inf.iwanoff.labs.fifth; public class ArrayTest { public static void main(String[] args) { System.out.println("Уведіть кількість елементів масиву:"); java.util.Scanner s = new java.util.Scanner(System.in); int size = s.nextInt(); double[] a = new double[size]; System.out.println("Уведіть елементи масиву:"); for (int i = 0; i < a.length; i++) { a[i] = s.nextDouble(); } // Робота з масивом // ... } }
Присвоювання імені масиву іншому імені масиву приводить тільки до копіювання посилання, але не самого масиву. Для копіювання елементів масиву необхідно організувати цикл або скористатися стандартними функціями.
2.2.2 Масиви як параметри та результат функцій
Масиви-параметри передаються у функції за посиланням. Після повернення з функції елементи можуть містити змінені значення:
package ua.inf.iwanoff.labs.fifth; public class SwapElements { static void swap(int[] a) { int z = a[0]; a[0] = a[1]; a[1] = z; } public static void main(String[] args) { int[] b = {1, 2}; swap(b); System.out.println(b[0]); // 2 System.out.println(b[1]); // 1 } }
Якщо параметр описано як одновимірний масив, то можна фактичним параметром вказати рядок двовимірного масиву. Також можна використовувати параметри типу багатовимірних масивів. Наприклад:
package ua.inf.iwanoff.labs.fifth; public class ArraysTest { static double sum(double[] arr1D) { double s = 0; for (double elem : arr1D) { s += elem; } return s; } static double sum(double[][] arr2D) { double s = 0; for (double[] line : arr2D) { for (double elem : line) { s += elem; } } return s; } public static void main(String[] args) { double[][] arr = {{1, 2}, {3, 4}, {5, 6}}; System.out.printf("Сума елементів рядка з індексом 1: %f%n", sum(arr[1])); System.out.printf("Сума всіх елементів: %f%n", sum(arr)); } }
У Java 1.5 з'явилася додаткова можливість створення функцій зі змінним числом параметрів визначеного типу (Variable Arguments List, varargs). Всередині функції такі параметри інтерпретуються як масив:
static void printIntegers(int... a) { for (int i = 0; i < a.length; i++) { System.out.println(a[i]); } }
Викликати таку функцію можна у два способи: передаючи список аргументів типу елемента масиву, або передаючи масив цілком:
public static void main(String[] args) {
printIntegers(1, 2, 3);
int[] arr = {4, 5};
printIntegers(arr);
}
Такий параметр може бути лише один і обов'язково розташований останнім в списку.
Функція може повертати посилання на масив. Найчастіше такий масив створюється всередині функції. Наприклад, створюємо масив цілих, заповнений одиницями:
static int[] arrayOfOnes(int n) { int[] arr = new int[n]; for (int i = 0; i < arr.length; i++) { arr[i] = 1; } return arr; }
Тепер можна створити масив за допомогою нашої функції:
int[] a = arrayOfOnes(6);
Якщо потрібен тільки один елемент, для нього можна не створювати масив з ім'ям і поєднати створення масиву з його використанням:
System.out.println((arrayOfOnes(2)[0]));
Можна також повертати посилання на багатовимірні масиви.
Посилання на масиви передаються за значенням.
static void resize(int[] arr, int newSize) { // збереження існуючих елементів arr = new int[newSize]; // копіювання }
Нове посилання слід повертати, інакше новий масив буде втрачено:
static int[] resize(int[] arr, int newSize) { // збереження існуючих елементів arr = new int[newSize]; // копіювання; return arr; }
2.2.3 Стандартні функції для роботи з масивами
Клас System
надає найпростіший шлях копіювання одного масиву в іншій – використання статичного методу arraycopy()
:
System.arraycopy(a, a_from, b, b_from, size);
Це еквівалентно такому циклу:
for (int i = a_from, j = b_from; i < size + a_from; i++, j++) { b[j] = a[i]; }
Масив, у який здійснюється копіювання, повинен мати необхідні розміри. Функція arraycopy()
не створює нового масиву. Весь масив a
можна скопіювати в b
таким викликом:
System.arraycopy(a, 0, b, 0, a.length);
Для заповнення масивів можна використовувати статичні методи класу Arrays
, реалізованого в пакеті java.util
.
Статичний метод класу Arrays | Опис |
---|---|
void fill(тип[] a, тип val) |
заповнює масив вказаними значеннями |
void fill(тип[] a, int fromIndex, int toIndex, тип val) |
заповнює частину масиву вказаними значеннями |
String toString(тип[] a) |
повертає список елементів масиву у вигляді рядка |
String deepToString(тип[][] a) |
подає багатовимірний масив у вигляді рядка |
тип[] copyOf(тип[] a, int len) |
створює новий масив довжини len з копіями елементів |
тип[] copyOfRange(тип[] a, int from, int to) |
створює новий масив з копіями елементів діапазону |
boolean equals(тип[] a, тип[] a2) |
перевіряє еквівалентність елементів двох масивів |
boolean deepEquals(тип[][] a, тип[][] a2) |
перевіряє еквівалентність елементів багатовимірних масивів |
void sort(тип[] a) |
здійснює сортування елементів за зростанням |
void sort(тип[] a, int fromIndex, int toIndex) |
здійснює сортування частини елементів за зростанням |
int binarySearch(тип[] a, тип key) |
здійснює пошук у відсортованому масиві |
У таблиці тип
означає один з фундаментальних (примітивних) типів або тип Object
.
Перший варіант функції fill()
використовується для заповнення всього масиву, другий – частини, при чому елемент з номером toIndex
не включається в послідовність. Наприклад:
package ua.inf.iwanoff.labs.fifth; public class FillArray { public static void main(String[] args) { int[] a = new int[6]; java.util.Arrays.fill(a, 0, 4, 12); // Інші елементи дорівнюють 0 for (int x : a) { System.out.print(x + " "); } System.out.println(); java.util.Arrays.fill(a, 100); // Всі елементи дорівнюють 100 for (int x : a) { System.out.print(x + " "); } System.out.println(); } }
У попередньому прикладі для виведення елементів масиву на екран був використаний цикл. Альтернативний спосіб – використання функції toString()
класу Arrays
. Ця функція повертає представлення масиву у вигляді рядка, яке є зручним для більшості застосувань:
java.util.Arrays.fill(a, 100); System.out.println(Arrays.toString(a)) // [100, 100, 100, 100, 100, 100];
Примітка: для виведення в рядок багатовимірних масивів слід використовувати функцію deepToString()
.
Клас Arrays
надає альтернативний шлях копіювання масивів. Функція copyOf()
створює новий масив копій елементів. Перший параметр – вихідний масив, другий параметр – довжина результуючого масиву. Елементи, які не помістилися, відкидаються, відсутні заповнюються нулями. Функція copyOfRange(тип[] a, int from, int to)
копіює в новий масив частину масиву, включаючи початок інтервалу і не включаючи кінця інтервалу:
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class CopyOfTest { public static void main(String[] args) { int[] a = { 1, 2, 3, 4 }; int[] b = Arrays.copyOf(a, 3); System.out.println(Arrays.toString(b));// [1, 2, 3] int[] c = Arrays.copyOf(a, 6); System.out.println(Arrays.toString(c));// [1, 2, 3, 4, 0, 0] int[] d = Arrays.copyOfRange(a, 1, 3); System.out.println(Arrays.toString(d));// [2, 3] } }
Порівняти два масиви чи частину їх можна за допомогою функцій групи equals()
. Масиви порівнюються поелементно. Два масиви також вважаються еквівалентними, якщо обидва посилання – null
. Наприклад:
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class ArraysComparison { public static void main(String[] args) { double[] a = null, b = null; System.out.println(Arrays.equals(a, b)); // true a = new double[] { 1, 2, 3, 4 }; b = new double[4]; System.out.println(Arrays.equals(a, b)); // false System.arraycopy(a, 0, b, 0, a.length); System.out.println(Arrays.equals(a, b)); // true b[3] = 4.5; System.out.println(Arrays.equals(a, b)); // false } }
Є також метод deepEquals()
, використання якого аналогічне. Різниця є істотною для багатовимірних масивів. Здійснюється більш "глибока" перевірка:
int[][] a1 = { { 1, 2 } , { 3, 4 } }; int[][] a2 = { { 1, 2 } , { 3, 4 } }; System.out.println(Arrays.equals(a1, a2)); // false System.out.println(Arrays.deepEquals(a1, a2));// true
За допомогою функції sort()
можна здійснити сортування масиву чисел за зростанням. Наприклад:
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class ArraySort { public static void main(String[] args) { int[] a = new int[] { 11, 2, 10, 1 }; Arrays.sort(a); // 1 2 10 11 for (int x : a) { System.out.print(x + " "); } System.out.println(); } }
Функція sort()
реалізована для масивів усіх примітивних типів та рядків. Рядки впорядковуються за алфавітом. Можна також сортувати частину масиву. Як і для функції fill()
, вказується початковий і кінцевий індекси послідовності, яку слід відсортувати. Кінцевий індекс не включається в послідовність. Наприклад:
int[] a = {7, 8, 3, 4, -10, 0}; java.util.Arrays.sort(a, 1, 4); // 7 3 4 8 -10 0
У відсортованих масивах можна виконати пошук за допомогою методів класу Arrays
. Група функцій binarySearch()
, реалізована для всіх примітивних типів і типу Object
, повертає індекс знайденого елемента або від'ємне значення, якщо елемент відсутній.
2.3 Визначення класів
2.3.1 Поля і методи
Клас -
це структурований тип даних, набір елементів даних різних типів і функцій для роботи з цими даними. Опис класу складається зі специфікаторів (наприклад, public
, final
), імені, імені базового класу, списку інтерфейсів і тіла у фігурних дужках.
Тіло класу містить поля (їм відповідають елементи даних у C++) і методи (функції-елементи в C++). Поля і методи разом іменуються елементами (членами) класу. Нижче наводиться приклад опису класу:
class Rectangle { double width; double height; double area() { return width * height; } }
Після останньої фігурної дужки, що закривається, не слід ставити крапку з комою. Методи завжди реалізуються усередині визначення класу.
Під час створення об'єкта класу поля ініціалізуються усталеними значеннями (нулями або null
для посилань). Java допускає ініціалізацію полів початковими значеннями:
class Rectangle { double width = 10; double height = 20; double area() { return width * height; } }
Можна створити спеціальний блок ініціалізації усередині тіла класу. Такий блок виконуватиметься щораз під час створення нового об'єкта:
class Rectangle { double width; double height; { width = 10; height = 20; } double area() { return width * height; } }
Для того, щоб працювати з полями і методами класу, необхідно створити об'єкт. Для цього спочатку створюють посилання на об'єкт, а потім за допомогою операції new
створюють сам об'єкт шляхом виклику конструктора. Ці дії можна поєднати. Після цього можна викликати методи і використовувати поля:
Rectangle rect = new Rectangle(); // rect - ім'я посилання на об'єкт double a = rect.area(); // a = 200 rect.width = 15; // зміна значення поля double b = rect.area(); // b = 300
Під час виклику методів аргументи передаються за значенням.
Ключове слово this
використовується як посилання на об'єкт, для якого викликаний метод. Усі нестатичні методи неявно отримують посилання на об'єкт для якого вони використані. Ключове слово this
використовувати явно, наприклад, коли треба повернути з функції посилання на поточний об'єкт, або запобігти конфлікту імен.
2.3.2 Специфікатори доступу. Інкапсуляція
Як і C++, Java підтримує закритий (private
), пакетний, захищений (protected
) і відкритий (public
) рівні доступу. Сам клас може бути оголошений як public
. На відміну від C++, Java вимагає окремої специфікації доступу для кожного елемента, або групи полів одного типу:
public class Rectangle { private double width = 10; private double height = 20; { width = 30; height = 40; } public void setWidth(double width) { this.width = width; } public double getWidth() { return width; } public void setHeight(double height) { this.height = height; } public double getHeight() { return height; } public double area() { return width * height; } }
Доступ до закритих (private
) елементів класу обмежений методами усередині класу. У Java немає ключового слова friend
, яке у С++ забезпечує доступ до закритих елементів ззовні класу.
Відкриті (public
) елементи відкритого класу можуть бути доступні з будь-якої функції будь-якого пакету.
Елементи класу без атрибутів доступу мають пакетну видимість. Такий доступ ще називають "дружнім". Всі інші класи цього пакета мають доступ до таких елементів як до відкритих. Ззовні пакету такі елементи взагалі недоступні.
Доступ до захищеного (protected
) елемента класу, визначеного в деякому пакеті, обмежений методами цього класу і похідних класів будь-яких пакетів, а також класів цього пакету.
Інкапсуляція (приховування даних) – одна з трьох парадигм об'єктно-орієнтованого програмування. Зміст інкапсуляції полягає у приховуванні від зовнішнього користувача деталей реалізації об'єкту. Зокрема доступ до даних (полів), які зазвичай описані з модифікатором private
, здійснюється через відкриті функції доступу. Як правило, це так звані сеттери та геттери. Якщо поле має ім'я name
, відповідні функції доступу мають імена setName
та getName
.
Автоматична генерація геттерів і сеттерів здійснюється за допомогою функції Source | Generate Getters and Setters... головного меню Eclipse.
2.3.3 Конструктори
Екземпляр класу створюється шляхом застосування операції new
до конструктора.
Конструктор – це функція, яка здійснює ініціалізацію даних об'єкта. Ім'я конструктора збігається з ім'ям класу. Не можна вказувати типу результату конструктора. У класі може бути визначено кілька конструкторів. Якщо жоден конструктор явно не визначений, автоматично створюється усталений конструктор (без параметрів). Такий конструктор ініціалізує всі поля усталеними початковими значеннями. Після визначення принаймні одного конструктора усталений конструктор автоматично не створюється.
Один конструктор можна викликати з іншого з використанням слова this
, після якого випливають необхідні аргументи. Спочатку здійснюється ініціалізація в місці опису, після якої значення можуть бути перевизначені в блоці ініціалізації, а потім перевизначені в конструкторі
public class Rectangle { private double width = 10; private double height = 20; { width = 30; height = 40; } public Rectangle(double width, double height) { this.width = width; this.height = height; } public Rectangle() { this(50, 60); // виклик іншого конструктора } } . . . Rectangle rectangle = new Rectangle(); // width = 50, height = 60
У Java немає конструкторів копіювання і деструкторів. Можна створити спеціальний метод finalize()
, який викликається збирачем сміття перед ліквідацією об'єкта. У деяких випадках об'єкт може бути не вилучений збирачем сміття ніколи (пам'яті вистачало до кінця програми), отже метод finalize()
може бути ніколи не викликаний.
2.3.4 Статичні елементи. Константи
Методи і поля можуть бути оголошені з ключовим словом static
. Звернення до таких полів і методів може здійснюватися без створення екземпляра класу. Статичні поля є альтернативою відсутнім у Java глобальним змінним. На відміну від С++, статичні елементи даних не потрібно окремо визначати в глобальній області видимості. Статичні поля можуть бути проініціалізовані під час створення:
class SomeClass { static double x = 10; static int i = 20; }
Можна створити окремий блок статичної ініціалізації:
class SomeClass { static double x; static int i; static { x = 10; i = 20; } }
На відміну від нестатичної ініціалізації, створення й ініціалізація статичних полів здійснюється під час першого звернення до класу (створенні екземпляра класу чи зверненні до статичних елементів). Java не створює статичних полів для класів, які не використовуються.
Статичні методи не отримують посилання на об'єкт і не можуть використовувати посилання this
.
Звернення до статичних елементів може здійснюватися як через ім'я класу, так і через посилання на об'єкт:
SomeClass.x = 30; SomeClass s = new SomeClass(); s.x = 40;
Всередині класів можна визначати константи. Константи можуть бути двох видів – статичні й нестатичні. Статичну константу створює компілятор. За угодою її ім'я має містити лише великими літерами:
public static final double PI = 3.14159265;
Значення нестатичної константи слід визначити, причому один раз – у місці визначення, в блоці ініціалізації (тоді це значення буде однаковим для всіх екземплярів), або в конструкторі:
public class ConstDemo { public final int one = 1; public final int two; { two = 2; } public final int other; public ConstDemo(int other) { this.other = other; } }
Цілком безпечно визначати константи як public
, оскільки компілятор не дозволить змінити їх значення.
2.4 Використання стандартних класів
Раніше вже використовувалися статичні засоби стандартних класів System
(поля-потоки in
і out
), Math
(стандартні математичні функції, реалізовані у вигляді статичних методів) і Arrays
. Крім того, створювався об'єкт класу java.util.Scanner
з викликом його методів.
Для тестування програмного забезпечення, статистичного моделювання, в задачах криптографії тощо використовують випадкові та псевдовипадкові значення, які можна отримати за допомогою класу java.util.Random
.
Практично жодна програма на Java не може обійтися без об'єктів класу String
: функція main()
описується з параметром типу масиву рядків, дані читаються з потоків у рядки і записуються з рядків у потоки, рядки використовуються для представлення даних у візуальних компонентах графічного інтерфейсу користувача тощо. Для модифікації вмісту рядків використовуються стандартні класи StringBuffer
і StringBuilder
. Для поділу рядку на лексеми використовують клас StringTokenizer
.
Класи-обгортки Integer
, Double
, Boolean
, Character
, Float
, Byte
, Short
і Long
використовують для зберігання даних примітивних типів у об'єктах, з якими можна працювати, як з посиланнями. Крім того, ці класи надають ряд корисних методів для перетворення даних.
2.5 Робота з випадковими величинами
Іноді під час тестування виникає необхідність у заповненні масивів випадковими (псевдовипадковими) значеннями. Це можна здійснити за допомогою функції random()
класу Math
і за допомогою спеціального класу java.util.Random
. Перший варіант дає випадкове число у діапазоні від 0 до 1. Наприклад:
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class MathRandomTest { public static void main(String[] args) { double[] a = new double[5]; for (int i = 0; i < a.length; i++) { a[i] = Math.random() * 10; // випадкові значення від 0 до 10 } System.out.println(Arrays.toString(a)); } }
Використання класу Random
дозволяє отримати більш різноманітні результати. Конструктор класу Random
без параметрів ініціалізує давач псевдовипадкових чисел так, що послідовності випадкових значень практично не повторюються. Якщо для зневадження нам необхідно кожного разу отримувати однакові випадкові значення, слід скористатися конструктором з цілим параметром. Параметром може бути будь-яке ціле, яке ініціалізує генератор випадкових чисел.
У наведеній нижче таблиці представлені функції класу java.util.Random
, що дозволяють отримати різні псевдовипадкові значення.
Функція | Опис |
---|---|
nextBoolean() |
повертає наступне рівномірно розподілене значення типу boolean |
nextDouble() |
повертає наступне значення типу double , рівномірно розподілене на інтервалі від 0 до 1 |
nextFloat() |
повертає наступне значення типу
float , рівномірно розподілене на інтервалі від 0 до 1 |
nextInt() |
повертає наступне рівномірно розподілене значення типу int |
nextInt(int n) |
повертає наступне значення типу
int , рівномірно розподілене від 0 до n (не включаючи n ) |
nextLong() |
повертає наступне рівномірно розподілене значення типу long |
nextBytes(byte[] bytes) |
заповнює масив цілих типу byte випадковими значеннями |
nextGaussian() |
повертає наступне значення типу double , розподілене на інтервалі від 0 до 1 за нормальним законом |
У наведеному нижче прикладі ми отримуємо псевдовипадкові цілі значення в діапазоні від 0 (включно) до 10:
package ua.inf.iwanoff.labs.fifth; import java.util.*; public class UtilRandomTest { public static void main(String[] args) { int[] a = new int[15]; Random rand = new Random(100); for (int i = 0; i < a.length; i++) { a[i] = rand.nextInt(10); // випадкові значення від 0 до 10 } System.out.println(Arrays.toString(a)); } }
Після кожного запуску програми ми будемо отримувати однакову послідовність псевдовипадкових значень. Якщо ми хочемо, щоб значення були дійсно випадковими, треба скористатися конструктором Random
() без параметрів.
2.6 Рядки
2.6.1 Використання класу String
Рядки в Java – це екземпляри класу java.lang.String
. Об'єкти цього класу містять символи Unicode. Об'єкт-рядок може бути створений під час опису посилання шляхом присвоювання йому рядкового літералу:
String s = "Перший рядок";
Рядок можна також створити за допомогою різних конструкторів. Клас String
у Java надає 15 конструкторів, які дозволяють визначити початкове значення рядка. Наприклад, можна отримати рядок з масиву символів:
char[] chars = { 'Т', 'е', 'к', 'с', 'т'}; String s1 = new String(chars); // Текст
Можна створити масив байтів і отримати з нього рядок. Перетворення байтів у символи здійснюється згідно з усталеною таблицею кодування, прийнятою в системі.
byte[] bytes = { 49, 50, 51, 52 }; String s2 = new String(bytes); System.out.println(s2); // 1234
Можна створити рядок з іншого рядка. Слід відрізняти створення нового посилання від створення нового рядка:
String s = "текст"; String s1 = s; // s и s1 посилаються на один рядок String s2 = new String(s); // s2 посилається на новий рядок - копію s
Якщо один з операндів – рядок, а інший – ні, то цей операнд подається у вигляді рядка. Альтернативний спосіб перетворення числових даних у рядки – застосування статичних функцій valueOf()
. Відповідні функції реалізовані для аргументів числових типів, а також типів Object
, char
, boolean
і масивів символів. Наприклад:
double d = 1.1; String sd = String.valueOf(d); // "1.1"
Нижче наведені методи класу String
, які використовують найбільш часто.
Метод | Аргументи | Повертає | Опис |
---|---|---|---|
length |
() |
int |
Повертає кількість символів у рядку. |
concat |
(String str) |
String |
Додає вказаний рядок у кінець поточного рядка |
charAt |
(int index) |
char |
Повертає символ, що відповідає певному індексу |
compareTo |
(String value) |
int |
Порівнює поточний рядок з аргументом-рядком. Результат від'ємний, якщо поточний рядок лексикографічно передує рядку-аргументу. Результат дорівнює 0, якщо рядки збігаються і додатний у протилежному випадку |
compareToIgnoreCase |
(String str) |
int |
Працює як compareTo() , але ігнорує регістр |
equals | (String value) |
boolean |
Порівнює поточний рядок з аргументом-рядком. Повертає true , якщо рядки збігаються |
equalsIgnoreCase |
(String str) |
boolean |
Порівнює поточний рядок з аргументом-рядком з ігноруванням регістрів. Повертає true , якщо рядки збігаються |
indexOf |
(String substring) |
int |
Повертає індекс, який відповідає першому входженню підрядка у рядок. Якщо підрядок не входить у рядок – повертає –1 |
indexOf |
(char ch) |
int |
Повертає індекс, який відповідає першому входженню символу в рядок. Якщо символ відсутній – повертає –1 |
lastIndexOf |
(String substring) |
int |
Повертає індекс, який відповідає останньому входженню підрядка у рядок. Якщо підрядок не входить у рядок – повертає –1 |
lastIndexOf |
(char ch) |
int |
Повертає індекс, який відповідає останньому входженню символу в рядок. Якщо символ відсутній – повертає –1 |
substring |
(int beginindex, int endindex) |
String |
Повертає новий рядок, що є підрядком вихідного рядку |
toLowerCase |
() |
String |
Повертає рядок, усі символи якого переведені в нижній регістр |
toUpperCase |
() |
String |
Повертає рядок, усі символи якого переведені у верхній регістр |
regionMatches |
(int toffset, String other, int ooffset, int len) |
boolean |
Перевіряє, чи збігаються дві послідовності символів у двох рядках |
regionMatches |
(boolean ignoreCase, int toffset, String other, int ooffset, int len) |
boolean |
Перевіряє, чи збігаються дві послідовності символів у двох рядках. Додатково можна встановлювати можливість ігнорування регістра під час перевірки |
toCharArray |
() |
char[] |
Перетворює поточний рядок у новий масив символів |
getChars |
(int srcBegin, int srcEnd, char[] dst, int dstBegin) |
void |
Копіює символи поточного рядка у результуючий масив символів |
getBytes |
() |
byte[] |
Повертає масив байтів, що містять коди символів з урахуванням таблиці кодів платформи (операційної системи) |
trim |
() |
String |
Повертає копію рядка, у якій початкові і кінцеві пропуски опущені |
startsWith |
(String prefix) |
boolean |
Перевіряє, чи починається рядок із зазначеного префікса |
endsWith |
(String suffix) |
boolean |
Перевіряє, чи закінчується рядок зазначеним суфіксом |
Наведені нижче приклади демонструють використання методів обробки рядків.
String s1 = new String("Hello World."); int i = s1.length(); // i = 12 char c = s1.charAt(6); // c = 'W' i = s1.indexOf('e'); // i = 1 (індекс 'e' у "Hello World.") String s2 = "abcdef".substring(2, 5); // s2 = "cde" int k = "AA".compareTo("AB"); // k = -1 s2 = "abc".toUpperCase(); // s2 = ABC
Одна з найбільш типових операцій з рядками – зшивання. Для зшивання двох рядків можна застосувати функцію concat()
:
String s3 = s1.concat(s2); s3 = s3.concat("додаємо текст");
Але найчастіше замість виклику функції concat()
застосовують операцію +
:
String s1 = "first"; String s2 = s1 + " and second";
Якщо один з операндів – рядок, а інший – ні, то цей операнд приводиться до рядкового представлення.
int n = 1; String sn = "n дорівнює" + n; // "n дорівнює 1" double d = 1.1; String sd = d + ""; // "1.1"
Можна також використовувати операцію "+=
" для дошивання в кінець рядка.
Можна створювати масиви рядків. Як і для інших типів-посилань, масив зберігає не рядки безпосередньо, а посилання на них. Функція sort()
класу java.util.Arrays
реалізована також для рядків. Рядки впорядковуються за алфавітом:
String[] a = { "dd", "ab", "aaa", "aa" }; java.util.Arrays.sort(a); // aa aaa ab dd
Для читання рядка з потоку з використанням класу java.util.Scanner
рядок можна отримати за допомогою методу next()
(до роздільник) або nextLine()
(до кінця рядка).
Екземпляр класу String
не може бути змінений після створення. Робота деяких методів та операцій зовні нагадує модифікацію об'єкту, однак насправді створюється новий рядок.
String s = "ab"; // У пам'яті один рядок s = s += "c"; // У пам'яті три рядки: "ab", "c" та "abc". На "abc" посилається s // Зайві рядки потім будуть видалені збирачем сміття
2.6.2 Використання класів StringBuffer и StringBuilder
Існує спеціальний клас StringBuffer
, що дозволяє модифікувати вміст рядкового об'єкта. Починаючи з Java 5, замість StringBuffer
можна використовувати StringBuilder
. У програмах, які не створюють окремих потоків виконання, функції цього класу виконуються більш ефективно.
Створити об'єкт типу StringBuilder
можна з існуючого рядка. Після модифікації можна створити новий об'єкт класу String
, використовуючи об'єкт класу StringBuilder
. Наприклад:
String s = "abc"; StringBuilder sb1 = new StringBuilder(s); // Виклик конструктора StringBuilder sb2 = new StringBuilder("cd"); // Виклик конструктора // модифікація sb1 та sb2 // ... String s1 = new String(sb1); // Виклик конструктора String s2 = sb2 + ""; // Перетворення типів
Окрім деяких типових для класу String
функцій, таких як length()
, charAt()
, indexOf()
, substring()
, клас StringBuilder
надає низку методів для модифікації вмісту. Це такі методи, як append()
, delete()
, deleteCharAt()
, insert()
, replace()
, reverse()
, та setCharAt()
. Розглянемо використання цих функцій на наведеному нижче прикладі:
public class StringBuilderTest { public static void main(String[] args) { String s = "abc"; StringBuilder sb = new StringBuilder(s); sb.append("d"); // abcd sb.setCharAt(0, 'f'); // fbcd sb.delete(1, 3); // fd sb.insert(1, "gh"); // fghd sb.replace(2, 3, "mn"); // fgmnd sb.reverse(); // dnmgf System.out.println(sb); } }
Використання StringBuilder
може підвищити ефективність роботи програми у випадках, коли певний рядок зазнає багаторазових модифікацій протягом роботи програми. Але важливо пам'ятати, що декілька посилань вказують на один об'єкт типу StringBuilder
. Тому, коли ми його змінюємо, усі посилання вказуватимуть на змінений рядок.
2.6.3 Поділ рядка на лексеми
Існує кілька способів поділу рядка на лексеми. Найпростіший спосіб – використання класу java.util.StringTokenizer
. Об'єкт цього класу створюється за допомогою конструктора з параметром типу String
, який визначає рядок, що підлягає поділу на лексеми:
StringTokenizer st = new StringTokenizer(someString);
Після створення об'єкта можна отримати загальну кількість лексем за допомогою методу countTokens()
. Клас реалізує внутрішній "поточний вказівник", який вказує на наступне слово. Функція nextToken()
повертає наступну лексему з рядка. Функції nextToken()
можна задати альтернативний роздільник лексем як параметр. За допомогою функції hasMoreTokens()
можна перевірити, чи є ще лексеми. У наведеному нижче прикладі всі слова рядка виводяться у окремих рядках:
package ua.inf.iwanoff.labs.fifth; import java.util.*; public class AllWords { public static void main(String[] args) { String s = new Scanner(System.in).nextLine(); StringTokenizer st = new StringTokenizer(s); while (st.hasMoreTokens()) { System.out.println(st.nextToken()); } } }
Більш сучасний спосіб розбиття на лексеми – використання методу split()
класу String
. Параметр цього методу – так званий регулярний вираз, який визначає роздільники. Регулярні вирази дозволяють визначати шаблони для рядків. Наприклад, "\\s
" – це будь-який символ-роздільник. Однак у найпростішому випадку можна використовувати безпосередньо розділовий символ – пропуск. Наприклад:
String s = "aa bb ccc"; String[] a = s.split(" "); System.out.println(Arrays.toString(a)); // [aa, bb, ccc]
2.7 Класи Integer, Double, Boolean, Character, Float, Byte, Short і Long
Класи Integer
, Double
, Boolean
, Character
, Float
, Byte
, Short
і Long
дозволяють представити числові та булеві значення в об'єктах. Тому ці класи також називають класами-обгортками. Додатково ці класи надають набір методів для перетворення арифметичних значень у представлення рядком і навпаки й інші засоби для зручної роботи з цілими і дійсними числами.
Статичний метод Double.parseDouble()
повертає дійсне число за представленням у вигляді рядку:
String s = "1.2"; double d = Double.parseDouble(s);
Аналогічно працюють функції Integer.parseInt()
, Long.parseLong()
, Float.parseFloat()
, Byte.parseByte()
, Short.parseShort()
та Boolean.parseBoolean()
.
У версії Java 5 (JDK 1.5) об'єкти типу Integer
можна ініціалізувати виразами цілого типу, використовувати у виразах для одержання значень (автоматичне упакування / розпакування). Цілі значення (константи) можна заносити в списки й інші контейнери. Автоматично будуть створюватися і заноситися в контейнер об'єкти типу Integer
. У попередній версії Java (JDK 1.4) необхідно було писати:
Integer m = new Integer(10); // ініціалізуємо об'єкт типу Integer int k = m.intValue() + 1; // використовуємо значення у виразі // створюємо масив: Integer[] a = {new Integer(1), new Integer(2), new Integer(3)}; a[2] = new Integer(4); // заносимо об'єкт з новим цілим значенням // отримаємо об'єкт типу Integer з масиву і використовуємо значення: int i = a[1].intValue() + 2;
Тепер усе простіше:
Integer m = 10; // ініціалізуємо об'єкт типу Integer int k = m + 1; // використовуємо значення у виразі // створюємо масив: Integer[] a = {1, 2, 3}; a[2] = 4; // заносимо об'єкт з новим цілим значенням // отримаємо об'єкт типу Integer з масиву і використовуємо значення: int i = a[1] + 2;
Примітка: автоматичне упакування і розпакування – це операція, яка вимагає додаткових ресурсів, зокрема, неявного створення об'єктів; тому, наприклад операція m++
для змінної m
типу Integer
виконуватиметься вкрай неефективно.
Те ж саме стосується інших типів-обгорток. Фактично об'єкти цих можуть бути використані замість змінних відповідних примітивних типів. Недоліком використання типів-обгорток є зменшення ефективності за рахунок додавання операцій розміщення у динамічній пам'яті. Але перевагою є можливість використання значення null
. Наприклад, якщо значення функції не може бути обчислене, функція може повернути null
:
package ua.inf.iwanoff.labs.fifth; import java.util.Scanner; public class Reciprocal { // Зворотна величина: static Double reciprocal(double x) { if (x == 0) { return null; } return 1 / x; } public static void main(String[] args) { Scanner s = new Scanner(System.in); double x = s.nextDouble(); Double y = reciprocal(x); if (y == null) { System.out.println("Помилка"); } else { System.out.println(y); } } }
Клас Character
– це, в першу чергу, оболонка для зберігання значення примітивного типу char
(символ) в об'єкті. Об'єкт типу Character
містить одне поле типу char
. Крім того, цей клас надає кілька методів для визначення категорії символу (малі літери, цифри і т.д.) і для перетворення символів з верхнього регістру в нижній і навпаки.
Є можливість переводити окремий символ в верхній (або нижній) регістр:
char c1 = 'a'; char c2 = Character.toUpperCase(c1); // 'A' char c3 = Character.toLowerCase(c2); // 'a'
Є функції, що дозволяють перевіряти властивості символів. Наприклад, метод Character.isLetter()
повертає true
, якщо символ є літерою в англійській, українській, російській, китайській, німецькій, арабській або іншій мові. Нижче наведені деякі найбільш корисні методи порівняння символів:
isDigit()
повертаєtrue
, якщо символ є цифроюisLetter()
повертаєtrue
, якщо символ є літероюisLetterOrDigit()
повертаєtrue
, якщо символ є літерою або цифроюisLowerCase()
повертаєtrue
, якщо символ є літерою в нижньому регістріisUpperCase()
повертаєtrue
, якщо символ є літерою у верхньому регістріisSpaceChar()
повертаєtrue
, якщо символ є роздільником – символом пробілу, нового рядка або символом табуляції.
2.8 Використання аргументів командного рядку
У Java можна організувати читання аргументів з командного рядку (окремі слова, набрані в командному рядку після імені головного класу). Наприклад, наведена нижче програма виводить перший аргумент командного рядку на екран:
public static void main(String[] args) { System.out.println(args[0]); }
Аргументи командного рядку у програмі представлені у вигляді масиву рядків. Для того, щоб отримати числове значення, яке представлено рядком, використовують функції класів Integer
та Double
. У наведеному нижче прикладі програма здійснює читання з командного рядку цілого та дійсного значень та знаходить їхню суму.
public class TestArgs { public static void main(String[] args) { int n = Integer.parseInt(args[0]); double x = Double.parseDouble(args[1]); double y = n + x; System.out.println(y); } }
Кількість аргументів, які були уведені і командному рядку, можна отримати за допомогою виразу args.length
.
Для визначення аргументів командного рядку в середовищі Eclipse використовується функція Run | Run Configurations..., далі на закладці Arguments аргументи набираються у вікні Program arguments. Якщо аргумент – це рядок, який містить кілька слів, його необхідно брати в лапки.
3 Приклади програм
3.1 Сума елементів масиву
Наведена нижче програма знаходить суму елементів масиву дійсних чисел.
package ua.inf.iwanoff.labs.fifth; public class SumOfElements { public static void main(String[] args) { double[] a = {1, 2, 1, 2.5, 1}; double sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } System.out.println("Sum is " + sum); } }
Другий варіант тієї ж програми:
package ua.inf.iwanoff.labs.fifth; public class SumOfElements { public static void main(String[] args) { double a[] = {1, 2, 1, 2.5, 1}; double sum = 0; for (double x : a) { sum += x; } System.out.println("Sum is " + sum); } }
Третій варіант можна реалізувати за допомогою рекурсії:
package ua.inf.iwanoff.labs.fifth; public class SumWithRecursion { static double sum(double[] a, int n) { if (n <= 0) { return 0; } n--; return a[n] + sum(a, n); } static double sum(double[] a) { return sum(a, a.length); } public static void main(String[] args) { double[] a = { 1, 2, 1, 2.5, 1 }; System.out.println("Sum is " + sum(a)); } }
3.2 Індекс максимального елемента
Наведена нижче програма знаходить номер максимального елемента в масиві цілих чисел.
package ua.inf.iwanoff.labs.fifth; public class MaxElement { public static void main(String[] args) { int[] a = {1, 2, 14, 8}; int indexOfMax = 0; for (int i = 1; i < a.length; i++) { if (a[i] > a[indexOfMax]) { indexOfMax = i; } } System.out.println(indexOfMax + " " + a[indexOfMax]); } }
3.3 Сума добутків рядків
У наведеній нижче програмі обчислюється сума добутків рядків двовимірного масиву.
package ua.inf.iwanoff.labs.fifth; public class SumOfProducts { public static void main(String[] args) { int[][] a = {{1, 2, 3}, {2, 3, 4}, {0, 1, 2}, {1, 1, 12}}; int sum = 0; for (int i = 0; i < a.length; i++) { int product = 1; for (int j = 0; j < a[i].length; j++) { product *= a[i][j]; } sum += product; } System.out.println(sum); } }
3.4 Заміна елементів
У наведеній нижче програмі від'ємні елементи двовимірного масиву заміняються нулями.
package ua.inf.iwanoff.labs.fifth; public class ReplaceZeros { public static void main(String[] args) { double[][] a = {{1, -2, 3}, {2.1, 3, -4}, {0,-0.5, 11}}; for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { if (a[i][j] < 0) { a[i][j] = 0; } } } for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { System.out.print("\t" + a[i][j]); } System.out.println(); } } }
Примітка: для виведення двовимірного масиву не слід використовувати Arrays.toString()
, оскільки замість елементів ми отримаємо адреси рядків. Для виведення всіх елементів одним рядком слід використовувати
System.out.println(Arrays.deepToString(a));
3.5 Сортування
Припустимо, необхідно впорядкувати елементи масиву за зростанням. Наведена нижче програма реалізує алгоритм сортування методом бульбашки.
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class SortClass { public static void main(String[] args) { double[] a = { 11, 2.5, 4, 3, 5 }; boolean mustSort;// Повторюємо доти, доки mustSort true do { mustSort = false; for (int i = 0; i < a.length - 1; i++) { if (a[i] > a[i + 1]) { // Міняємо елементи місцями: double temp = a[i]; a[i] = a[i + 1]; a[i + 1] = temp; mustSort = true; } } } while (mustSort); System.out.println(Arrays.toString(a)); } }
Наведена нижче програма використовує стандартну функцію sort()
:
package ua.inf.iwanoff.labs.fifth; import java.util.Arrays; public class SortClass { public static void main(String[] args) { double[] a = { 11, 2.5, 4, 3, 5 }; Arrays.sort(a); System.out.println(Arrays.toString(a)); } }
3.6 Обчислення факторіалів
Припустимо, необхідно розробити функцію обчислення факторіалів (від 0 до 20 включно) з використанням допоміжного масиву (статичного поля). Під час першого виклику функції масив заповнюється до необхідного числа. Під час наступних викликів число або повертається з масиву, або обчислюється з використанням останнього числа, що зберігається у масиві, з подальшим заповненням масиву.
Необхідно також здійснити тестування функції для різних чисел, що вводяться у довільному порядку. Програма матиме такий вигляд:
package ua.inf.iwanoff.labs.fifth; public class Factorial { private static long[] f = new long[30]; private static int last = 0; public static long factorial(int n) { f[0] = 1; if (n > last) { for (int i = last + 1; i <= n; i++) { f[i] = i * f[i - 1]; } last = n; } return f[n]; } public static void main(String[] args) { System.out.println(factorial(5)); System.out.println(factorial(1)); System.out.println(factorial(3)); System.out.println(factorial(6)); System.out.println(factorial(20)); } }
3.7 Сума цифр
Для знаходження суми цифр цілого числа можна скористатись його представленням у вигляді рядка:
package ua.inf.iwanoff.labs.fifth; public class SumOfDigids { public static void main(String[] args) { String n = args[0]; int sum = 0; for (int i = 0; i < n.length(); i++) { sum += Integer.parseInt(n.charAt(i) + ""); } System.out.println(sum); } }
3.8 Видалення зайвих пропусків
У наведеному нижче прикладі з рядка, прочитаного з першого аргументу командного рядку, видаляються зайві пропуски (залишаємо по одному).
package ua.inf.iwanoff.labs.fifth; public class SpaceRemover { public static void main(String[] args) { System.out.println(args[0]); String s = args[0]; while (s.indexOf(" ") >= 0) { s = s.replaceAll(" ", " "); } System.out.println(s); } }
Перед виконанням програми необхідне значення слід встановити в налаштуваннях конфіґурації часу виконання на закладці Arguments у вікні Program arguments. Якщо рядок містить кілька слів, його необхідно брати в лапки. Якщо як аргумент заданий, наприклад, рядок
"To be or not to be"
отримаємо рядок
To be or not to be
3.9 Точка на площині
Припустимо, необхідно реалізувати програму для роботи з точками на площині. Точка задається парою дійсних чисел. Клас для представлення точки повинен реалізовувати такі методи:
- обчислення відстані від точки до початку координат;
- обчислення відстані між двома точками.
Друга функція може бути реалізована як статичний метод. Програма може бути такою:
package ua.inf.iwanoff.labs.fifth; public class Point { private double x, y; public Point(double x, double y) { this.x = x; this.y = y; } public double distance() { return Math.sqrt(x * x + y * y); } public static double distance(Point p1, Point p2) { return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } public static void main(String[] args) { Point p1 = new Point(3, 4); System.out.println(p1.distance()); Point p2 = new Point(4, 5); System.out.println(distance(p1, p2)); } }
3.10 Лінійне рівняння
Припустимо, необхідно спроектувати клас, що представляє лінійне рівняння. Поля цього класу -
коефіцієнти a і b, а також корінь x, який необхідно знайти. У середовищі програмування Eclipse створюємо клас LinearEquation
, який містить функцію main()
. Після додавання необхідних полів генеруємо геттери и сеттери. Геттери необхідні для всіх полів, а сеттери -
для полів a
і b
. Далі до класу додаємо функцію solve()
, яка повертає false
або true
залежно від того, чи можна розв'язати рівняння. Тестування здійснюється у функції main()
. Отримаємо таку програму:
package ua.inf.iwanoff.labs.fifth; public class LinearEquation { private double a, b, x; public double getA() { return a; } public void setA(double a) { this.a = a; } public double getB() { return b; } public void setB(double b) { this.b = b; } public double getX() { return x; } public boolean solve() { if (a == 0) { return false; } x = - b / a; return true; } public static void main(String[] args) { LinearEquation e = new LinearEquation(); e.setA(1); e.setB(-2); if (e.solve()) { System.out.println("x = " + e.getX()); } else { System.out.println("Немає розв'язків!"); } e.setA(0); e.setB(4); if (e.solve()) { System.out.println("x = " + e.getX()); } else { System.out.println("Немає розв'язків!"); } } }
3.11 Класи "Країна" та "Перепис населення"
Припустимо, необхідно спроектувати програму, в якій описуються класи для представлення країни та перепису населення. Дані про країну -
назва, територія, а також масив посилань на об'єкт типу "Перепис населення". В свою чергу, клас, який представляє перепис населення, повинен включати дані про рік перепису, кількість населення та коментарі. Необхідно реалізувати конструктори, методи доступу, а також функції обчислення щільності населення згідно з певним переписом, визначення перепису з найбільшою кількістю населення та перевірки входження певного слова в коментарі.
Оскільки в класі для представлення країни повинен міститись масив посилань на переписи населення, доцільно почати з класу Census
(перепис населення). Створюємо новий проект, наприклад з назвою Countries
. Додаємо новий клас Census
у пакеті labs.fifth
. Далі додаємо до його опису поля year
, population
(цілі) та comments
(рядок):
package ua.inf.iwanoff.labs.fifth; public class Census { private int year; private int population; private String comments; public static void main(String[] args) { } }
Тепер можна згенерувати конструктор. Це здійснюється за допомогою функції контекстного меню (Source | Generate Constructor using Fields...). Згенерований конструктор матиме такий вигляд:
public Census(int year, int population, String comments) { super(); this.year = year; this.population = population; this.comments = comments; }
У тілі конструктора super
()
– це виклик конструктора базового класу. Також автоматично можна згенерувати методи доступу (Source | Generate Getters and Setters...). Вони матимуть вигляд:
public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } public int getPopulation() { return population; } public void setPopulation(int population) { this.population = population; } public int getYear() { return year; } public void setYear(int year) { this.year = year; }
Функція обчислення входження певного слова в коментар додається вручну. Залежно від того, який пошук треба здійснювати – пошук цілих слів, або послідовності літер, можна навести дві реалізації функції пошуку. Слід звернути увагу, що перевірку входження слова або літер у рядок треба здійснювати з ігноруванням регістру:
public boolean containsWord(String word) { StringTokenizer st = new StringTokenizer(comments); String s; while (st.hasMoreTokens()) { s = st.nextToken(); if (s.equalsIgnoreCase(word))) { return true; } } return false; } public boolean containsSubstring(String substring) { return comments.toUpperCase().indexOf(substring.toUpperCase()) >= 0; }
Функція containsWord()
реалізує такий алгоритм:
- рядок коментарів розбивається на лексеми (підрядки, відокремлені пробілами);
- кожна лексема переводиться у верхній регістр та перевіряється, чи збігається вона із вказаним словом;
- якщо знайдена перша збіжність, функція повертає
true
; - якщо жодна збіжність не знайдена, функція повертає
false
.
Для реалізації цього алгоритму застосовуємо клас StringTokenizer
, у конструкторі якого вказуємо рядок, який необхідно розділити на лексеми. Метод nextToken()
дозволяє отримати наступну лексему. Метод hasMoreTokens()
повертає true
, якщо є необроблені лексеми. Для того, щоб скористатись класом , необхідно додати твердження імпорту:
import java.util.StringTokenizer;
Для тестування методів containsWord()
та containsSubstring()
створюємо окрему функцію testWord()
. Ця функція придасться тільки для тестування класу Census
, тому її доцільно описати як private
:
private void testWord(String word) { if (containsWord(word)) { System.out.println("Слово \"" + word + "\" міститься у коментарі"); } else { System.out.println("Слово \"" + word + "\" не міститься у коментарі"); } if (containsSubstring(word)) { System.out.println("Текст \"" + word + "\" міститься у коментарі"); } else { System.out.println("Текст \"" + word + "\" не міститься у коментарі"); } }
У функції main()
створюємо об'єкт та здійснюємо тестування створених функцій. Наведемо весь вихідний текст файлу Census.java:
package ua.inf.iwanoff.labs.fifth; import java.util.StringTokenizer; public class Census { private int year; private int population; private String comments; public Census(int year, int population, String comments) { this.year = year; this.population = population; this.comments = comments; } public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } public int getPopulation() { return population; } public void setPopulation(int population) { this.population = population; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public boolean containsWord(String word) { StringTokenizer st = new StringTokenizer(comments); String s; while (st.hasMoreTokens()) { s = st.nextToken(); if (s.equalsIgnoreCase(word))) { return true; } } return false; } public boolean containsSubstring(String substring) { return comments.toUpperCase().indexOf(substring.toUpperCase()) >= 0; } private void testWord(String word) { if (containsWord(word)) { System.out.println("Слово \"" + word + "\" міститься у коментарі"); } else { System.out.println("Слово \"" + word + "\" не міститься у коментарі"); } if (containsSubstring(word)) { System.out.println("Текст \"" + word + "\" міститься у коментарі"); } else { System.out.println("Текст \"" + word + "\" не міститься у коментарі"); } } public static void main(String[] args) { Census census = new Census(2001, 48475100, "Перший перепис у незалежній Україні"); census.testWord("Україні"); census.testWord("Країні"); census.testWord("Україна"); } }
Після запуску програми у консольному вікні з'являться такі результати:
Слово "Україні" міститься у коментарі Текст "Україні" міститься у коментарі Слово "Країні" не міститься у коментарі Текст "Країні" міститься у коментарі Слово "Україна" не міститься у коментарі Текст "Україна" не міститься у коментарі
Тепер можна створювати клас Country
. Цей клас доцільно розмістити у тому ж пакеті. Далі додаємо до його опису поля name
(рядок), area
(дійсне), censuses
(масив посилань на Census
):
package ua.inf.iwanoff.labs.fifth; public class Country { private String name; private double area; private Census[] censuses; public static void main(String[] args) { } }
Тепер можна згенерувати конструктор. Згенерований конструктор матиме такий вигляд:
public Country(String name, double area, Census[] censuses) { this.name = name; this.area = area; this.censuses = censuses; }
Також автоматично можна згенерувати методи доступу. Вони матимуть вигляд:
public double getArea() { return area; } public void setArea(double area) { this.area = area; } public Census[] getCensuses() { return censuses; } public void setCensuses(Census[] censuses) { this.censuses = censuses; } public String getName() { return name; } public void setName(String name) { this.name = name; }
Функція density()
дозволяє обчислити щільність населення для певного перепису. Перепис знаходиться за роком. Функція maxYear()
повертає рік перепису, коли була зафіксована найбільша кількість населення, функція findWord()
виводить переписи з певними назвами у коментарях:
public double density(int year) { for (int i = 0; i < censuses.length; i++) { if (year == censuses[i].getYear()) { return censuses[i].getPopulation() / area; } } return 0; } public int maxYear() { Census census = censuses[0]; for (int i = 1; i < censuses.length; i++) { if (census.getPopulation() < censuses[i].getPopulation()) { census = censuses[i]; } } return census.getYear(); } public void findWord(String word) { System.out.println("Слово \"" + word + "\":"); for (Census census : censuses) { if (census.containsWord(word)) { System.out.println("Перепис " + census.getYear() + " року. Коментар:" + census.getComments()); } } }
У функції main()
здійснюємо створення масиву переписів, створення тестування реалізованих методів. Наведемо весь вихідний текст файлу Country.java:
package ua.inf.iwanoff.labs.fifth; public class Country { private String name; private double area; private Census[] censuses; public Country(String name, double area, Census[] censuses) { this.name = name; this.area = area; this.censuses = censuses; } public double getArea() { return area; } public void setArea(double area) { this.area = area; } public Census[] getCensuses() { return censuses; } public void setCensuses(Census[] censuses) { this.censuses = censuses; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double density(int year) { for (int i = 0; i < censuses.length; i++) { if (year == censuses[i].getYear()) { return censuses[i].getPopulation() / area; } } return 0; } public int maxYear() { Census census = censuses[0]; for (int i = 1; i < censuses.length; i++) { if (census.getPopulation() < censuses[i].getPopulation()) { census = censuses[i]; } } return census.getYear(); } public void findWord(String word) { System.out.println("Слово \"" + word + "\":"); for (Census census : censuses) { if (census.containsWord(word)) { System.out.println("Перепис " + census.getYear() + " року. Коментар:" + census.getComments()); } } } public static void main(String[] args) { Census[] censuses = { new Census(1959, 41869000, "Перший післявоєнний перепис"), new Census(1970, 47126500, "Нас побільшало"), new Census(1979, 49754600, "Просто перепис"), new Census(1989, 51706700, "Останній радянський перепис"), new Census(2001, 48475100, "Перший перепис у незалежній Україні") }; Country country = new Country("Україна", 603628, censuses); System.out.println("Щільність населення у 1979 році: " + country.density(1979)); System.out.println("Рік з найбільшим населенням: " + country.maxYear()); country.findWord("перепис"); } }
4 Вправи для контролю
- Проініціалізувати одновимірний масив дійсних чисел списком початкових значень. Знайти та вивести суму додатних елементів.
- Проініціалізувати одновимірний масив дійсних чисел списком початкових значень. Знайти та вивести суму квадратів елементів.
- Проініціалізувати одновимірний масив цілих чисел списком початкових значень. Знайти та вивести суму парних елементів.
- Увести з клавіатури кількість елементів та елементи одновимірного масиву цілих чисел. Вивести на екран добуток парних елементів.
- Проініціалізувати одновимірний масив дійсних чисел списком початкових значень. Знайти суму максимального і мінімального елементів.
- Увести з клавіатури кількість елементів та елементи одновимірного масиву цілих чисел. Знайти добуток максимального і мінімального елементів.
- Проініціалізувати одновимірний масив дійсних чисел списком початкових значень. Замінити додатні елементи нулями та вивести масив.
- Проініціалізувати одновимірний масив цілих чисел списком початкових значень. Замінити від'ємні елементи нулями та вивести масив.
- Проініціалізувати двовимірний масив цілих чисел списком початкових значень, знайти добуток ненульових (таких що не дорівнюють нулю) елементів і вивести на екран.
- Проініціалізувати одновимірний масив дійсних чисел списком початкових значень, змінити порядок проходження елементів на протилежний і вивести елементи масиву на екран.
- Увести з клавіатури кількість елементів та елементи одновимірного масиву цілих чисел. Відсортувати масив за збільшенням елементів.
- Увести з клавіатури рядок та підрядок, перевірити, чи входить підрядок у рядок та вивести
false
абоtrue
. - Увести з клавіатури рядок (
String
), перевести усі символи у верхній регістр та вивести рядок на екран. - Увести з клавіатури рядок (
String
), змінити порядок символів на зворотний та вивести рядок на екран. - Увести рядок, замінити літеру "я" на "ja", вивести рядок на екран.
- Увести рядок, видалити літеру "а", вивести рядок на екран.
- Проініціалізувати одновимірний масив рядків. Відсортувати масив за алфавітом.
- Створити клас з конструктором для опису точки в тривимірному просторі.
- Створити клас з конструктором для опису товару (зберігаються назва та ціна).
- Створити клас з конструктором для опису користувача (зберігаються ім'я та пароль).
5 Контрольні запитання
- Чим відрізняється посилання Java від указівника C++?
- Чим типи-посилання відрізняються від типів-значень?
- Як у Java здійснюється розіменування?
- Що є результатом присвоєння одного посилання іншому?
- Чи завжди змінним типів-посилань можна присвоювати
null
? - Як у Java видалити об'єкт, який було створено за допомогою
new
? - У чому полягає "збирання сміття"?
- Чи можна використовувати змінні для визначення довжини масиву?
- Чи можна змінити розміри масиву за допомогою поля
length
? - Як додати новий елемент у кінець масиву?
- Як визначити кількість стовпців двовимірного масиву?
- Чи можна створити двовимірний масив з різною довжиною рядків?
- Чим відрізняється застосування двох різних конструкцій
for
для обходу елементів масиву? - Чим визначається розмір масиву, який створюється функцією
arraycopy()
? - Чи можна за допомогою
arraycopy()
скопіювати частину масиву? - Як здійснити читання масиву з клавіатури?
- У які способи можна заповнити елементи масиву без циклу?
- Як без циклу встановити, що елементи масивів збігаються?
- Чи можна без циклу відсортувати частину масиву?
- Чи дозволяє функція
binarySearch()
здійснити пошук у невідсортованому масиві? - Чи можна змінити значення елементів масиву за допомогою функції?
- Як у Java створити функцію зі змінною кількістю аргументів?
- З яких основних елементів складається опис класу?
- Чи завжди необхідно явно ініціалізувати поля класу?
- Чи можна в Java поза класом реалізовувати методи, оголошені всередині класу?
- Чим відрізняються статичні та нестатичні елементи класу?
- Як здійснюється ініціалізація статичних даних?
- Де може бути розташована конструкція ініціалізації?
- Як у Java визначається дружній доступ до елементів класу?
- Як встановити рівень доступу для групи елементів?
- У чому полягає зміст інкапсуляції та як вона реалізована в Java?
- Як можна використовувати посилання
this
? - Як викликати конструктори з інших конструкторів?
- Скільки конструкторів без параметрів може бути створено в одному класі?
- Як створити клас, у якому немає жодного конструктора?
- Чому в Java немає деструкторів?
- Коли викликається метод
finalize()
? - Чи можна змінити вміст раніше створеного рядка?
- Як змінити конкретний символ у рядку?
- У чому є недоліки й переваги класу
StringBuilder
у порівнянні з класомString
? - Як перевести число в його рядкове представлення і навпаки?
- Які є недоліки й переваги застосування об'єктів класу
Integer
замість змінних типуint
?