autor: Ivan Kupka

Verzia 1

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Java 4

 

Tvorenie objektov, referenčné premenné a objekty

 

Cieľom kapitoly je oboznámiť sa s procesom vytvárania objektu, s procesom inicializácie statických premenných a premenných inštancie. Krátke varovanie o práci s referenčnými premennými. Implicitný balík.

 

 

Tvorenie objektov. Objekt a referenčná premenná

 Na to, aby bol objekt nejakej triedy vytvorený a aby všetky jeho členské premenné boli inicializované, je potrebná špeciálna metóda.  V Jave je touto metódou konštruktor. Táto metóda musí mať rovnaký názov, ako je názov príslušnej triedy. Ak žiaden konštruktor nevytvoríme sami, Java vytvorí implicitný konštruktor ( tvaru MojaTrieda(), t. j. – logicky – je to metóda bez argumentov ). Samozrejme, ako chceme, aby boli členské premenné novovytvoreného objektu inicializované podľa našich predstáv, musíme vhodný konštruktor, alebo viacero konštruktorov,  definovať sami.

Prezrite si nasledujúci kód a sledujte najprv definíciu konštruktorov. Potom si prezrite prvé štyri riadky z metódy main a výpis, zodpovedajúci týmto riadkom. Z príkladu vyplýva, že aj pri konštruktoroch môžeme použiť takzvané preťaženie metódy. T. j.  definujeme konštruktory s tým istým názvom, ale rôznymi  parametrami. Konštruktor sa od ostatných metód líši okrem mena ešte jednou vecou – nedeklarujeme pri ňom návratový typ.

 

 

 

Kód P4.1

 

// Objekty

 

public class Slovo {

          // ako sa pise?

          private String text;

          // ake je dlhe?

          private int dlzka;

          // prislusne gety, pripripade sety....

          public String getText(){

                    return text;

          }

          public void setText(String text){

                   

          this.text = text;      

          dlzka = text.length();

          }

         

          public int getLength(){

                    return dlzka;

          }

          // konstruktor

         

          public Slovo() {

          System.out.println("Slovo, konstruktor bez argumentov.");      

                   

          }

          public Slovo(String obsah){

                    text = obsah;

                    dlzka = text.length();

                    System.out.println("Konstruktor Slovo vytvoril slovo s textom \" " + text +

                                                        "\" s dlzkou " +dlzka);

          }

         

          public String napisOdzadu(){

                    StringBuffer otoc = new StringBuffer(text);

                    otoc = otoc.reverse();

                    return otoc.toString();

          }

         

          public String toString(){

            return text;

          }

         

         

 

          public static void main(String[] args){

                    Slovo  pes = new Slovo("bernardin");

                    Slovo dravec = new Slovo("tiger");

                    System.out.println(dravec);

        System.out.println(pes);

    System.out.println("dlzka slova " + pes

                       + " je " + pes.getLength());

 

 

    // referencna premenna pes zacne ukazovat na iny objekt

    pes = dravec;

    System.out.println("dlzka slova " + pes

                                       + " je " + pes.getLength());

       // pozor, ked sa zmeni "dravec", zmeni sa aj "pes"

    // ide o jeden a ten isty objekt

    dravec.setText("lev");

        System.out.println("dlzka slova " + pes

                  + " je " + pes.getLength());

       // dalsia zmena  aky bude vytlacenz text?

    pes = new Slovo();

    System.out.println("dlzka slova " + pes

                  + " je " + pes.getLength());

 

          }//EOMain

}//EOClass

         

 

Výpis z programu:

Konstruktor Slovo vytvoril slovo s textom " bernardin" s dlzkou 9

Konstruktor Slovo vytvoril slovo s textom " tiger" s dlzkou 5

tiger

bernardin

dlzka slova bernardin je 9

dlzka slova tiger je 5

dlzka slova lev je 3

Slovo, konstruktor bez argumentov.

dlzka slova null je 0

 

  Počas písania programu, počas práce s referenčnou premennou treba mať stále na pamäti, na aký objekt premenná ukazuje. Na jeden a ten istý objekt môže ukazovať viacero premenných. Ak sa objekt akýmkoľvek zásahom zmení, zmenu zbadáme, nech ho voláme ktoroukoľvek referenčnou premennou, ktorá naň ukazuje.

Na ilustráciu tohto faktu si pozrite si záver metódy main v predchádzajúcom kóde a takisto výpis z programu. Interpretujte jednotlivé výpisy.

 

Výhody práce s objektami

 Práca s objektami umožňuje vačšiu pružnosť, než len práca s lineárnym kódom“ ktorý sa opiera o dopredu definované funkcie, podprogramy.  V kóde P3.5  sa písal krátky príbeh pomocou metód. Pozrime sa na podobné písanie príbehu pomocou objektov.  Tentoraz budeme pracovať až s dvoma triedami. Kód P4.1 a Kód P4.2 umiestnite do príslušných súborov Slovo.java  a  .java  a umiestnite ich do osobitného, spoločného adresára. Tam ich oba odlaďte a spustite program NapisRozpravku.

 

 (V nasledujúcom kóde by bolo asi pre programátora pohodlnejšie pracovať s objektami typu String a nie Slovo, ale neskôr triedu slovo ešte rozvinieme a tým aj možnosti písania príbehov.)

 

Kód P4.2

public class NapisRozpravku{

         

          // povodne nastavenie

          Slovo[] podstatneMena = new Slovo[] {new Slovo("Princ"),new Slovo("Laktibrada"),new Slovo("Tarzan"),new Slovo("Popolvar" )};

           Slovo[] prislovky  = new Slovo[]{new Slovo("pomaly"), new Slovo("udatne"),new Slovo("ospalo"),new Slovo("vynachadzavo"),new Slovo("dovtipne")};

           Slovo[] slovesa = new Slovo[]{new Slovo("zaspal"),new Slovo("zvolal"),new Slovo("zaspieval"),new Slovo("bojoval"),new Slovo("obedoval")};

           Slovo[] zaverecnyZnak = new Slovo[] {new Slovo("."),new Slovo("?"),new Slovo("!"),new Slovo("...")};

          // prislusne gety

          /*

          public Slovo[] getPodstatneMena(){

            return podstatneMena;  

          }

  

   public Slovo[] getPrislovky(){

            return prislovky; 

    }

  

   public Slovo[] getSlovesa(){

            return slovesa;    

    }

 

    public Slovo[] getZaverecnyZnak(){

            return zaverecnyZnak;  

    }*/

         public NapisRozpravku(){

         

         }

 

          // konstruktor, ktory nastavenie upravi

          public NapisRozpravku(Slovo[] podst, Slovo[] prisl,Slovo[] sloves, Slovo[] znak){

          System.out.println("Hlasi sa konstruktor triedy \" Napis rozpravku\"");         

          podstatneMena = podst;

          prislovky = prisl;

          slovesa = sloves;

          zaverecnyZnak = znak;   

                   

          }

         

          // metoda, ktora vytlaci nahodne 1 prvok z pola typu Slovo[]

          public void tlacNahodne(Slovo[] slova){

            int dlzka = slova.length;

            int nahodnyIndex = (int)(Math.random()*dlzka);

            System.out.print(slova[nahodnyIndex]);

                   

          }

          // metoda, ktora vytlaci rozpravku

   public void pis(){

        System.out.println();                 

        tlacNahodne(podstatneMena);

        System.out.print(" ");

        tlacNahodne(prislovky);

        System.out.print(" ");

        tlacNahodne(slovesa);

        tlacNahodne(zaverecnyZnak);

        System.out.println( "\n");

       }

 

                   

           

          public static void main(String[] args){

                    NapisRozpravku bard = new NapisRozpravku();

                    bard.pis();

                    bard = new NapisRozpravku(new Slovo[]{new Slovo("Snehulienka"),new Slovo("Popoluska")}, new Slovo[]{new Slovo("ladne"),new Slovo("spanilo")}, new Slovo[]{new Slovo("priadla"),new Slovo("zametala")}, new Slovo[]{new Slovo(".")});

                    bard.pis();

          }//EOMain

}//EOClas

         

 

Jeden z možných výstupov programu je:

 

Konstruktor Slovo vytvoril slovo s textom " Princ" s dlzkou 5

Konstruktor Slovo vytvoril slovo s textom " Laktibrada" s dlzkou 10

Konstruktor Slovo vytvoril slovo s textom " Tarzan" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " Popolvar" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " pomaly" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " udatne" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " ospalo" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " vynachadzavo" s dlzkou 12

Konstruktor Slovo vytvoril slovo s textom " dovtipne" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " zaspal" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " zvolal" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " zaspieval" s dlzkou 9

Konstruktor Slovo vytvoril slovo s textom " bojoval" s dlzkou 7

Konstruktor Slovo vytvoril slovo s textom " obedoval" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " ." s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " ?" s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " !" s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " ..." s dlzkou 3

 

 

Tarzan udatne zvolal...

 

 

Konstruktor Slovo vytvoril slovo s textom " Snehulienka" s dlzkou 11

Konstruktor Slovo vytvoril slovo s textom " Popoluska" s dlzkou 9

Konstruktor Slovo vytvoril slovo s textom " ladne" s dlzkou 5

Konstruktor Slovo vytvoril slovo s textom " spanilo" s dlzkou 7

Konstruktor Slovo vytvoril slovo s textom " priadla" s dlzkou 7

Konstruktor Slovo vytvoril slovo s textom " zametala" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " ." s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " Princ" s dlzkou 5

Konstruktor Slovo vytvoril slovo s textom " Laktibrada" s dlzkou 10

Konstruktor Slovo vytvoril slovo s textom " Tarzan" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " Popolvar" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " pomaly" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " udatne" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " ospalo" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " vynachadzavo" s dlzkou 12

Konstruktor Slovo vytvoril slovo s textom " dovtipne" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " zaspal" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " zvolal" s dlzkou 6

Konstruktor Slovo vytvoril slovo s textom " zaspieval" s dlzkou 9

Konstruktor Slovo vytvoril slovo s textom " bojoval" s dlzkou 7

Konstruktor Slovo vytvoril slovo s textom " obedoval" s dlzkou 8

Konstruktor Slovo vytvoril slovo s textom " ." s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " ?" s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " !" s dlzkou 1

Konstruktor Slovo vytvoril slovo s textom " ..." s dlzkou 3

Hlasi sa konstruktor triedy " Napis rozpravku"

 

 

Snehulienka ladne priadla.

 

 

Process Exit...

 

Implicitný balík

  Metóda main() v triede NapisRozpravku „pozná“ triedu Slovo preto, lebo sú v spoločnom adresári. Tvoria takzvaný implicitný balík.

Pre túto metódu main() už nie sú zaujímavé výpisy o tvorení jednotlivých slov. Zakomentujte preto pomocné tlačenia, obsiahnuté v konštruktore triedy Slovo a tento  kód dajte preložiť. Znovu spustite program main z triedy NapisRozpravku. Potom urobte ďalší pokus: premiestnite preložený súbor Slovo.class do iného adresára. Spustite program NapisRozpravku. Čo sa stane?

(java.lang.NoClassDefFoundError: Slovo

          at NapisRozpravku.<init>(NapisRozpravku.java:4)

          at NapisRozpravku.main(NapisRozpravku.java:59)

Exception in thread "main" Process Exit...)

 

Teraz najprv preložte NapisRozpravku, potom tento program spustite. Čo sa stane?

Nakoniec odstráňte z adresára aj súbor Slovo.java aj súbor Slovo.class. Preložte NapisRozpravku. Aká je reakcia prekladača?

Ďalší pokus: konštruktor v triede Slovo

public Slovo(String obsah) premenujte na

private Slovo(String obsah)

Preložte Slovo. java a spustite Slovo. Výstup je taký istý ako predtým. Preložte NapisRozpravku:

Ako zareagoval prekladač?

(

NapisRozpravku.java:4: Slovo(java.lang.String) has private access in Slovo

          Slovo[] podstatneMena = new Slovo[] {new Slovo("Princ"),new Slovo("Laktibrada"),new Slovo("Tarzan"),new Slovo("Popolvar" )};

                                             ^

Prepíšte konštruktor v triede Slovo na

protected Slovo(String obsah) a znovu preložte a spustite najprv Slovo, potom NapisRozpravku. Oba programy zbehnú, lebo NapisRozpravku je v spoločnom (implicitnom) balíku s triedou Slovo.

(Viac Eckel str. 216-217  o kľúčovom slove protected a dedičnosti, my sa ku slovu protected  vrátime pri preberaní dedičnosti ).

Proces inicializácie

V nasledovnom kóde sa pozrieme na to, ako prebieha proces inicializácie statických premenných, premenných inštancie a v akom poradí sa vytvára  objekt, keď je zavolaný konštruktor.

 

Kód P4.3

 //nahodny cinziak lezi v radovej zastavbe, na nahodnom sidlisku

// vsetky cinziaky tam maju nahodne vybrany, ale jednotny podorys

// lisia sa vyskou a tym, ci maju vytah, alebo nie

 

public class NahodnyCinziak{

         

          // konstruktor

          public NahodnyCinziak(){

          System.out.println("Tu konstruktor: vytvaram nahodny cinziak s " +

                                                        "minimalnou sirkou." + minimalnaSirka);

          }//EndOFKonstruktor

          //staticke premenne - premenne triedy:

          public  static final  double minimalnaSirka = 8.0;

          public static double sirka;

           public static double dlzka;

           // premenne instancie:

           public int pocetPoschodi;

           public double vyska;

           public boolean maVytah = false;

           

           

          // inicializacny blok

          {

                System.out.println("\n Zacina pracovat inicializacny blok");

          System.out.println("pocetPoschodi pred inicializaciou: " +                          pocetPoschodi);

       pocetPoschodi = 1 + (int)(Math.random()*30);

       System.out.println("Pocet poschodi: " +pocetPoschodi);

       vyska = 0.5 + pocetPoschodi*2.3;

       System.out.println("vyska: " + vyska);

       if(pocetPoschodi > 3) {

          maVytah = true;

       }

       System.out.println("Ma vytah: " + maVytah);

 

          }

          //staticky inicializacny blok:

          static {

          System.out.println("\n Zacina pracovat staticky inicializacny blok);                System.out.println("Sirka vsetkych cinziakov pred inicializaciou: " + sirka);

                    sirka = minimalnaSirka +  Math.random()*10;

                    System.out.println("Sirka vsetkych cinziakov: " + sirka);

                     dlzka = sirka + Math.random()*20;

                     System.out.println("Dlzka vsetkych cinziakov: " + dlzka);

          }

 

    

           public static void main(String[] args){

System.out.println("\n Spustam metodu main v triede Nahodny cinziak");

        System.out.println("\n Idem vytvorit prvy cinziak:");

       NahodnyCinziak n = new NahodnyCinziak();

       System.out.println("\n Idem vytvorit druhy  cinziak:");

                    NahodnyCinziak m = new NahodnyCinziak();

           }//EOMain

}//EOClass

 

 

 Jeden z výstupov programu:

 

 Zacina pracovat staticky inicializacny blok

Sirka vsetkych cinziakov pred inicializaciou: 0.0

Sirka vsetkych cinziakov: 9.180804686437227

Dlzka vsetkych cinziakov: 23.577058346934585

 

 Spustam metodu main v triede Nahodny cinziak

 

 Idem vytvorit prvy cinziak:

 

 Zacina pracovat inicializacny blok

pocetPoschodi pred inicializaciou: 0

Pocet poschodi: 3

vyska: 7.3999999999999995

Ma vytah: false

Tu konstruktor: vytvaram nahodny cinziak s minimalnou sirkou.8.0

 

 Idem vytvorit druhy  cinziak:

 

 Zacina pracovat inicializacny blok

pocetPoschodi pred inicializaciou: 0

Pocet poschodi: 25

vyska: 57.99999999999999

Ma vytah: true

Tu konstruktor: vytvaram nahodny cinziak s minimalnou sirkou.8.0

Process Exit...

 

 Stručne o poradí inicializácie

  Z predchádzajúceho príkladu vyplýva, stručne zhrnuté, takéto poradie inicializácie: 

Ešte predtým, ako je spustený príslušný inicializačný blok, dané premenné majú priradené implicitné hodnoty: Pre číselné premenné sú to nuly. Potom,  sa nastavia statické premenné, prípadne prebehne statický inicializačný blok (iba raz, pri  pri prvom volaní triedy). Pri zavolaní konštruktora sa najprv nastavia hodnoty referenčných premenných, prebehne inicializačný blok – ak je nejaký definovaný, potom sa vykoná zvyšok práce konštruktora.

  Presne a podrobne o tomto procese informuje Bruce Eckel. Citujme ho:

    It’s helpful to summarize the process of creating an object. Consider a class called Dog:

 

The first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath. 

As Dog.class is loaded (creating a Class object, which you’ll learn about later), all of its static initializers are run. Thus, static initialization takes place only once, as the Class object is loaded for the first time. 

When you create a new Dog( ), the construction process for a Dog object first allocates enough storage for a Dog object on the heap. 

This storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values (zero for numbers and the equivalent for boolean and char) and the references to null. 

Any initializations that occur at the point of field definition are executed. 

Constructors are executed. As you shall see in Chapter 6, this might actually involve a fair amount of activity, especially when inheritance is involved.

Koniec citátu.

 

Trieda String

Preskúmajte jej dokumentáciu. Podobne u triedy Integer a Boolean.

 

Cvičenia 4

 

Cv4.1

Konštruktor triedy môže vo svojom vnútri volať ľubovoľnú metódu triedy, teda aj iný konštruktor. Presvedčte sa o tom vytvorením a spustením vhodného kódu.

 

Cv4.2 dva malé hlavolamy

  Už sme sa oboznámili s varovaním, týkajúcim sa práce s referenčnými premennými a s objektami. Overte si, či ste toto varovanie správne pochopili:

I.

V Kóde P4.1 zameňte v metóde main riadok:

pes = new Slovo();

za riadok:

dravec = new Slovo();

Inak nechajte kód bez zmeny. Čo sa stane s výpisom programu? Zmení sa? Odôvodnite svoju odpoveď, potom pozmenený program spustite a presvedčte sa, či ste uhádli správne.

II.

V Kóde P4.1 zakomentujte nasledujúci text:

 

public Slovo() {

          System.out.println("Slovo, konstruktor bez argumentov.");      

                   

          }

Spustite prekladač. Čo sa stalo? Viete vysvetliť reakciu prekladača? Nie je v rozpore s tým, čo sme si hovorili o vytvorení implicitného konštruktora?

 

Cv4.3

a) V triede NahodnyCinziak  zakomentujte všetky príkazy v metóde main(). Kód preložte a spustite.

Aký bude podľa Vás výstup z programu?

b)Vytvorte triedu NahodnyCinziak2, okopírujte do nej všetok kód z triedy

NahodnyCinziak.

V kóde o činžiakoch pridajte pred triedu main definíciu novej metódy,

 S názvom „ public static double nahodneCislo(double odkial,double pokial) “. Táto metóda má ako výstup náhodné číslo v intervale <odkial, pokial).

Upravte teraz statický inicializačný blok a inicializačný blok tak, že budú pri generovaní náhodných čísel v maximálne možnej miere používať služby

Metódy nahodneCislo. Kód spustite a presvedčte sa, že aj inicializačný blok môže používať služby metódy, definovanej v triede. Koniec koncov, nie je to v tomto príklade až také prekvapivé, vedeli by ste povedať prečo?

Premenujte teraz metódu na

public  double nahodneCislo(double odkial,double pokial)

a dajte kód preložiť. Všimnite si reakciu prekladača.

c) Urobte zmeny v kóde tak, aby ste zistili, aké sú hodnoty, priradené premenným typu boolean a String pred inicializáciou.

Cv4.4

Vytvorte krátky program, v ktorom využijete objekty a metódy triedy String, Integer, Boolean.

Preveďte pritom Stringové reprezentácie do objektov Integer, Boolean a opačne.

Riešenia

Cv.4.2

Implicitný konštruktor bez argumentu Java vytvorí, AK sme my nevytvorili žiaden konštruktor. V opačnom prípade implicitný konštruktor nevytvára.

Cv.4.3

a) Statický inicializačný blok sa spustí.

b) Používanie metód zvonka nie je čudné preto, lebo pôvodne inicializačný blok používal služby metódy random(), definovanej dokonca v inej triede – v triede Math. A tiež služby metódy println(), definovanej pre objekt „out“ triedy „System“ .

c) Do inicializačného bloku dajte vypísať hodnotu premennej maVytah ešte pred jej inicializáciou.  Zaveďte v triede statickú premennú typu String „nahodneMenoSidliska“ a pred jej inicializáciou ju dajte vypísať.

d) Urobte podobný pokus s premennou typu char.

Cv4.4

Využite okrem iného metódy valueOf(). Pozor, sú statické.

© Ivan Kupka

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Vaše komentáre: kupka@fmph.uniba.sk