Podstawy klas
AUTOR: MAREK SOWA
Klasy są nieodłączną częścią Javy. Każdy, nawet najprostszy program napisany w tym języku posiada przynajmniej jedną, najprostszą klasę. Co to jest? To tak jakby kontener, w którym trzymamy pola i metody. Naturalną reprezentacją klasy jest obiekt. Tu warto zapamiętać, że zgodnie z zasadą Single responsibility principle (Zasada jednej odpowiedzialności) - Klasa powinna mieć tylko jedną odpowiedzialność (jak sama nazwa wskazuje). A to oznacza, że nigdy nie powinien istnieć więcej niż jeden powód do modyfikacji klasy.
Aby to zobrazować, wyobraźcie sobie, że szkoła to program komputerowy. Program ten składa się z klas takich jak: wychowania fizycznego, matematyczna czy szatnia:
Jak widzimy na obrazku klasy zaznaczone są na fioletowo. Mogą one składać się z pól i metod.
Zielone to pola, czyli właściwości klasy.
Szarym kolorem oznaczono metody, czyli czynności, które można wykonać dzięki tej klasie.
Po wstępnym zapoznaniu się z naszym programem "szkoła" możemy wywnioskować, że w tej "szkole" można, na przykład, dokonywać obliczeń metodą oblicz() po wywołaniu jej z klasy: matematyka.
class Matematyka{ //pola Tablica tablica; Kreda krreda; Biurko biurko; Kalkulator kalkulator; Rzutnik rzutnik; void wejdz(){ // cialo metody wejdz } void przeczytaj(){ // cialo metody przeczytaj } void oblicz(){ // cialo metody oblicz } void napisz(){ // cialo metody napisz } } //kod Matematyka matematyka = new Matematyka(); matematyka.oblicz();
* nie możemy stworzyć dwóch klas o tej samej nazwie w tym samym pakiecie
Pola (cechy, zmienne, właściwości, dane, elementy)
Wszystkie informacje o klasie przechowujemy w polach dzięki zmiennych. Oczywiście pola są opcjonalne, istnieją klasy, które przechowują w sobie tylko metody, czyli czynności. Są też i takie, które przechowują tylko pola. Inaczej mówiąc, są to swego rodzaju kontenery na dane. Takie magazyny informacyjne.
class Human{ String name; int age; int pesel; }
Obiekt
Jak już zostało wspomniane, obiekty reprezentują klasy. Aby dostać się do obiektu, musimy go najpierw stworzyć, używając słówka kluczowego new:
Human human = new Human();
Wyżej stworzyliśmy obiekt human, który reprezentuje swoją klasę. Aby dostać się do metod, które posiada klasa Human, trzeba odwołać się do nich po kropce:
Human human = new Human(); human.think(); // Human thinking } } class Human{ String name; int age; int pesel; void think(){ System.out.println("Human thinking"); } }
Konstruktor
Czasami bywa tak, że tworząc obiekt, chcielibyśmy od razu deklarować jego wstępne dane. Takie deklaracje pozwalają tworzyć wyróżniające się obiekty. Z pomocą przychodzi nam konstruktor klasy. Bardzo łatwo go stworzyć i rozpoznać, wygląda podobnie do zwykłej metody, ale o takiej samej nazwie, jak nazwa klasy. Spójrzcie poniżej:
class Human{ String name; int age; int pesel; public Human() { } void think(){ System.out.println("Human thinking"); } }
Słówko kluczowe public to modyfikator dostępu - teraz go zignorujcie, będzie opisane w innym wpisie. Konstrukcja Human(){} oznacza konstruktor bezargumentowy. Działa dokładnie tak, jak metoda bezargumentowa. Ten konstruktor tworzy obiekt tak, jak to widzieliśmy do tej pory, bez przypisania polom konkretnych wartości. Aby zadeklarować nowy obiekt z konkretnymi wartościami, trzeba stworzyć odpowiedni konstruktor argumentowy:
public Human(String name, int age, int pesel) { this.name = name; this.age = age; this.pesel = pesel; }
W przykładzie powyżej widzimy, że konstruktor przyjmuje wartości w nawiasie kolejno swoich pól. Zobaczmy to na przykładzie. Nasze pole w klasie Human{ String name;} posiada taką samą nazwę jak argument w konstruktorze =] Dzięki takiemu konstruktorowi możemy tworzyć teraz obiekty i dynamicznie przypisywać im właściwości:
Human monia = new Human("Monika", 20, 123456789); Human gucio = new Human("Gustaw", 30, 456789123); Human frania = new Human("Franciszka", 40, 789456123);
Pierwszy przyjęty argument tego konstruktora to nasze pole 'name'. Analogicznie drugi to 'age', a trzeci to 'pesel'. Stworzone obiekty będą miały już zapisane swoje właściwości. Żeby to udowodnić, wystarczy się do nich odwołać:
System.out.println(monia.age); // 20 System.out.println(gucio.age); // 30 System.out.println(frania.age); // 40
Konstruktory można przeciążać tak, jak metody. Do tej pory już przeciążyliśmy konstruktor klasy Human, ponieważ posiadamy już konstruktor bezargumentowy: Human() oraz jeden konstruktor argumentowy: Human(String name, int age, int pesel).
Warto pamiętać, że konstruktorem domyślnym jest ten bezargumentowy: Human(), pomimo tego, że nie został utworzony w klasie. Będzie on dostępny zaraz po utworzeniu klasy oraz w przypadku, gdy klasa nie posiada żadnego innego konstruktora. Natomiast po stworzeniu przez nas jakiegokolwiek konstruktora argumentowego: Human(String name, int age, int pesel), to właśnie on będzie tym domyślnym. Aby znowu uzyskać dostęp do konstruktora bezargumentowego: Human(), trzeba go po prostu stworzyć. Przy okazji przeciążając konstruktor. Na szczęście nie musicie się tym zbytnio martwić. Kompilator poinformuje was o tym przy próbie tworzenia obiektu. Dodatkowo większość IDE posiada funkcje tworzenia konstruktorów za pośrednictwem odpowiedniego skrótu klawiszowego =]
Tworząc konstruktor możemy zadeklarować dowolną liczbę przyjmowanych pól, ale tworząc obiekt z tego konstruktora, musimy już wszystkie pola podać w nawiasach. Niektórych pól nie musimy deklarować, a można je sobie wstępnie generować:
public Human(String name, int age) { this.name = name; this.age = age; this.pesel = age*10; }
Human bolek = new Human("Bolesław", 25); System.out.println(bolek.pesel); // 250
This
Aby kompilator połapał się, o którą dokładnie nazwę nam chodzi, używamy słowa kluczowego this. Oznacza ono, że mamy na myśli nazwę pola w danej klasie, a nie nazwę argumentu w konstruktorze. Zapis this.name odwołuje się do zmiennej name z pola klasy Human.
Abstrakt
W tym wstępie do klas w Javie warto wspomnieć na szybko o abstrakcyjności klas. Słówko abstract przed klasą oznacza twór abstrakcyjny. Klasa abstrakcyjna działa prawie tak samo, jak zwykła klasa. Może posiadać pola, konstruktory i metody, ale nie może zostać stworzony obiekt tej klasy. Zapytacie, jaki w takim razie to ma sens? Odpowiedź jest prosta. Ponieważ czasami musimy użyć klasy w naszym drzewku hierarchii. Stworzyć przodka oraz kilka klas potomnych zamiast wielkiej ilości pojedynczych klas. Takie rozwiązania są elastyczne i bezpieczne, tylko spójrzcie na przykład poniżej:
abstract class Animal{ String name; int legs; } class Mammal extends Animal { } class Bird extends Animal { } Bird jaskółka = new Bird(); jaskółka.legs = 2;
Dzięki użyciu klasy abstrakcyjnej nie musimy deklarować w każdej klasie zwierzęcej pola leg; czy name;. Zadeklarowaliśmy to tylko w jednej klasie abstrakcyjnej Animal(). Wiemy, że każdy Animal() posiada nogi.
Co znaczy słówko extends oraz jak to działa, że pomimo braku pola leg; w klasie Bird() mogliśmy się do tego pola odwołać? To już pozostawimy sobie na inny wpis o dziedziczeniu.
Komentarze są wyłączone