#01 — wstęp do obiektów w Javie

By 14 August 2014 January 12th, 2018 Kurs Javy

Dzisiejsza lekc­ja poświę­cona będzie w całoś­ci obiek­tom w języku Java. Nauczymy się tworzyć obiek­ty, defin­iować ich metody, pola, uży­wać ich, a także poz­namy kil­ka zasad z nimi związanych.

Na tę chwilę nie będziemy wprowadzać poję­cia dziedz­iczenia. Wprawdzie jest to ele­men­tar­na cecha pro­gramowa­nia obiek­towego, ale jej prak­ty­czne zas­tosowanie zna­jdziemy dopiero w kole­jnych lekc­jach. Na ten moment należy wiedzieć tylko, że ta lekc­ja to nie wszys­tko, co mamy do powiedzenia o obiektach.

Zastrzeżenie

Niek­tóre ele­men­ty przed­staw­ione w tej lekcji nie są zgodne ze sztuką — np. kon­wenc­ja sugeru­je nazy­wanie obiek­tów od ang­iel­s­kich nazw. Celowo korzys­tamy z pol­s­kich nazw aby ułatwić zrozu­mie­nie przykładów. Warto jed­nak mieć na uwadze, że nie jest to postrze­gane jako dobra prak­ty­ka. W przyszłoś­ci pojawi się lekc­ja w całoś­ci poświę­cona dobrym prak­tykom, kon­wencjom itp. Na  razie należy zapamię­tać, że tworząc pro­jekt komer­cyjny nazwy klas (i pól oraz metod) tworzymy od ang­iel­s­kich nazw w licz­bie pojedynczej.

Lekcja

Dzisiejsza lekc­ja będzie poświę­cona obiek­tom. Przed przys­tąpi­e­niem do niej, upewnij się że znasz różnice pomiędzy klasą a obiek­tem i rozu­miesz, czym jest obiekt, pole oraz meto­da (o tym była lekc­ja #0)

Obiekty

Aby utworzyć obiekt w jezyku Java potrze­bu­je­my znać nazwę klasy oraz paki­et (może być domyśl­ny, ale będziemy korzys­tali z innego). Aby utworzyć pier­wszą klasę sko­rzys­tamy z kreato­ra dostęp­nego w Eclipse, a następ­nie przeanal­izu­je­my wygen­erowany kod.

Krok pierwszy — tworzymy nowy projekt

Ten krok został opisany na stron­ie jak przy­go­tować środowisko. Jeśli jeszcze go nie przeczy­tałeś, zrób to teraz.

Krok drugi — tworzymy nową klasę

W menu górnym wybier­amy New -> Class lub klikamy na ikonkę download zna­j­du­jącą się na górnej belce. Pojawi się okno jak poniżej:

Dodawanie nowej klasy w środowisku Eclipse

Dodawanie nowej klasy w środowisku Eclipse

Krok trzeci — analizujemy kod

Jak zauważymy, kod który został wygen­erowany wyglą­da mniej więcej tak jak poniżej:


package pl.kobietydokodu.bazakotow.model; 

public class Kot {

}

Pier­wsza lin­ij­ka to deklarac­ja paki­etu. Paki­et, tak jak było wspom­ni­ane w poprzed­niej lekcji to sposób na grupowanie klas w naszym pro­jek­cie. Najczęś­ciej grupu­je­my je wg funkcji, np. wszys­tkie klasy doty­czące wybranego wycin­ka rzeczy­wis­toś­ci w pakiecie xxx.model, klasy, które zaw­ier­a­ją logikę biz­ne­sową w pakiecie xxx.services itp.

Główny człon nazwy paki­etu najczęś­ciej jest niezmi­en­ny dla aplikacji i tworzymy go przepisu­jąc naszą domenę inter­ne­tową od koń­ca i doda­jąc nazwę aplikacji. Przykład­owo, nasza dom­e­na to kobietydokodu.pl, a aplikac­ja którą tworzymy to baza kotów. Oczy­wiś­cie nie może­my stosować pol­s­kich znaków, nazwa paki­etu nie może zaw­ier­ać też odstępów czy znaków spec­jal­nych (więcej infor­ma­cji zna­jdziesz pod odnośnikiem numer 4 w sekcji dodatkowe infor­ma­c­je). Odwracamy najpierw nazwę dome­ny otrzy­mu­jąc pl.kobietydokodu (oczy­wiś­cie, nie musi to być rzeczy­wista nazwa — jeśli nie posi­adasz dome­ny możesz ją ‘wymyśleć’, np uży­wa­jąc pl.imienazwisko jako początek nazwy paki­etu). Następ­nie dok­le­jamy nazwę aplikacji usuwa­jąc pol­skie zna­ki, spac­je i ogon­ki, przez co otrzy­mu­je­my pl.kobietydokodu.bazakotow . To nasz pod­sta­wowy paki­et dla aplikacji, doda­je­my do niego sufiksy w zależnoś­ci od potrzeb.

Spójne nazewnict­wo paki­etów oraz ich dzie­le­nie jest też ważne z innych powodów — w przyszłoś­ci wyko­rzys­tamy paki­ety żeby automaty­zować niek­tóre czyn­noś­ci i ułatwić sobie pracę. Wprawdzie nic nie stoi na przeszkodzie żeby uży­wać domyśl­nego paki­etu lub trzy­mać wszys­tkie klasy w jed­nym pakiecie, to nie jest to dobra prak­ty­ka i nie należy tego robić poza wyjątkowy­mi sytuacjami.

Pola

Tak jak uczyliśmy się wcześniej, pola to sposób na prze­chowywanie poje­dynczej infor­ma­cji o obiek­cie. Ponieważ Java jest językiem staty­cznie typowanym musimy od razu powiedzieć jakiego typu jest to infor­ma­c­ja, tzn. jakiego typu dane chce­my prze­chowywać (zain­tere­sowanych, czym dokład­nie jest staty­czne typowanie i z czym to się wiąże odsyłam do Wikipedii). Tzw. deklarac­ja pola skła­da się z czterech elementów:

  1. klasy­fika­tor dostępu — klasy­fika­torom dostępu poświę­cona będzie osob­na lekc­ja w przyszłoś­ci, ten ele­ment jest opcjonalny
  2. typ pola — ele­ment wyma­gany; nazwa klasy (np. String czy Inte­ger) lub określe­nie prymi­ty­wu (np. int, long) — poza wyjątkowy­mi przy­pad­ka­mi będziemy uży­wać w tym miejs­cu wyłącznie nazw klas i jest to zale­cana praktyka
  3. nazwa pola — ele­ment wyma­gany; nazwa, pod którą dane pole będzie dla nas dostępne
  4. wartość domyśl­na — ele­ment opcjon­al­ny, może­my od razu przyp­isać wartość domyśl­ną polu (cza­sem nosi to nazwę inicjowa­nia pola); przyp­isanie wartoś­ci domyśl­nej wyma­ga napisa­nia znaku równoś­ci i poda­niu wartości

przykład­owa deklarac­ja pola wyglą­da następująco:

String imie;

Mamy więc brak klasy­fika­to­ra dostępu (jest on w tym wypad­ku domyśl­ny, ale będziemy się o tym uczyć), mamy typ danych (String) oraz nazwę pola — imie. Lin­ię kończymy śred­nikiem — jest to wyma­gane w języku Java, aby każde polece­nie kończyło się śred­nikiem. Dzię­ki temu kom­put­er wie, gdzie kończy się jed­no polece­nie, a zaczy­na następne.

Dla porów­na­nia weźmy np taką deklarację:

String imie = "nie mam imienia";

Ta deklarac­ja różni się od wcześniejszej tym, że dodal­iśmy wartość domyśl­ną (zainicjowal­iśmy pole). Jeśli nie przyp­isze­my nowej wartoś­ci do tego pola, to będzie ono miało wartość “nie mam imienia”. Zwróć uwagę, że ciąg znaków umieś­cil­iśmy pomiędzy cud­zysłowa­mi. W języku Java cud­zysłowów uży­wamy właśnie do tego — żeby powiedzieć kom­put­erowi że to jest ciąg znaków, a nie jakieś polecenie.

Poniższa tabel­ka przed­staw­ia klasy w Javie, które może­my użyć do prze­chowywa­nia konkret­nych rodza­jów infor­ma­cji (to oczy­wiś­cie zestaw­ie­nie bard­zo uproszc­zone i abso­lut­nie nie jest kom­pletne — częs­to ist­nieje sytu­ac­ja, w której potrze­bu­je­my bardziej rozbu­dowanych funkcjon­al­noś­ci niż te ofer­owane przez sam język Java np. w zakre­sie dat, w takich przy­pad­kach mamy do wyboru wiele zewnętrznych bibliotek).

Rodzaj infor­ma­cji Przykład­owa deklaracja Uwa­gi
Ciąg znaków  String imie = “Ania”;
Licz­ba całkowita  Inte­ger wiek = 15; Są klasy, które moga prze­chowywać więk­sze i mniejsze licz­by, np. Short, Long, Byte itp
Licz­ba z częś­cią ułamkową  Float waga = 10.0;  Także są klasy które mogą prze­chowywać dokład­niejsze licz­by lub inaczej je prze­chowywać, np. Dou­ble czy BigDecimal.
Data / czas  Date ter­az = new Date();  Ponieważ ta klasa nie jest w pakiecie java.lang musimy dodać tzw. import — przed ‘class XXX’ wpisujemy:
import java.util.Date;Alternatywnie IDE pod­kreśli na czer­wono deklarac­je, po najecha­niu myszką będziemy mieli dostęp­ne opc­je, w tym m.in. ‘Import java.util.Date’
Fla­ga (prawda/fałsz)  Boolean akty­wny = true;

Metody

Metody klas to sposób na wykony­wanie jakichś oper­acji, tzw. logi­ki biz­ne­sowej. To, co może­my zro­bić w meto­dach to np. odczyt pól, zapis do nich, wywołanie metod innych klas czy sprawdze­nie pewnych warunk­ów lub wyko­nanie pewnej oper­acji wiele razy. Słowem: może­my zapro­gramować dowol­ny algo­rytm (czyli sposób postępowania).

Deklarac­ja metody jest bardziej złożona, przeanal­izu­jmy ją na przykładzie:

public String powiedzCos(String coPowiedziec) {
    return "Mówię: " + coPowiedziec;
}

Podob­nie jak w przy­pad­ku pól, zaczy­namy od klasy­fika­to­ra dostępu, który jest opcjon­al­ny. Następ­nie mamy typ danych — w tym wypad­ku nie jest to jed­nak typ infor­ma­cji, którą prze­chowu­je­my (bo nie prze­chowu­je­my w meto­dach żad­nych infor­ma­cji), ale typ infor­ma­cji którą meto­da zwraca. Weźmy dla przykładu metodę obliczPodatek(Integer kosztZa­kupu); Meto­da taka powin­na ‘odpowiedzieć’ — zwró­cić do miejs­ca, w którym ją wywołano oblic­zony podatek. To jest właśnie typ zwracany metody. Jeśli meto­da nic nie zwraca (np. służy tylko do zapisy­wa­nia jakiejś infor­ma­cji), uży­wamy słowa kluc­zowego ‘void’ zami­ast typu. Na przykład:

public void zapiszDane(String dane) {
    // tutaj coś zapisujemy
}

Widz­imy kole­jny nowy ele­ment języ­ka — komen­tarze :) dwa ukośni­ki bez odstępu powodu­ją, że wszys­tko co jest za nimi do koń­ca linii jest trak­towane jako komen­tarz. Jeśli chce­my napisać komen­tarz na wiele lin­i­jek, może­my użyć kon­strukcji: /* komen­tarz */, np tak jak poniżej:

public void zapiszDane(String dane) {
    /* to
    jest
    komentarz na kilka linii */
}

Następ­nie mamy nazwę metody — obow­iązu­ją tutaj podob­ne zasady, jak w przy­pad­ku nazw pól, szczegółowo będziemy je poz­nawać w jed­nej z kole­jnych lekcji, nato­mi­ast na tą chwilę należy pamię­tać, że nie uży­wamy pol­s­kich znaków, odstępów i znaków spec­jal­nych. Może­my uży­wać cyfr, pod warunk­iem że pier­wszym znakiem nazwy jest litera.

Po nazwie w naw­iasach zwykłych mamy tzw. para­me­try — może być ich zero lub więcej. Każdy para­metr deklaru­je­my poda­jąc typ oraz nazwę. W przy­pad­ku para­metrów nie poda­je­my klasy­fika­torów dostępu. Para­me­try to sposób na przekazy­wanie infor­ma­cji do metody.

Kole­jnym ele­mentem jest tzw. ciało metody, które rozpoczy­namy otwier­a­ją­cym naw­iasem klam­rowym i kończymy ana­log­icznie, zamyka­ją­cym naw­iasem klam­rowym. W ciele metody może­my pisać polece­nia (pamię­ta­jąc, żeby każde z nich kończyć śred­nikiem) które składa­ją się na nasz program.

Jed­nym z pole­ceń, które może­my napisać jest return X; Powodu­je to wyjś­cie z metody i zwróce­nie wyniku. X to coś, co zwracamy z metody — musi być takiego typu jak zadeklarowal­iśmy przed nazwą metody (chy­ba że meto­da zwraca typ void, wtedy wystar­czy użyć: return; , aby zakończyć dzi­ałanie metody i wró­cić do miejs­ca, gdzie meto­da została uruchomiona).

Wraca­jąc do naszego przykładu:

public String powiedzCos(String coPowiedziec) {
    return "Mówię: " + coPowiedziec;
}

Meto­da ta jest pub­licz­na (o tym będziemy jeszcze się uczyli), zwraca ciąg znaków (String), nazy­wa się powiedz­Cos i ma jeden argu­ment (także ciąg znaków, który nazwal­iśmy coPowiedziec). Ciało metody zwraca to, co podal­iśmy jako argu­ment poprzed­zone frazą “Mówię:”. Poz­na­je­my zarazem kole­jny ele­ment języ­ka — tzw. oper­a­tor dodawa­nia oraz konkate­nacji (łączenia) ciągów. Pozwala on połączyć ze sobą dwa cią­gi znaków w jeden.

Powyższa meto­da wywołana np. w następu­ją­cy sposób:

String coPowiedzial = powiedzCos("lubię programować");

Spowodu­je, że w zmi­en­nej (o zmi­en­nych także powiemy sobie więcej w przyszłoś­ci) coPowiedzial będziemy mieli zapisaną wartość, którą zwró­ciła meto­da powiedz­Cos z argu­mentem “lubię pro­gramować”. Na pod­staw­ie naszej wiedzy o dzi­ała­niu tej metody, wiemy, że będzie to “Mówię: lubię programować”.

To, co musimy jeszcze wiedzieć to to, że wywoły­wanie metod moż­na zag­nieżdżać, tj. przekazać wynik wyko­na­nia jed­nej metody jako argu­ment drugiej. Np. aby wyp­isać na kon­solę zawartość pola imię, może­my w jakiejś metodzie użyć konstrukcji:

System.out.println(this.getImie());

Jest to (funkcjon­al­nie) równoważne poniższe­mu fragmentowi:

String imie = this.getImie();
System.out.println(imie);

Metody specjalne

W Javie ist­nieje kil­ka nazw metod, które mają spec­jalne znacze­nie. Dokład­nie omówie­nie tych metod pojawi eis przy okazji kole­jnych lekcji, nato­mi­ast na tą chwilę zapoz­namy się z tzw. get­tera­mi i setterami.

Get­tery i set­tery to metody, które pozwala­ją odpowied­nio pobier­ać i zapisy­wać wartość pól klasy. Dobrą prak­tyką jest uży­wanie tylko metod innych klas, a nie bezpośred­nio ich pól. Tak zbu­dowane klasy to tzw. Beany — czyli klasy które swo­je pola udostęp­ni­a­ją tylko za pośred­nictwem metod. Zale­ty takiego pode­jś­cia będziemy omaw­iać w kole­jnych lekcjach.

Przykład­owy get­ter wyglą­da następująco:

public String getImie() {
    return this.imie;
}

nato­mi­ast setter:

public void setImie(String imie) {
    this.imie = imie;
}

Na szczęś­cie IDE ułatwia nam zadanie i pozwala automaty­cznie gen­erować takie metody. Aby to zro­bić, wystar­czy umieś­cić kur­sor na nazwie pola, kliknąć prawym przy­ciskiem mysz­ki i wybrać opcję Source -> Gen­er­ate get­ters and set­ters (wtedy może­my zro­bić to automaty­cznie dla wielu pól jednocześnie).

Inicjowanie obiektów, this

Mając już stwor­zoną klasę i jej metody, może­my ją ter­az zainicjować, tzn. utworzyć obiekt, który będzie tego typu. Tworze­nie obiek­tu odby­wa się poprzez wywołanie spec­jal­nej metody, zwanej kon­struk­torem (może­my to zro­bić np. w metodzie main naszej klasy), np:

Kot kot = new Kot();

W tej lin­i­jce robimy trzy rzeczy: deklaru­je­my zmi­en­ną typu Kot o nazwie kot a także tworzymy nowy obiekt ( “new Kot()” ) oraz przyp­isu­je­my go do zdeklarowanej zmi­en­nej (uży­wa­jąc znaku równości).

Co praw­da przyp­isanie obiek­tu do zmi­en­nej nie jest konieczne, aby go utworzyć, ale w prze­ci­wnym wypad­ku mielibyśmy prob­lem, aby cokol­wiek z tym obiek­tem dalej zrobić.

Konstruktory

Kon­struk­to­ry są spec­jal­ny­mi meto­da­mi — moż­na je wywołać tylko raz dla jed­nego obiek­tu, pod­czas jego tworzenia. Kole­jne wywołanie kon­struk­to­ra spowodu­je utworze­nie nowego obiek­tu. Kon­struk­to­ry innych klas moż­na wywoły­wać tylko i wyłacznie poprzedza­jąc je słowem new — jest to infor­ma­c­ja dla Javy, że chce­my utworzyć nowy obiekt.

Kon­struk­to­ry deklaru­je­my praw­ie iden­ty­cznie, jak nor­malne metody — za wyjątkiem tego, że niemoż­na opisać typu zwracanego, a nazwa metody musi być iden­ty­cz­na jak nazwa klasy. W naszym przy­pad­ku było­by to np:

public Kot() {
    // jakieś operacje
}

Powyższy kon­struk­tor to przykład tzw. kon­struk­to­ra bezar­gu­men­towego — takiego, który nie przyj­mu­je argu­men­tów. Kon­struk­to­ry mogą przyj­mować dowol­ną ilość argu­men­tów dowol­nego typu i wykony­wać dowolne oper­ac­je — ale prze­ważnie uży­wamy ich do przekaza­nia pewnych danych do obiek­tu (argu­men­ty kon­struk­to­ra pokry­wa­ją się wtedy z pola­mi zdefin­iowany­mi w klasie). Klasa może też mieć wiele kon­struk­torów, które muszą różnić się przyj­mowany­mi argu­men­ta­mi. Wewnątrz kon­struk­torów moż­na wywoły­wać inne kon­struk­to­ry tej samej klasy podob­nie jak metody, uży­wa­jąc jed­nak zami­ast ich nazwy this().

Zas­tanaw­iasz się pewnie co się dzieję, jeśli nie zdefini­u­je­my żad­nych kon­struk­torów. W takiej sytu­acji Java sama doda­je tzw. kon­struk­tor domyśl­ny — czyli pub­liczny kon­struk­tor bezar­gu­men­towy, który nie wykonu­je żad­nej oper­acji. Co ważne, jeśli zdefini­u­je­my choć jeden włas­ny kon­struk­tor, domyśl­ny nie zostanie utworzony.

Słówko kluczowe this

Słowo to ma spec­jalne znacze­nie w Javie — w dużym skró­cie pozwala ono na odwoły­wanie się do ‘siebie samego’. Pozwala to na odwołanie się do pól i metod w sposób bezpośred­ni i czytel­ny. Słówko this nie jest stricte niezbędne — w wielu przy­pad­kach może­my je pom­inąć, ale zwięk­sza czytel­ność i w przy­pad­ku, kiedy zmi­enne lokalne lub argu­men­ty mają taką samą nazwę jak pola naszej klasy, pozwala uniknąć kon­flik­tów. Pozwala też odwoły­wać się do innych kon­struk­torów. Zobaczmy na poniższy przykład:

public class KotZKonstruktorami {

    String imie;

    public KotZKonstruktorami() {
        //konstruktor bezargumentowy
    }

    public KotZKonstruktorami(String imie) {
        this(); //tutaj wywołujemy konstruktor bezargumentowy tej samej klasy tak, jakby była to metoda
        this.imie = imie; //przypisujemy polu imie wartość zmiennej imie
        imie = imie; //błąd - przypisujemy nie do pola obiektu imie, ale do zmiennej o tej nazwie
        // ponieważ przypisujemy wartość zmiennej imie realnie nic się nie dzieje w linijce powyżej
    }
}

Jak sama widzisz, nie zawsze da się uniknąć korzys­ta­nia ze słówka kluc­zowego this. Dobrą prak­tyką jest jed­nak stosowanie go także tam, gdzie nie ma takiego wymogu — dzię­ki temu jas­no wskazu­je­my, że chodzi nam o pole kasy a nie o zmi­en­ną, co zwięk­sza czytel­ność kodu.

Zadanie

Utwórz klasę Kot . Klasa ta powin­na mieć następu­jące pola:

  • imię (ciąg znaków)
  • data urodzenia (data)
  • waga (licz­ba zmiennoprzecinkowa)
  • imię opieku­na (ciąg znaków)

Klasa ta powin­na też mieć jed­ną metodę o nazwie przed­stawSie. Meto­da ta nie przyj­mu­je żad­nych argu­men­tów i zwraca ciąg znaków który jest zdaniem zaw­ier­a­ją­cym imię kot­ka, jego datę urodzenia, wagę oraz imię opiekuna.

Podpowiedzi

Aby pokazać pod­powiedzi, kliknij link poniżej. 

pokaż pod­powiedź »
  • ciąg znaków to typ String
  • do prze­chowywa­nia daty uży­wamy klasy java.util.Date
    • pamię­ta­jmy, żeby dodać tą klasę jako import — IDE praw­dopodob­nie zro­bi to za nas
  • do prze­chowywa­nia liczb zmi­enno­przecinkowych uży­wamy typu Float

Jeżeli masz prob­lem z zadaniem z lekcji i nie wiesz, jak go rozwiązać, to sprawdź, jak sobie radz­ić — przeczy­taj o szuka­niu rozwiązań prob­lemów z kodem.

Rozwiązanie

Przeglądaj kodPobierz ZIP

Rozwiąza­nia do lekcji są dostęp­ne w ser­wisie GitHub — użyj przy­cisków po prawej aby pobrać lub prze­jrzeć kod do tej lekcji. Jeśli masz wąt­pli­woś­ci, jak posługi­wać się Git’em, instrukc­je i lin­ki zna­jdziesz w naszym wpisie na tem­at Git’a.

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!

3