Kurs java dla początkujących - #2 Klasy i Obiekty

Kurs Java dla początkujących – #2 Klasy i Obiekty

Witam Cię w drugiej części kursu programowania w języku Java. Dzisiaj zajmiemy się tematem obiektowości języka Java. Czyli czymś, co jest jego esencją. Java jest językiem obiektowym, tzn. że „wszystkie” byty, które występują w tym języku są obiektami. Prawie wszystkie, bo poznałeś już w pierwszej części metody statyczne, które mogą istnieć bez obiektów. Ale generalną zasadą jest to, że Java jest językiem obiektowy. Co to, tak naprawdę, znaczy najlepiej będzie zrozumieć na przykładzie.

 

Pierwszy obiekt

Poniżej mamy klasę Main, z poprzedniej części. Mamy w niej tylko metody statyczne.

public class Main {

    public static void main(String[] args) {
        System.out.println(sum(3,2));
        System.out.println(substract(3,2));
    }

    private static int sum(int x, int y) {
        return x + y;
    }

    public static int substract(int x, int y) {
        return x - y;
    }
}

Nie mam tutaj żadnych obiektów. Możemy jednak w prosty sposób, przekształcić naszą klasę w taki sposób, by pojawił się pierwszy obiekt.

public class Main {

    public static void main(String[] args) {
        Main object = new Main();
        System.out.println(object.sum(3,2));
        System.out.println(object.substract(3,2));
    }

    private int sum(int x, int y) {
        return x + y;
    }

    private int substract(int x, int y) {
        return x - y;
    }
}

Dodałem linijkę  Main object = new Main();. Początkowa część tej konstrukcji to utworzenie zmiennej lokalnej typu Main i nadanie jej nazwy object. Następnie poprzez znak równości przypisujemy zmiennej object referencję (odwołanie) do obiektu, który tworzymy poprzez new Main. Za każdym razem gdy będziemy chcieli utworzyć obiekt będziemy używali słowa kluczowego new (jest to charakterystyczne dla wielu języków programowania).

Zmienna – zmienna jest to element języka, który przechowuje referencje do obiektu lub jakieś wartości.

Dzięki temu, że stworzyliśmy zmienną object, możemy teraz korzystać z metod, które udostępnia klasa Main (metod publicznych, bo tylko te są widoczne na zewnątrz obiektu). Odwołujemy się do nich za pomocą kropki np. object.sum(3,2). Tak jak to napisałem w kolejnych liniach.

Kolejną rzeczą, która się zmieniła to: usunąłem słowa kluczowe static, nie będą one już potrzebne, ponieważ nasze metody będziemy wywoływać w kontekście obiektu, więc nie mogą być one statyczne.

 

Klasy i obiekty

Klasa vs Obiekt vs Typ – padły tu trzy pojęcia, których użycie i kontekst może nie być do końca jasny. Jak napisałem w pierwszej części kursu. Klasa pomaga nam zorganizować kod. Ale w kontekście obiektowości klasa jest szablonem dla obiektu. Z jednej klasy możemy utworzyć dowolną ilość różnych (różniących się od siebie) obiektów. Typ w kontekście obiektu, jest to typ klasy, z której powstał obiekt. Obiekt utworzony poprzez new Main() jest więc typu Main. Typ w kontekście zmiennej, jest to typ wartości zmiennej, jaka może być przypisana do danej zmiennej. Zmienna object jest typu Main , ponieważ możemy do niej przypisać tylko obiekty typu Main. Jeśli byś spróbował przypisać do zmiennej typu Main jakiś inny obiekt, to otrzymasz błąd kompilacji.

Niespójność typów

Obiekt – jest bytem, który powstaje podczas działania programu i jest przechowywany w pamięci komputera. W samym kodzie nie tworzymy obiektów, jedynie deklarujemy gdzie te obiekty mają powstać podczas działania programu.

 

Wywołanie object.sum(3,2) jest esencją korzystania z obiektów.

Za każdym razem, gdy będziesz chciał wykonać jakąś operację na obiekcie lub ustawić jakieś jego właściwości. Albo zmienić w jakiś sposób jego stan. Będziesz korzystał z operatora kropki „.” poprzedzonego nazwą zmiennej, do której przypisany jest dany obiekt. Natomiast po kropce będzie następowała nazwa wywoływanej operacji. I mimo, że w kodzie nie tworzymy obiektów, będziemy bardzo często mówili, że używamy jakiś obiektów lub wywołujemy jakieś metody z danego obiektu.

 

Dodałem trochę kodu, trochę pozmieniałem istniejący, przez co jest on troszkę bardziej skomplikowany. Ale co nam to tak naprawdę daje ? W tym przykładzie niewiele. Ale w bardziej rozbudowanych przykładach (programach), umieszczenie wszystkiego w jednej klasie jest bardzo niepraktyczne. Im większy program tym trudniej edytować tę jedną klasę. Wyobraź sobie, że istnieje wiele systemów, które składają się z milionów linii kodu. Systemy te są tworzone przez dziesiątki, a czasem setki programistów. Czy zastanawiałeś się, jak by wyglądała edycja takiego programu gdyby zawrzeć go w jednym pliku? Była by niemożliwa!

Dlatego programy dzielimy na klasy (czasem dziesiątki tysięcy klas), z których możemy tworzyć obiekty, na których możemy łatwo operować wywołując odpowiednie metody.

 

Rozdzielenie klas

Spróbujmy jeszcze bardziej przerobić przykładowy program tak, by był jeszcze bardziej profesjonalny. Obok klasy main stwórz nową klasę o nazwie Calculator . Możesz to zrobić na kilka sposobów. W Intellij IDEA kliknij na folder src, kliknij prawy przyciskiem myszy, pojawi ci się kontekstowe menu. Wybierz New > Java Class. Wpisz nazwę Calculator i zatwierdź.

Dodano nową klasę

Teraz musisz przenieść metody z klasy Main do klasy Calculator.

public class Calculator {

    public int sum(int x, int y) {
        return x + y;
    }

    public int substract(int x, int y) {
        return x - y;
    }
}

Teraz za wszystkie obliczenia będzie odpowiadała klasa kalkulatora. Zauważ jeszcze jedną rzecz: w deklaracjach metod, słowa kluczowe private zmieniłem na public – bez tego, metody z klasy Calculator nie były by widoczne na zewnątrz i można by ich tylko używać wewnątrz klasy Calculator.

Przeróbmy teraz klasę Main tak, by zawierała wywołanie klasy kalkulator.

public class Main {

    public static void main(String[] args) {
        Calculator object = new Calculator();
        System.out.println(object.sum(3,2));
        System.out.println(object.substract(3,2));
    }
}

Po uruchomieniu programu otrzymasz dokładnie ten sam wynik, który otrzymałeś w poprzedniej części kursu. Ale organizacja kodu jest już zupełnie inna. Ta mała zmiana wprowadza Cię na zupełnie nowy poziom programowania. To jest właśnie programowanie obiektowe, w takiej formie jak zawodowi programiści używają go na co dzień.

 

Pola klasy

Poza metodami, klasy mogą też mieć właściwości (inaczej pola). Pola służą do zapisywania wartości. W Javie przyjęło się, że pola obiektów mają zwykle dostęp prywatny, a ustawianie ich wartości odbywa się przez specjalne metody w zwane getterami i setterami (o tym jeszcze później w kolejnych częściach kursu):

class Summary {
  private int sum;
  private float average;
  
  public int getSum() {
    return sum;
  }

  public void setSum(int sum) {
    this.sum = sum;
  }

  public float getAverage() {
    return average;
  }

  public void setAverage(float average) {
    this.average = average;
  }
}

a ustawiamy je w taki sposób:

Summary summary = new Summary();
summary.setSum(1);
summary.setAverage(1.0F);
// odczytujemy
System.out.println(summary.getSum() + " " + summary.getAverage());

 

Każdy komponent powinien mieć jedną odpowiedzialność

Dokonałeś właśnie swojego pierwszego refactoring’u, czyli reorganizacji kodu, która miała na celu poprawienie jego struktury, tak by była bardziej elastyczna i łatwiejszy do rozbudowywania. Teraz każda z dwóch klas ma swoje przeznaczenie (odpowiedzialność). Klasa Calculator zawiera tylko operacje związane z obliczeniami (nie powinna zawierać niczego innego). Klasa Main zawiera tylko odpowiednie wywołania innych klas, w tym wypadku tylko klasy Calculator (dodatkowo uruchamia nasz program poprzez metodę main co jest jej efektem ubocznych).

Uniwersalną zasadą jest to, że moduły, klasy, czy metody we wszystkich językach programowania powinny zwierać jedną funkcjonalność lub grupę powiązanych funkcjonalności stanowiącą spójną całość. Dlatego klasa Calculator zawiera tylko operacje związane z obliczeniami. A klasa Main tylko wywołuje kalkulator.

Dzięki temu, że od początku dbamy o to, by nasz kod był przejrzysty i łatwy w rozbudowie, dodanie np. kolejnego rodzaju kalkulatora jest bardzo proste.

 

Dodawanie nowych klas

Dodaj teraz kolejną klasę o nazwie MoneyCalculator, która będzie zajmował się operacjami na pieniądzach. Do operacji na pieniądzach w Javie używany jest specjalny typ o wysokiej precyzji BigDecimal (ponieważ typy float i double, to typy o małej precyzji – na razie nie jest to istotne na tym etapie nauki). Nasza klasa będzie wyglądała mniej więcej tak:

import java.math.BigDecimal;

public class MoneyCalculator {
    public BigDecimal sum(BigDecimal x, BigDecimal y) {
        return x.add(y);
    }

    public BigDecimal subtract(BigDecimal x, BigDecimal y) {
        return x.subtract(y);
    }
}

import – słowo kluczowe import służy do importowania dodatkowych modułów bądź bibliotek języka Java. Tutaj importujemy klasę java.math.BigDecimal, dzięki czemu będziemy w kodzie używać klasy BigDecimal w taki sposób np. new BigDecimal() lub jako parametru BigDecimal.

A w naszej klasie Main dodajemy wywołanie klasy MoneyCalculator

import java.math.BigDecimal;

public class Main {

    public static void main(String[] args) {
        Calculator object = new Calculator();
        System.out.println(object.sum(3,2));
        System.out.println(object.substract(3,2));

        MoneyCalculator moneyCalculator = new MoneyCalculator();
        System.out.println(moneyCalculator.sum(new BigDecimal("10.10"), new BigDecimal("10.20")));
        System.out.println(moneyCalculator.subtract(new BigDecimal("10.10"), new BigDecimal("5.05")));
    }
}

Tutaj również pojawił się import (IDEA powinna zrobić to za Ciebie automatycznie). Ponadto, parametry metod w nowym kalkulatorze są obiektami (tworzone  poprzez new) typu BigDecimal. Dzięki czemu w klasie MoneyCalculator wewnątrz metod sum i subtract możliwe jest wywołanie ich metod publicznych, x.add(y) i x.subtract(y).

A wynik uruchomienia jest taki jak poniżej:

5
1
20.30
5.05

 

Podsumowanie

To już wszystko w tej części kursu. Nauczyliśmy się podstaw obiektowości, a także tego jak organizować swój kod, by łatwiej można było nim zarządzać i rozwijać. Zapamiętaj, że przeciętny kod jest 8 razy częściej czytany niż, pisany. Dlatego dbałość o separację różnych funkcjonalności w różnych klasach jest kluczowa dla jego czytelności i możliwości rozbudowywania.

 

Co powinien wiedzieć każdy początkujący programista?

 

Kurs Java dla początkujących

Spis Treści:

  1. Wprowadzenie
  2. Klasy i Obiekty
  3. Tablice
  4. Kolekcje
  5. Instrukcje warunkowe i pętle
  6. Operacje wejścia i wyjścia
  7. Dziedziczenie, Polimorfizm, Interfejsy
  8. Stream’y i lambdy

 

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<