Академія Сучасного Програмування
З збільшенням розмірів програми і числа програмістів, що працюють над нею, збільшується і кількість помилок, і час, що витрачається на пошук кожної. Розглянемо, наприклад, клас цілих чисел Int:
Зауважимо, що програміст, який використовує цей клас, може без перешкод змінити значення поля myValue . У випадку з класом цілих чисел це, швидше за все, не призведе до збою в роботі програми, але якщо ми мали б справу з більш складним класом, поля якого були б нетривіально взаємопов'язані один з одним, це могло б повністю зруйнувати структуру класу і призвести до дуже сумним наслідків. Хотілося б убезпечити створювані класи від подібного безцеремонного втручання, будь-які зміни значень полів об'єкта даного класу повинні проводитися тільки "зсередини", за допомогою засобів класу - методів. Для цього існують модифікатори прав доступу.
і використання поля myValue скрізь, крім методів класу, стане неможливим (компілятор видасть помилку).
Слід зазначити, що модифікатор прав доступу відноситься тільки до одного елемента класу, перед яким він поставлений, тобто в прикладі модифікатор private відноситься тільки до поля myValue , але не до поля smth . Поле smth за замовчуванням не private і не public, насправді, воно "майже public", і ми вважатимемо його public (про те, що відбувається насправді, ми поговоримо в одній з подальших лекцій).
Статичні поля
У мові Java, на відміну, наприклад, від Паскаля чи З, відсутні константи у звичному значенні. Але оскільки константи – річ необхідна, мова Java надає можливість використання констант. Ця можливість реалізується за допомогою статичних полів.
Для того, щоб задати поле класу як статичне, необхідно в його визначеннідодати слово static:
Що таке статичне поле? Статичне поле – це власність не конкретного об'єкта, а класу загалом. Це аналог глобальної змінної ( Java, як відомо, глобальних змінних немає). Статичні поля створюються в момент завантаження класу, до якого відносяться (про те, що це означає, буде розказано в одній з наступних лекцій) і доступні, навіть якщо жоден з об'єктів даного класу не був створений. При цьому вони не є константними, тобто їх можна змінювати протягом програми (як зробити поля константами буде розказано в одній з найближчих лекцій).
Статичними бувають як поля, а й методи. Статичні методи, як і статичні поля, належать усьому класу. Усередині статичних методів можна використовувати лише статичні поля.
Викликати статичний метод або звернутися до статичного поля можна від імені всього класу, наприклад:
Тепер можна остаточно пояснити рядок:
- System – class
- System.out – public static field
- System.out.println() – public method (не static)
- Float – class
- MIN_VALUE – public static field
Прикладом статичного методу може бути функція Float.isNaN(float v) , яка повертає true , якщо параметр – не число, і false інакше.
В одній з попередніх лекцій було перераховано 8 стандартних типів Java. Все інше Java - це класи. Рядки в Java – це один із класів стандартної бібліотеки.
Оголошення посилання на об'єкт класу String виглядає так:
Можливий більш короткий варіант:
Це дещо відрізняється від звичайного створення об'єкта. Але так писати також можна. Так як при написанні програм рядки трапляються досить часто, для них зробленовиняток. Наявний неявний виклик new () . Java розрахована на те, що їй користуватимуться, а не на те, щоб бути справжньою об'єктно-орієнтованою мовою.
Щоб дізнатися ще одну важливу особливість рядків Java, розглянемо наступний приклад. При виконанні коду:
отримуємо новий об'єкт класу String, який посилається t.
Крім того, якщо:
то створюється черговий новий об'єкт, і його тепер посилається s . А що сталося з рядком " Hello " , яку s посилалася раніше? Якщо на неї є інші посилання, то вона продовжує своє існування, а якщо посилань більше немає, то скоро її "з'їсть" garbage collector.
Таким чином, необхідно запам'ятати одну важливу властивість об'єктів String Java: їх не можна змінювати. За будь-якої спроби додати щось до рядка створюється новий об'єкт класу String .
String - тип посилання. Якщо s і t - довільні посилання об'єкти класу String , то:
перевіряє, чи вказують посилання s і t на той самий об'єкт. Якщо ж необхідно порівняти рядки, а не посилання на них, то слід використовувати метод equals() :
Після створення двох об'єктів:
є певна ймовірність, що s і t вказують на той самий об'єкт, однак, у ряді випадків все відбувається інакше. У програмі існує особливий пул рядків, у якому будь-який рядок є не більше одного разу. У деяких випадках приміщення рядка в пул відбувається автоматично, іноді може виявитися, що цього рядка в пулі немає. Останнє, втім, легко виправляється за допомогою методу intern() , який повертає посилання на рядок, якщо вже є в пулі, або поміщає цей рядок в пул і повертає посилання на неї.
Наприклад, не можна стверджувати, що після створення деякого рядка s == s.intern() . Все залежитьвід того, як і коли цей рядок був створений. Але після оператора:
рядок точно перебуватиме в пулі, а s, очевидно, дорівнюватиме s.intern() .
Таким чином, після виклику:
s і t гарантовано вказують на той самий об'єкт, розміщений у пулі рядків.
Помістивши рядок у пул, ми досягаємо важливої переваги: у рівних рядків проінтернованих - рівні посилання. Тепер ми маємо можливість порівнювати рядки за допомогою оператора ==. Порівняння посилань відбувається за константний час, отже таке порівняння значно вигідніше, ніж використання методу equals() , в якому порівняння рядків відбувається за лінійним від довжини рядка час. Крім того, інтернування економить пам'ять: всі однакові рядки зберігаються в тому самому об'єкті, поміщеному в пул.
З наведеного вище ясно, що рядки можна складати. Крім того, що до рядка можна додати інший рядок, до рядка можна додати практично все, що завгодно. Наприклад:
У попередніх випадках у рядок буде записано десятковий запис числа n .
Таким чином, можливий такий оператор:
Але, якщо написати:
то в результаті на екран виведеться 14 = 14 , а не 77 = 14 . Крім того, щоб уникнути помилок не допускаються присвоєння виду:
Коли в коді зустрінеться:
то неявно буде викликаний саме метод toString() , за допомогою якого об'єкт буде перетворено на рядок.
Слід зазначити, що додавання рядків у Java досить дорога операція (одне додавання, нам, звичайно, не зашкодить, а от якщо до рядка треба буде "приклеїти" багато шматочків, то на створення при кожному додаванні нового об'єкта може піти багато часу). Так, наприклад, розглянемо код:
Розглянемо i-й крок цього циклу: поточний рядок містить i-1символ. Створюється новий рядок, у який копіюються i-1 символ із поточного і додається один новий символ. Таким чином, новий рядок містить i символи:
Наш цикл на кожній ітерації вимагає створення нового рядка, копіювання до нього i-1 елемента та додавання одного нового:
В результаті, підсумовуючи кількість копіювань на кожному кроці, отримуємо
0 + 1 + 2 + 3 +. + (N - 1) = N * (N - 1) / 2
копію символів. З іншого боку, кожному кроці створюється новий об'єкт (всього N кроків). Отже, час, що витрачається
де С2 досить велике, оскільки створення об'єктів витрачається значний час.
У вирішенні цієї проблеми допомагає використання об'єкта класу StringBuffer, що помітно зменшує час, що витрачається на модифікацію (зокрема, додавання) рядків:
У разі використання буфера час буде пропорційно N, так як на кожній ітерації проводиться єдина операція - додавання елемента в буфер.
Ще два корисні методи, визначені для класу String :
Масив у мові Java – це також клас.
Завдання та ініціалізація масиву:
Під час створення масиву відбувається ініціалізація елементів (у прикладах - нульовими значеннями). При зверненні до елементів масиву виконується перевірка вихід індексу межі масиву, тому ця операція стає досить дорогої.
Одного разу задана довжина масиву не може змінитися. Її можна отримати так:
Багатомірних масивів Java немає. Можна задати масиви масивів, наприклад:
Ініціалізувати масив масивів можна так:
На перший погляд, користь другого варіанта ініціалізації не очевидна, але такий спосіб дозволяє нам задати прямокутний масив. Наприклад, створення трикутного масиву маєнаступний вид:
Конструктори
Конструктори – спеціальні методи, призначені до створення та ініціалізації об'єктів. Якщо у своєму класі програміст не створив жодного конструктора, автоматично створюється конструктор без параметрів. Свої конструктори можна створювати так:
Конструктори не повертають ніякого значення (навіть типу void), а назва конструктора має збігатися з назвою класу.
Створюватимуться об'єкти класу Int таким чином:
Можна створити кілька конструкторів, які відрізняються списком параметрів (створення однакових методів, що відрізняються списком типів аргументів називається перевантаженням). Наприклад:
Якщо програміст визначив свій конструктор і навіть хоче іноді використовувати конструктор без параметрів, він має задати його явно.
Зауважимо, що у другому конструкторі ніде не надано значення полю myValue . Річ у тім, що з створенні об'єкта класу його поля ініціалізуються значеннями за промовчанням. Для цілих типів ( byte , short , int , long ) і типів float і double це значення 0 , поля типу char ініціалізуються символом, код якого 0 , а поля логічного типу boolean - значенням false . Про те, як ініціалізуються поля – об'єкти, буде розказано в одній із наступних лекцій.