Освоюємо Java/Вступ в класи та методи
Класи
[ред.]Класи – це конструкції спеціального виду, які дозволяють об’єднати ряд змінних різних типів в одне ціле. Крім власне даних, класи зазвичай включають підпрограми (в термінології java - методи) і можуть включати блоки (сукупність інструкцій між фігурними дужками {}) та інші класи (внутрішні класи). Таким чином утворюються нові типи даних.
Роз’яснення принципів побудови і роботи з класами доволі громіздка тема, тому тут розглянемо лише спрощені основи їх роботи і створення. Детальніше дивіться розділ присвячений об'єктам і класам. Розглянути зараз необхідно через те, що Java повністю об’єктно-орієнтована мова. З класами та пов’язаними з ними методами ви зіштовхуватиметися на кожному кроці. Зокрема, навіть виведення даних System.out.println(); - здійснюється за допомогою класу System та методу println().
Для прикладу розглянемо наступну задачу: «Ви фанат збору 1 гривневих монет:). Назбирали їх чимало і хочете купити для своєї колекції невеличкий сейф та вбудувати цей сейф у стіну, щоб його з вашою колекцією не вкрали:). В інтернет-магазині вказані розміри сейфу. Постає питання, який же сейф вибрати? Оскільки сейф не один, то щоб не розраховувати вручну кожного разу об'єм сейфу, Ви захотіли написати відповідну програму.»
І так маємо:
/**
* CoinVolume.java
*/
public class CoinVolume {
public static void main(String[] args) {
double width1 = 10, height1 = 15, depth1 = 20;
double width2 = 15, height2 = 20, depth2 = 15;
double width3 = 15, height3 = 25, depth3 = 15;
int cointAmount = 15000; //кількість монет - непогано зібрав :)
double coinVolume = 2 * 2 * 0.1; //приблизний об'єм монети=2*діаметр*товщину
double safeVolume1;
double safeVolume2;
double safeVolume3;
double totalCoinVol;
totalCoinVol = cointAmount * coinVolume; // заг. об'єм, який займають монети
safeVolume1 = width1 * height1 * depth1; //об'єм першого сейфу
safeVolume2 = width2 * height2 * depth2; //об'єм другого сейфу
safeVolume3 = width3 * height3 * depth3; //об'єм третього сейфу
System.out.println("Об'єм 1-го сейфу=" + safeVolume1);
System.out.println("Об'єм 2-го сейфу=" + safeVolume2);
System.out.println("Об'єм 3-го сейфу=" + safeVolume3);
System.out.println("Загальний об'єм монет=" + totalCoinVol);
}
}
Результат виконання:
Об'єм 1-го сейфу=3000.0 Об'єм 2-го сейфу=4500.0 Об'єм 3-го сейфу=5625.0 Загальний об'єм монет=6000.0
Як бачимо з трьох сейфів не підійшов жоден, потрібен більший. Проте додавання кожного нового сейфу вимагає дописування великої кількості інструкцій. Потрібно оголошувати три змінні для довжини, ширини та глибини сейфу, також потрібна змінна для об’єму, інструкція обчислення та виведення на екран результатів. Звичайно, можна оцінювати по одному, змінюючи значення змінних (полів даних), але тоді доведеться кожного разу перекомпільовувати і запускати програму, що не дуже зручно, коли сейфів близько ста. А якщо ви захочете ще раз оцінити розміри деяких сейфів, доведеться виконувати роботу ще раз. Ось тут в пригоді і стане нам клас Safe. Зверніть увагу, що назва класу пишеться з великої літери. Рекомендовано давати назви класів з великої літери. Таким чином дуже просто розрізнити його від інших складових програми, наприклад назву класу від назви змінної, яку прийнято писати з маленької літери.
Оголосити найпростіший клас можна так:
class Safe
{
double width;
double height;
double depth;
double safeVolume;
}
Для його оголошення використовується ключове слово class та назва класу, далі іде тіло класу у фігурних дужках {}. Проте, хоч клас існує, ми не можемо так просто його використовувати. Ми повинні спочатку створити екземпляр даного класу (об’єкт). Для цього необхідно написати наступну інструкцію:
Safe mySafe1 = new Safe();
де Safe – назва класу, mySafe – назва об'єктної змінної, яка посилатиметься на наш екземпляр класу (об'єкт типу Safe), new Safe() – створює об’єкт(при цьому відбувається виділення пам’яті під об’єкт). Таким чином ми створили перший сейф. Щоб створити другий сейф використовується аналогічна інструкція:
Safe mySafe2 = new Safe(); //як бачимо змінилась лише назва змінної
Інколи класи порівнюють з формочкою для випічки печива, а саме печиво – це уже конкретний створений екземпляр (об’єкт). Форма може бути одна, а печива на її основі можна зробити безліч із різною начинкою. Так само і з класом – це шаблон, а на його основі можемо створити безліч екземплярів даного класу.
Проте як же ми можемо звернутися до полів об’єкту (змінних всередині об'єкту)? Для цього використовується операція «.»(крапка), а сама інструкція звернення записується в такій формі - назваОб’єкту.змінна:
mySafe1.width = 10;
Перепишемо нашу програму з використанням класу Safe:
class Safe {
double width = 0;
double height = 0;
double depth = 0;
double safeVolume = 0; //додамо змінну, в якій зберігатиметься об'єм сейфу
}
public class CoinVolume {
public static void main(String[] args) {
//створюємо перший сейф
Safe mySafe1 = new Safe();
//вказуємо його розміри
mySafe1.width = 10;
mySafe1.height = 15;
mySafe1.depth = 20;
//обчислюємо об'єм сейфу
mySafe1.safeVolume = mySafe1.width * mySafe1.height * mySafe1.depth;
//аналогічно для другого сейфу
Safe mySafe2 = new Safe();
mySafe2.width = 15;
mySafe2.height = 20;
mySafe2.depth = 15;
mySafe2.safeVolume = mySafe2.width * mySafe2.height * mySafe2.depth;
//виводимо на консоль об'єми сейфів
System.out.println("Об'єм 1-го сейфу=" + mySafe1.safeVolume);
System.out.println("Об'єм 2-го сейфу=" + mySafe2.safeVolume);
}
}
Щоб зосередитись на основному, з програми усунено обчислення об’єму монети. Компіляцію програми здійснюємо аналогічно до попередньої, за назвою класу в якому знаходиться метод main()— javac CoinVolume.java. Клас Safe не обов'язково може бути в одному і тому ж файлі, що і клас CoinVolume.
Отже клас ми додали, проте тепер програма виглядає складнішою ніж попередній варіант. Ситуацію виправити може використання методів.
Методи
[ред.]Методи в java – це аналог підпрограм, функцій, процедур в інших мовах програмування. За допомогою методів ми виносимо текст повторюваного коду програми окремо в тіло методу, після чого можна викликати даний метод з будь-якого місця програми, безліч разів.
Спрощене оголошення та визначення методу, який ми зараз будемо використовувати, має вигляд:
тип_повернення назва_методу(параметри){ //тіло методу; інструкція1; інструкція2; ….. інструкціяN; }
Тип_повернення – результат виконання методу, наприклад він може повертати об’єм сейфу, тоді тип_повернення буде double. Якщо метод нічого не повертає, то вказується слово ключове слово void.
Виклик методу здійснює наступна інструкція: назва_методу(параметри)
. Взагалі в термінології мов програмування для виклику методу використовуються аргументи, а в самому методі – це уже параметри, оскільки передаються лише значення змінних, а не самі змінні. Аргументи та параметри повинні бути одного і того ж типу. Якщо ми передаємо цілочисельне значення, то і параметр повинен бути цілочисельним і т.п. Тобто в методі створюються нові змінні. В деяких мовах, наприклад в С++, як аргумент можна передати посилання на певну змінну, таким чином її можна буде модифікувати в функції через вказівник на дану змінну. В Java в метод передаються лише значення змінних, тому розрізнення аргументів і параметрів менш суттєве і ми там і там використовуватимемо термін «параметр». Об'єкти ж в методи передаються по посиланню. Тобто, при передачі в якості аргументу об'єкта, буде передане посилання на об'єкт, а не створений новий об'єкт.
Повернемось до нашої програми. Повторюваними є присвоювання значень змінним, обчислення та виведення об’ємів. Тому корисно буде створити два методи safeValue (double width, double height, double depth) та safeVolume (). Перший для присвоєння змінним значень, а другий для обчислення об'єму.
Модифікована програма матиме вигляд:
class Safe {
double width = 10; // поля класу можуть бути ініціалізовані з самого початку
double height = 10;
double depth = 10;
double safeVolume = 0;
// метод присвоєння значень змінним
void safeValue(double pWidth, double pHeight, double pDepth) {
width = pWidth;
height = pHeight;
depth = pDepth;
}
// метод для обчислення об'єму сейфа
double safeVolume() {
return width * height * depth;
}
}
public class CoinVolume {
public static void main(String[] args) {
double width1 = 10, height1 = 20, depth1 = 40;
Safe mySafe1 = new Safe(); //створюємо перший сейф
Safe mySafe2 = new Safe(); //створюємо другий сейф
//задаємо розміри сейфу
//викликаємо метод safeValue() класу Safe, що ініціалізує поля об'єкту
mySafe1.safeValue(width1, height1, depth1);
mySafe2.safeValue(10.0, 15.0, 15.5); //можна і так
//виводимо на екран об'єми сейфів
//для чого викликаємо метод safeVolume(), який повертає обчислений об'єм кожного сейфу
System.out.println("Об'єм 1-го сейфу=" + mySafe1.safeVolume());
System.out.println("Об'єм 2-го сейфу=" + mySafe2.safeVolume());
}
}
Як бачимо код програми спростився. Тепер набагато легше модифікувати програму, додаючи нові сейфи. Крім того можна, наприклад, вивести повідомлення з інформацією про об’єм, розмістивши його в метод safeVolume() або зовсім в окремий метод, наприклад, printVolume().
Слід зауважити, щодо назв змінних-параметрів. Так для висоти, ширини та глибини в методі safeValue() вибрані назви pWidth, pHeight, pDepth. Вони могли б мати назви і просто width, height, depth, але тоді б вони перекрили доступ до однойменних змінних класу. В такому випадку, щоб звернутися до змінних класу з методу необхідно вживати ключове слово this. Наприклад: this.height=height
– тут ми присвоюємо змінній класу this.height одержаний методом параметр height.
Конструктори
[ред.]Замість методу safeValue(), який в нас заповнює змінні об’єкту значеннями ми можемо створити метод, який буде мати назву таку ж як і клас, тобто Safe(pWidth, pHeight, pDepth). Це дасть нам можливість ще скоротити програму, оскільки при створенні об’єкту ми зможемо зразу ж задавати розміри сейфу.
Safe mySafe1 = new Safe(10.0, 15.0, 20.0)
Такі методи носять назву “конструктор класу”. Коли ми писали просто new Safe(); то віртуальна машина використовувала конструктор по замовчуванню без параметрів, який практично нічого корисного для нас не робив. Тепер же ми можемо використовувати новий створений нами конструктор.
Таким чином, новий варіант програми:
class Safe {
double width;
double height;
double depth;
// конструктор
Safe(double pWidth, double pHeight, double pDepth) {
width = pWidth;
height = pHeight;
depth = pDepth;
}
// обчислюємо об'єм сейфу
double getVolume() {
return width * height * depth;
}
}
public class CoinsVolume {
public static void main(String[] args) {
double width1 = 10, height1 = 20, depth1 = 40;
Safe safe1 = new Safe(width1, height1, depth1); // створюємо 1-й сейф
Safe safe2 = new Safe(10.0, 15.0, 20.0); // створюємо 2-й сейф
Safe safe3 = new Safe(10.3, 15.4, 20.5); // створюємо 3-й сейф
Safe safe4 = new Safe(20.0, 30.0, 20.0); // створюємо 4-й сейф
printSafeVolume(safe1, 1); // виводимо об'єм 1-го сейфу
printSafeVolume(safe2, 2); // виводимо об'єм 2-го сейфу
printSafeVolume(safe3, 3); // виводимо об'єм 3-го сейфу
printSafeVolume(safe4, 4); // виводимо об'єм 4-го сейфу
}
// вивід об'єму сейфу
// safe - сейф
// number - номер сейфу
static void printSafeVolume(Safe safe, int number) {
// викликаємо метод getVolume(), що обчислює об’єм сейфу і результат виводимо на екран
System.out.println("Об'єм " + number + "-го сейфу = " + safe.getVolume());
}
}
Результат виконання:
Об'єм 1-го сейфу = 8000.0 Об'єм 2-го сейфу = 3000.0 Об'єм 3-го сейфу = 3251.71 Об'єм 4-го сейфу = 12000.0
В наведеному прикладі, щоб обчислити об’єм нового сейфу, нам потрібно додати лише два рядочки тексту (дві інструкції). Щоправда навіть це можна автоматизувати за допомогою використання іншого типу даних – масивів, які будуть розглядатися пізніше. Можна також додати клас Coin, в якому був би метод для обчислення сукупного об’єму різноманітних монет. Ви можете спробувати зробити таку програму зараз. Це буде корисним для засвоєння викладеного матеріалу.
Тема класів та методів значно комплексніша, тому основне, що Ви повинні винести з даного розділу – це розуміння того, як використовуються методи класів. Java надає великий набір уже готових класів та методів, які значно спрощують роботу програміста.