Об’єктно-орієнтоване програмування
Огляніться: навколо себе Ви бачите багато речей — все це об’єкти, і більшість, якщо не всі, можна віднести до якогось типу — класу. Погляньте на схему нижче. “Машина” — це клас, а “BMW” або “Mercedes-Benz” — це об’єкти цього класу. Кожна машина має свої властивості — змінні об’єктів — та методи — функції об’єктів. Така система є першим із “чотирьох китів”, що включає в своє поняття Об’єктно-орієнтоване програмування (ООП) — інкапсуляція.
Нехай є базовий клас “машина”, який поділяється на підкласи “вантажний” та “пасажирський”, який у свою чергу є батьківським класом (суперкласом) для дочірніх класів (підкласів) “седан”, “пікап”, “універсал”. Клас “машина” має властивість “швидкість”, клас “пасажирський” також має властивість “кількість місць”, але так як він є підкласом “машини”, тобто наслідує її, то також має “швидкість”. Успадкування є другим компонентом ООП.
Залишмо автомобілі й візьмімо для прикладу тварин: є клас “Тварина” з методом “голос”. Різні тварини роблять відмінні один від одного звуки, тому викликавши метод “голос”, ми побачимо “Тварина видає звук”. “Кіт” та “Собака” успадковують клас “Тварина” (адже є підкласами) та мають свої реалізації (імплементації) цього методу. Якщо є декілька різних тварин (для прикладу, у масиві), то викликавши метод “голос” для кожної, результати будуть різні: як “гав”, так і “няв”. Виходить, що не потрібно знати яка саме ця тварина для того, щоб почути відповідний звук: кожен підклас “Тварини” має свій “голос”, заміщуючи цей метод. Третім принципом ООП є поліморфізм, що означає здатність мати багато форм.
Напевно, у Вас є смартфон або комп’ютер, якщо Ви це можете читати. На ньому є кнопки регулювання звуку. Вам достатньо використати їх, і гучність зміниться; таким чином не потрібно знати, як вони працюють всередині в деталях. Так само у Java деякі методи або властивості класу можуть бути невидимими поза ним, у цьому суть четвертої складової ООП — абстракції.
Пакети
У Java кожен клас (навіть Main) вірогідніше знаходиться у певному пакеті. Всі змінні, класи, об’єкти мають свою назву (наприклад tree12
чи kilkist_vygotovlenyh_verstativ
) — ці назви вказуються ідентифікаторами (іменами). У сучасних мовах програмування їх об’єднують у простори імен. Нехай Максим працює в компанії А, і він має номер працівника 10, а Софія працює в компанії Б, хоча також має номер працівника 10. У обох працівників однакова змінна номер працівника
з однаковим значенням 10
, але її можна розрізнити, адже вони працюють у різних компаніях, тобто просторах імен. Саме у Java цю ідею втілюють пакети, які на практиці є папками, а у коді визначаються на початку файла (як у файлі Main.java) словом package
та шляхом до пакету, але через крапку:
package com.moyacompaniya.moyipratsivnyky;
Область видимості
Можливо, Ви помітили, що якщо створити змінну у блоці, то поза ним використовувати її не можна. Це ж правило стосується й кожної функції і навіть класів. Кожна така ділянка програми з обмеженням на доступ по імен має свою область видимості. Можна використовувати змінні з ширшої області видимості (наприклад, у циклі змінна, створена у функції), але не навпаки — інколи це називають “принципом мембрани”. До речі, класи (зокрема в пакетах) також мають свої області видимості.
В ООП є поділ відносних областей видимості на відкриті, закриті й захищені. Ці області видимості полів, методів і класів визначають словами public
, private
та protected
відповідно, а самі слова називають модифікаторами доступу. Нижче наведена таблиця порівняння доступу до даних, залежно від цих модифікаторів:
Модифікатор | Всередині класу | У пакеті класу | В підкласі класу | Всюди |
---|---|---|---|---|
public | ||||
protected | ||||
(без модифікатора) | ||||
private |
Інкапсуляція
Класи
Дослівно “внесення у капсулу” означає занесення деяких даних та функцій в обмежений простір, огортання у клас. Сама змінна “номер” не має сенсу — який номер? Тим часом призначення змінної “номер” у класі “Телефон” одразу зрозуміле. Найчастіше класи бувають відкритими (публічними) або без модифікатора. Причому, кожен публічний клас має бути в окремому файлі. Як видно із першого класу Main, для створення достатньо використати слово class
. Приклади:
public class Car {
//Тіло класу
}
class Engine {
//Тіло класу
}
Класи містять або дані (поля), або функції (методи). Вони можуть мати такі (насправді їх більше) модифікатори: видимості — public
, protected
, private
, та тип — static
. Створімо свій клас “Машина” з полями “швидкість”, “ціна”, “модель” і “виробник”, та методом “їхати”. Уявімо, що швидкість машини можна дізнатись лише вимірявши її, а напряму отримати не можна — швидкість буде “приватною” — для цього створимо ще один метод “виміряти швидкість”:
class Car {
private int speed;
public int price;
String model, manufacture;
void move() {
System.out.println("Машина їде");
}
int getSpeed() {
return this.speed;
}
}
По-перше, скажу, що звичайно можна було зробити ціну і швидкість без модифікаторів, а методи відкритими; це зараз не принципово, тому на розсуд автора коду. По-друге, щоб не повторювати тип для декількох змінних, у Java можна писати їх через кому: int a, b, c;
— це те ж саме, що й int a; int b; int c;
. По-третє, тут з’являється нове слово — this
. Можна було б писати без нього, однак я дуже рекомендую використовувати this
. Це слово існує для звернення до полів і методів класу в його межах. Це важливо, адже може бути змінна всередині методу з однаковою назвою, і тільки так їх можна розрізнити:
class A {
int x = 20;
void f(){
int x = 5;
System.out.println( x ); //5
System.out.println( this.x ); //20;
}
}
Об’єкти
У “шляху” до даних в Java використовують крапки, тому ми пишемо this.
x. Що ж, у нас є клас Автомобіль, тому тепер маємо можливість створити об’єкт — окремий екземпляр, сутність класу. Якщо Ви пам’ятаєте, для введення ми вже використовували об’єкт класу Scanner
. Створюються об’єкти як звичайні змінні (тип назва;
), а от задаються словом new
, назвою об’єкта і дужками:
Car car1;
Car car2 = new Car();
Якщо поле (або метод) не приватне, то можна його змінити або зчитати (або викликати) для даного об’єкта. Для цього використовуємо крапку:
car2.model = "Camaro SS";
car2.move();
Конструктори
Навіщо при створенні об’єкту потрібні дужки, якщо це не функція? Дивно, але кожен клас має хоч один конструктор — особливий метод, який викликається при створенні об’єкта, навіть якщо ми його ніде не писали. Конструкторів може бути багато, кожен з різними аргументами, як перевантажені функції. Щоб створити конструктор, достатньо написати метод з такою ж назвою, як і клас. Конструктори не мають повернення й найчастіше є відкритими. Ось відразу декілька конструкторів для “Машини”:
class Car {
... //Тут поля, функції, створені раніше
public Car(){
System.out.println("Ви створили нову машину.");
}
public Car(String manuf, String model){
this.manufacture = manuf;
this.model = model;
System.out.println("Ви створили новий " + manuf +" "+ model);
}
}
Тепер можна створювати машини, встановлюючи бренд і модель відразу, або не встановлювати, як раніше:
Car c1 = new Car();
Car c2 = new Car("Ford", "Mustang GT");
Також є метод для виклику іншого конструктора класу — this()
. Таким чином можна “доповнювати” дані об’єкта або встановлювати їх за замочуванням (це можна робити просто присвоєнням до полів, обидва варіанти нижче):
class A {
String name;
public A(String name) {
this.name = name;
}
public A() {
this("Julia");
}
}
class B {
String name = "Julia";
}
Статичність
Як відомо, у кожного об’єкта можуть бути свої значення полів, однак інколи зручно (наприклад, для статистичних даних), коли значення одне для всього класу. Такі поля і методи є статичними — вони належать не об’єктам, а класам. Візьмімо наш клас Автомобіля й додамо до нього статичне поле count
(кількість), а в конструкторі будемо додавати до нього 1. Отримаємо, що при створенні кожної машини кількість автомобілів збільшуватиметься і у будь-який момент можна буде дізнатись загальну кількість машин. Увага: статичні дані належать класу, тому доступ до них лише через сам клас, а не через об’єкт!
class Car {
static int count = 0;
public Car(){
count++;
}
}
System.out.println( Car.count ); //0
Car c1 = new Car();
Car c2 = new Car();
System.out.println( Car.count ); //2
Далі
Тема ООП є досить великою, тому я вимушен розділити її на дві частини. Тут Ви розглянули основу ООП — інкапсуляцію, а у другій частині буде успадкування, абстракція і поліморфізм, а також Ви дізнаєтесь про константи. Попрактикуйтесь та спробуйте зрозуміти усі попередні теми перед тим, як починати наступну.