Sierpień 8, 2018

Klasy – wstęp do obiektowości

Wstęp do obiektowości

W ostatniej lekcji podsumowaliśmy podstawy języka Javy – stworzyliśmy prostą aplikację do zarządzania zadaniami i projektami. Jednak musisz przyznać, że nie było to zbyt ładnie napisane – wszystko było wpakowane do klasy Main. Czas to zmienić – nadchodzą czasy obiektowości.

Obiektowość często jest trudny etapem w życiu osoby uczącej się programować, lecz postaram Ci się ją wytłumaczyć jak najprościej potrafię. Na koniec daj mi znać czy zrozumiałeś choć trochę z tego o czym piszę. 😉

Jak rozumieć obiektowość?

Obiektowość polega tak naprawdę na rozdzielaniu kodu aplikacji na kilka/wiele klas i na tworzeniu obiektów na podstawie tych klas.

Prawda jest taka, że wszystkie aplikacje mogłyby być napisane strukturalnie (tzw. ciurkiem) w jednym pliku – więc czemu tak nie robimy?

W skrócie obiektowość pozwala nam na prostsze rozbudowywanie aplikacji, stosowanie przeróżnych wzorców projektowych oraz utrzymywanie/naprawianie samej aplikacji. Kod napisany przy użyciu obiektowości jest dużo prostszy do zrozumienia i możliwy w łatwy sposób do podzielenia na modułu: np. warstwę odpowiadająca za komunikację z bazą danych, za komunikację z innym serwerem, za komunikację z ostatecznym użytkownikiem aplikacji.

Skoro już nagadałem Ci, że obiektowość jest taka super to czas zagłębić się dalej w to zagadnienie.

Klasy…

Wspomniałem wczesniej coś o klasach, w swoich projektach wszystko pisałeś w klasie Main – o co z nią chodzi?

Klasy to tak naprawdę kontenery na pola (zmienne) i metody – nawet lepszym określeniem byłoby użycie szablony obiektów.

Tak jak wspomniałem w klasie możemy zdefiniować pola – robiłeś to w ostatniej aplikacji todo np.:

static String [] tasks = new String[100];
static String [] projects = new String[100];
static String [] changeLog = new String[100];

static int tasksCount = 0;
static int projectsCount = 0;
static int changeLogCount = 0;

Oraz metody np.:

public static void addTask(String task) {
    boolean hasCapacityForNewTask = tasksCount < tasks.length;
    if (hasCapacityForNewTask) {
        tasks[tasksCount] = task;
        tasksCount++;
        addLog("Dodano nowe zadanie: " + task);
    } else {
        addLog("Nie udało się dodać nowego zadania: " + task);
    }
}

Definiując wspomniane: pola i metody tworzymy właśnie szablon dla obiektów – tworzymy nowy typ! Taki sam jak typ String, którego nie raz już używałeś.

Co już wiemy?

Klasę możemy rozumieć jako szablon, na podstawie, które mogą być tworzone obiekty.

Obiekty (własnie na podstawie klasy) mogą przechowywać w sobie informację – w zdefiniowanych polach, oraz wykonywać określone operacje – możemy „na nich” wywoływać operację – tak jak robiłeś to przy Scannerze:

String name = scanner.nextLine();

Właśnie metoda nextLine została zdefiniowana w klasie Scanner na podstawie, które zostal stworzony obiekt typu Scanner – o nazwie scanner.

Konstruktor

Zanim zaczniemy jeszcze pisać swoją pierwszą klasę muszę powiedzieć Ci o bardzo ważnej rzeczy – o konstruktorze.

Jak sama nazwa może Ci wskazuje – służy on do konstruowania. Konstruowania czego? Obiektu!

Konstruktor jest bardzo specyficzną metodą – albowiem jego nazwa musi być zawsze taka sama jak nazwa klasa. Nie przyjmuje on żadnego typu zwracanego np. void, int itd.

Przykładowy konstruktor wygląda tak:

public class User {
  public User() {
  }
}

Jak widzisz sam bardzo specyficzna metoda.

Wyżej przedstawiłem Ci konstruktor bezparametrowy – czyli nie przyjmuje żadnych argumentów.

Zaś istnieje również konstruktor parametrowy – jak każda inna zwykła funkcja z argumentami.

public User(String login, String password) {
  //do something
}

Możemy mieć wiele konstruktorów – każdy z inną ilością parametrów.

Kiedy jest wywoływany konstruktor?

Konstruktor jest wywoływany, gdy tworzymy obiekt przy użyciu operatora new. Robiłeś już to wielokrotnie np. przy Scannerze:

Scanner scanner = new Scanner(System.in);

Wykorzystywałeś konstruktor parametrowy klasy Scanner, który jako argument przyjmował zdefiniowaną stałą w Javie System.in.

Do czego jest wykorzystywany konstruktor?

Skoro konstruktor służy do stworzenia obiektu – to własnie wtedy wirtualna maszyna Javy (JVM) rezerwuję dla niego pamięc w komputerze. Gdy przestanie być używany to obiekt automatycznie zostanie usunięty z pamięci – więcej o czyszczeniu pamięci w JVM będzie w dalszej części kursu.

Konstruktor możemy wykorzystać do wprowadzenia pewnych wartości do tworzonego obiektu np.

public User(String login, String password) {
  //inicjalizacja pól na podstawie podanych argumentów
}

Dzięki temu możemy zapisać podane argumenty w polu obiektu i mieć go do czasu edycji lub usunięcia obiektu.

Podsumowując konstruktor służy do stworzenia obiektu typu danej klasy, zazwyczaj w nim inicjalizujemy zmienne i inne obiekty.

Mając tyle teorii możemy przejść do stworzenia pierwszej klasy.

Pierwsza klasa – User

Czas na stworzenie pierwszej klasy – możęmy to zrobić wyklikując w projekcie: PPM -> New -> Java Class oraz podać nazwę klasy – u nas będzie to User.

W zerowym tygodniu pojawił się wpis na temat protipów w Intellij Idea – możesz go teraz wykorzystać. Użyj CTR+SHIFT+A -> Wpisz Java Class -> Naciśnij enter i pojawi Ci się okno tworzenia klasy.

Po stworzeniu klasa powinna wyglądać tak:

package pl.maniaq;

public class User {
}

U mnie jest package pl.maniaq, ponieważ tak się nazywa u mnie paczka w projekcie – u Ciebie ta nazwa będzie inna!

Przypuśćmy, że tworząc obiekt typu User chcemy w nim przechowywać następujące informację: login, password, email oraz wiek. Stwórzmy, więc te cztery pola zachowując odpowiedni typ.

public class User {

    String login;
    String password;
    String email;
    int age;
}

Pola na razie zdefiniowaliśmy – do inicjalizacji wartością dojdzie w konstruktorze.

Zdefiniujmy na początku konstruktor domyślny – gdzie ustawimy jakieś domyślnie wartości w polach.

public User() {
    login = "login";
    password = "pass";
    email = "mail@example.com";
    age = 0;
}

Oraz konstruktor parametrowy, gdzie dojdzie do inicjalizacji wszystkich pól klasy.

public User(String login, String pass, String email, int age) {
    this.login = login;
    password = pass;
    this.email = email;
    this.age = age;
}

STOP! Chwila uwagi!

W ciele konstruktora użyłem słowa this – co ono oznacza?

This mówi, że chodzi nam o coś co jest elementem klasy np. właśnie poleDzięki niemu możemy zażegnać konflikty w nazwach pól z argumentami konstruktora.

Bez problemu mogłem zapisać password = pass – ponieważ nie ma żadnego konfliktu nazw.

Jednak, w innych przypadkach gdzie nazwa pola i nazwa argumentu są takie same to muszę wskazać wyraźnie przy użyciu this, że chodzi mi o pole klasy.

Nasza klasa jest gotowa i wygląda tak:

public class User {

    String login;
    String password;
    String email;
    int age;

    public User() {
        login = "login";
        password = "pass";
        email = "mail@example.com";
        age = 0;
    }

    public User(String login, String password, String email, int age) {
        this.login = login;
        this.password = password;
        this.email = email;
        this.age = age;
    }


}

Możemy przejść teraz do jej sprawdzenia w metodzie main.

Działanie klasy User

Zacznijmy od pustej klasy Main.

public class Main {

    public static void main(String[] args) {
  
    }
}

Stwórzmy swój pierwszy obiekt typu User – czyli wykorzystując naszą klasę. Użyjmy do tego na początku konstruktora bezparametrowego.

User user = new User();

Teraz sprawdźmy co się kryje w naszym pudełki – tzn. obiekcie user.

Wpisując w Intellij Idea:

user.

Powinieneś otrzymać taką o to listę. 😉

Środowisko nam podpowiada co możemy wykonać/wyciągnąć z obiektu.

Na początku mamy cztery elementy: age, email, login, password – są to pola obiektu, co wskazuje ikona z literą f po lewej stronie. Po prawej stronie możesz również zobaczyć typ konkretnego pola – czyli zmiennej.

Poniżej znajduję się wiele metod np. equals, hashCode, toString – te wszystkie metody są zdefiniowane w klasie Object – możemy ich wykorzystać, ponieważ każdy obiekt zawiera w sobie te metody – każdy!

Skoro w naszej klasie są cztery pola to wypiszmy je na ekran – zobaczmy co tam się kryje.

System.out.println(user.age);
System.out.println(user.email);
System.out.println(user.login);
System.out.println(user.password);

Po uruchomieniu otrzymujemy:

0
mail@example.com
login
pass

Myślę, że kojarzysz te wartości – są to wartości, którymi zainicjalizowaliśmy pola w konstruktorze bezparametrowym – o tutaj!

public User() {
    login = "login";
    password = "pass";
    email = "mail@example.com";
    age = 0;
}

Użyliśmy konstruktora bezparametrowego do stworzenia obiektu dlatego też pola mają takie wartości.

Użyjmy teraz konstruktora parametrowego – stwórzmy użytkownika na własnych zasadach!

User pablo = new User("pablo", "hasełko", "pablo@email.com", 25);

Teraz spójrzmy co się kryje pod wszystkimi polami obiektu!

System.out.println(pablo.age);
System.out.println(pablo.email);
System.out.println(pablo.login);
System.out.println(pablo.password);

Po uruchomieniu otrzymujemy:

25
pablo@email.com
pablo
hasełko

Jak widać stworzyliśmy obiekt User na własnych zasadach!

Skoro wartości możemy odczytywać – to możemy je również zmieniać. Zmieńmy hasło pablo i sprawdźmy rezultat.

pablo.password="nowe hasełko";
System.out.println(pablo.password);

Otrzymujemy:

nowe hasełko

Jak widzisz robimy to samo co ze zmiennymi na początku kursu – są one po prostu opakowane w pudełko zwane obiektem! 

Dzięki obiektom możemy grupować pola i metody – zamiast mieć milion takich zmiennych:

String loginUser1 = "admin";
String passwordUser1 = "pass";
String emailuser1 = "email@domain.com";
int ageUser1 = 13;

String loginUser2 = "escabo";
String passwordUser2 = "paqwess";
String emailuser2 = "email2@domain.com";
int ageUser2 = 15;

Możemy stworzyć dwa obiekty, które będą to przechowywały:

User admin = new User("admin", "pass", "email@domain.com", 13);
User escabo = new User("escabo", "paqwess", "email2@domain.com", 15);

Mam nadzieję, że widzisz już zaletę stosowania obiektów.

Podsumowanie

W tej lekcji damy sobie jeszcze spokój z tworzeniem metod w klasie i ich wywoływaniem – zrobimy to w następnej lekcji kursu.

Teraz wykonaj poniższe zadania, aby mieć pewność, żę wszystko zrozumiałeś. Po każdym wykonanym zadaniu obowiązkowo stwórz kilka obiektów w klasie Main!

  1. Stwórz klasę Dog, która będzie przechowywała wartości: age, name, breed – rasa psa zapisana w Stringu.
  2. Stwórz klasę Human, z konstruktorem parametrowym inicjalizującym następujące pola: name, lastname, age, height, weight.
  3. Stwórz klasę Engine – będzie w niej pole typeEngine typu String, które będzie inicjalizowane w konstruktorze parametrowym. Następnie stwórz klasę Car, która będzie miała konstruktor parametrowy, a przez niego będą inicjalizowane takie pola jak: brand (marka samochodu), color (typu String) oraz engine typu Engine – który wcześniej stworzyliśmy. Stwórz dwa samochody, wypisz na ekran jaki typ silnika każdy z nich ma.

Rozwiązania zadań możesz znaleźć tutaj.

Po wykonaniu tych zadań mamy już pewność, że możesz przejść do następnej lekcji. 😉