2014. szeptember 29., hétfő

Mi van ha valami probléma közbeszól? - Kivételkezelés

Ahogy haladunk tovább a nyelv alapelemeinek megtárgyalásával, egyre jobban fogjuk tudni specializálni, szebbé, pontosabbá tenni az osztályainkat és a kódunkat.
Most azt vizsgáljuk meg hogy mi van akkor ha valami előre nem látott probléma, hiba közbejön? Hogyan kezeljük le ezeket a kivételes eseteket?

Erre a problémára ad megoldást a kivételkezelés. A kivételeket le tudjuk kezelni a program futása közben, amennyiben felkészítjük a kódunkat arra, hogy a gyanús esteket figyeljék. Ilyen eset lehet például egy fájl megnyitásakor az hogy van-e egyáltalán engedélyünk arra, hogy a fájlhoz hozzáférjünk, létezik-e egyáltalán az adott fájl stb. Ilyenkor a kritikus műveleteket egy megfelelő szintaktikai elemmel közre tudjuk fogni és ha hiba keletkezik, akkor azt el tudjuk kapni és le tudjuk kezelni.
A nyelvi eszköz erre az úgynevezett try-catch-finally blokk. A try után álló utasításblokkba kell foglalni a kritikus utasításokat, melyek kivételt generálhatnak (kivétel "dobhatnak"). A try blokk után azonnal következnie kell legalább egy catch blokknak. Ugyanis ha kivétel generálódik a try blokkban akkor a létrejövő kivételobjektum átadódik a catch blokknak és ott szépen le tudjuk kezelni a problémát. Több catch blokk is lehet, ugyanis többféle kivétel (Exception) létezik és mi magunk is tudunk definiálni ilyeneket.
A catch blokk(ok) után következhet egy finally blokk (nem kötelező). Ez a blokk minden esetben megkapja a vezérlést, akár volt kivétel akár nem, akár elkaptuk akár nem. Ebben szokás erőforrásokat felszabadítani, fájlokat lezárni stb....
Amikor valami rendellenes probléma kivétel dob (throw) egy metódus futása közben, akkor létrejön kivétel objektum (Exception) ami információt hordoz a hibáról, a futtató környezetről és a programunk állapotáról. Ha hiba keletkezik akkor a futtató környezet elkezd keresni először az adott metódusban, majd az őt hívó metódusban (ha van ilyen) stb, egy olyan kivétel kezelő blokkot ami le tudja kezelni a keletkezett kivételt. Ha nem talál ilyet akkor a program végrehajtása megszakad és a JVM kapja meg a kivételt, ami ki is írja a problémát (de a programunk nem fut tovább). Ezért inkább próbáljunk meg felkészülni és elkapni a kivételeket.

Még nem beszéltünk a Java standard gyári csomagokról. Egyenlőre annyit kell tudni róluk, hogy különféle problémákra léteznek gyári megoldások, melyek segítenek a programozói munkába. Ezek kb a procedúrális nyelvek rutinkönyvtárainak felelnek meg, csak itt egy osztályhierarchiáról van szó. Nézzük ennek egy szeletét, mégpedig a kivételek (Exception) hierarchiát:


A fenti diagramból látható hogy milyen sokféle kivétel lekezelésére tud objektum generálódni.
Mi magunk is tudunk generálni kivételt a throw utasítással. Ilyenkor meg kell mondanunk hogy milyen kivételt akarunk dobni.

Most nézzünk erre egy példát.

Tegyük fel, hogy a Haromszog osztályunkat szeretnénk úgy módosítani, hogy a konstruáláskor ellenőrizzük hogy a megadott oldalhosszakkal lehet-e egyáltalán háromszöget rajzolni.

Ehhez ismét hívunk egy matematikust szakértőnek, aki elárulja hogy egy háromszög akkor létezik, ha bármely két oldalának összege nagyobb a harmadik oldalnál. Tehát ha nem teljesül a a>b+c és b>a+c és c>a+b ellenőrzés akkor kivételt generálunk, így nem jön létre a Haromszog objeltum.

Írjuk át a konstruktor a következőképp:

    public Haromszog(double a, double b, double c){  
       if ((a<b+c) && (b<a+c) && (c<a+b)) {  
          this.a=a;  
          this.b=b;  
          this.c=c;  
       } else {  
          throw new IllegalArgumentException("Ezekkel az oldalhosszakkal nem rajzolható háromszög ("+a+"; "+b+"; "+c+")");  
       }  
    }  
   

Így már csak akkor inícializálódnak az objektum tulajdonságai (oldalhosszai) ha szerkeszthető háromszög a megadott adatokkal. Ha nem akkor dobunk egy IllegalArgumentException kivételt, amit a tesztelő programunkban a következőképp tudunk elkapni:

1:  public class SikidomTest {  
2:    
3:     public static void main(String[] args) {  
4:       
5:        Haromszog h1=null;  
6:        try {  
7:           h1=new Haromszog(2, 3, 6);  
8:        } catch(IllegalArgumentException e) {           
9:           System.out.println(e.getMessage());  
10:        }  
11:          
12:        if (h1==null) {  
13:           System.out.println("A háromszög nem létezik!");  
14:        } else {  
15:           System.out.println("A háromszög létezik! Területe: " + h1.terulet());  
16:        }  
17:          
18:     }  
19:    
20:    

A 6 - 10 sorok között látható a kivételkezelés szintaktikai szerkezete. A fenti példa eredményeként nem fog létrejönni az objektum, így null érték lesz a referencia, amivel a későbbiekben vezéreljük a kiíratást.


2014. szeptember 26., péntek

Tovább az OO elvek mentén - Absztrakció, általánosítás, származtatás

Úgy tűnik hogy ez a Haromszog osztály nem lesz soha igazán objektum-orientált... Már egyszer újraszerveztük és még mindig nem az igazi. De mi is ennek az oka, hol rontottuk el?

Az objektum-orientált programozás alapelveit már tárgyaltam, most nézzük meg hogy hogyan is kell nekiállni egy OO programnak. Először is el kell gondolkodni a problémán és a szükséges mértékig (meg egy picit azon túl) általánosítani kell.
Megcsináltuk a Haromszog osztályunkat, aminél elkövettük azt a hibát, hogy egyből belefogtunk a kódolásba és nem igazán gondoltuk át, hogy mit is szeretnénk. Most ezért tervezzük újra az egész programot, ami két háromszögről megállapítja melyiknek nagyobb a kerülete.

A háromszög az objektum amivel dolgozunk ezt próbáljuk általánosítani. Ennek kapcsán általánosan mondhatjuk hogy a háromszögek azok síkidomok. A síkidomoknak van kerülete és területe. Többféle síkidom létezik (vannak pl a négyszögek, kör stb), ráadásul a háromszögeknek is vannak speciális esetei (derékszögű, egyenlő oldalú stb...). Miután ezt végiggondoltuk máris egy hierarchia jelenik meg a képzeletünkben aminek a csúcsán ott van a síkidom és abból származik az összes egyéb más előbb említett objektum.
Javában pontosan ezt a hierarchiát kell megcsinálnunk és a fa ágain haladva egyre specializáltabbá tennünk, kibővíteni, felülírni az egyes újabb szintek tulajdonságait, metódusait. A nyelv támogatja is ezt a fajta megvalósítást.

Definiáljuk először a Sikidom osztályt. Az osztályból példányosítani nem fogunk, csak általánosan fogalmazzuk meg benne ami egy síkidomra jellemző. Az ilyen osztályokat, amikből hiányoznak a konkrétumok absztrakt osztályoknak nevezzük és az abstract kulcsszóval definiáljuk. Ezek az osztályok nem példányosíthatók, csak a leszármazottakban lehet örököltetni a saját metódusait, amit majd ott fog kelleni konkretizálni. A példában a kerület és terület számítás minden síkidomra értelmezett, de hogy konkrétan hogyan kell kiszámolni azt majd az adott síkidom definiálásakor adjuk meg.

public abstract class Sikidom {
   public abstract double kerulet();
   public abstract double terulet();
}

Ezzel megadtuk a hierarchia csúcsán lévő absztrakt osztályt, amiből tovább szervezzük, specializáljuk a síkidomokat.

Hozzuk létre a háromszögek osztályt származtatással. Ez már nem absztrakt osztály lesz, mert pontosan fogjuk tudni az adatait és a metódusait is ki tudjuk fejteni. Tudni kell még azt, hogy az absztraktnak definiált metódusokat a leszármazottakban definiálni kell, különben a fordító hibát fog jelezni!

public class Haromszog extends Sikidom {
   private double a, b, c;
   
   public Haromszog(int a, int b, int c){
      this.a = a;
      this.b = b;
      this.c = c;
    }

   // absztrakt metódusok definiálása
   public double kerulet(){
      return a+b+c;
   }

   public double terulet(){
      //Heron képlet alapján a terület kiszámítása
      double s = this.kerulet()/2
      return Math.sqrt(s*(s-a)*(s-b)*(s-c)); 
   }
}

Megjegyzés: a feladat matematikai megoldásának ismerete nem programozói feladat, azaz nem jó vagy rossz programozó aki nem tudja hogy esetünkben hogyan kell kiszámítani a háromszög területét (valós helyzetben nem a programozónak kell tudni hogy hogyan működnek a pénzügyik/könyvelési folyamatok amikhez éppen szoftvert készít). Ezeket a dolgokat a rendszer tervezésekor a rendszerszervezőnek kell feltárnia. Természetesen alapvető tájékozottság elengedhetetlen, főként az algoritmusok, matematikai lehetőségek terén.

Az osztályhierarchiát tovább lehet bővíteni a többi síkidom definíciójával (kör, téglalap, négyzet stb):

public class Kor extends Sikidom{
   private double r;

   //konstruktor
   public Kor(double r){
      this.r = r;
   }

   // absztrakt metódusok definiálása
   public double kerulet(){
      return 2*r*Math.PI;
   }

   public double terulet(){
      return 4*r*r*Math.PI; 
   }
}


Az így elkészített osztályhierarchia már sokkal inkább átgondolt és logikus, a való világ egy részének absztrakciója, amit objektum-orientált szemlélettel terveztünk meg és implementáltuk Java programként.

A következő cikkben egy picit átgondoljuk hogy mi akkor a teendő, ha valami probléma van az adatokkal, hogyan tudjuk kezelni az ilyen eseteket.

2014. szeptember 24., szerda

Projektek, fordítási egységek, fordítás, futtatás

Nézzük most a háromszög példaprogramunk mentén azt, hogy hogyan is szervezzük a projektet, hogy utána futtatható programot kapjunk.
Először is a projekt több fájlból állhat. Minden osztálydefiníciót külön fájlba kell tenni és ha két osztály van akkor már két fájlt kell készíteni.
Korábban már volt szó róla hogy a JVM amikor betölt egy osztályt, akkor megnézi van e neki public static void main(String[] args) metódusa. Ugyanis ennek - és csak ennek - adja át a vezérlést, így tud elindulni egy Java program. Ha ilyen nincs, akkor hibaüzenettel leáll a JVM.

Tehát ha a Haromszog osztályunkat futtatni akarjuk akkor kell valahol egy main() metódus hogy azt futtatni tudjuk. Ezért készítsünk egy futtató programot (osztályt) aminek fő feladata az lesz hogy a main() metódusán keresztül elindul a program. Ez gyakorlatilag ismét egy osztálydefiníció (Java-ban minden osztály) lesz. A példánknál maradva két háromszög objektumot ha kezelni szeretnénk (mondjuk meghatározni hogy melyiknek nagyobb a kerülete) ahhoz egy olyan futtató osztályt kell definiálni, aminek van main() metódusa és lehet hogy egyetlen adattagja sincs.
Tehát a projektünk két .java kiterjesztésű fájlt fog tartalmazni. A Haromszog.java és a HaromszogTeszt.java fájlokat. Ezeket ha a fájlstruktúrában ugyanazon könyvtárba tesszük, akkor mindenféle trükk nélkül megtalálják egymást, azaz nem kell importálni semmit sem.

A Haromszog.java fájl tartalma a következő legyen:

public class Haromszog {
  private int a;
  private int b;
  private int c;

  //konstruktornál nem szabad megadni visszatérési típust

  public Haromszog(int o1, int o2, int o3){
    a=o1;
    b=o2;
    c=o3;
  }

  public int kerulet(){
    return a+b+c;
  }

  public void nagyit(float zoom){
     a = a * zoom;
     b = b * zoom;
     c = c * zoom;
  }  }


A HaromszogTeszt.java tartalma:

public class HaromszogTeszt {

  public static void main(String[] args){
    Haromszog h1 = new Haromszog(3,4,5);
    Haromszog h2 = new Haromszog(3,5,5);
    if (h1.kerulet()>h2.kerulet()){     
        System.out.println("H1 kerülete nagyobb");
    } else if(h1.kerulet()>h2.kerulet()) {
        System.out.println("H2 kerülete nagyobb");
    } else {
        System.out.println("A kerületek egyenlőek");
    }  
}

Mint láthatjuk a tesztprogramban példányosítjuk a Haromszog osztály két objektumát, majd elvégezzük az összehasonlító és kiírató műveleteket.

Fordításkor a HaromszogTeszt.java fájlt kell fordítanunk a következő paranccsal:

javac HaromszogTeszt.java

Mivel a Haromszog osztályt használja, megnézi hogy van-e olyan import könyvtár, ami tartalmazza a Haromszog osztály definícióját (az import később kerül tárgyalásra). Mivel nincs ezért megnézi van-e az aktuális könyvtárban Haromszog.java fájl. Mivel ott kell lennie ezért lefordítja azt, ezek után már fogja tudni használni a Haromszog osztályt.

A fordítás sikeres lefutása után a projektünk könyvtárában ott kell lenni a két .java fájl mellett két .class fájlnak is. Ezek után a tesztosztályt betöltetjük a JVM-el és végrehajtódik a programunk (figyelem! Csak az osztály nevét kell megadni paraméterül):

java HaromszogTeszt

A program pedig kiírja hogy melyiknek nagyobb a kerülete.

A fordításról és a futtatásról, valamint a fájlokról ennyit kellett elmondani. 

A következőkben tovább finomítjuk a Haromszog osztályunkat és még objektum-orientáltabbá tesszük az egész projektet.

2014. szeptember 23., kedd

A Java programozási nyelv - Láthatósági szintek

Az előző "A Java programozási nyelv - Osztályok, objektumok" című cikkben létrehoztunk osztályokat, amiből példányosítottunk objektumot és használtuk azt. A cikk végén írtam hoyg a péládk sok sebből véreznek és az alapvető OO elveket is megsértik.
Most ezeknek az elveknek a rendbetétele következik, azaz megnézzük hogy hogyan tudunk minden OO elvnek megfelelően osztályokat létrehozni Java-ban.

A következő osztálydefiníciót készítettük arra a célra hogy egy háromszög objektumot tárolni és kezelni tudjunk.

class Haromszog {
  int a;
  int b;
  int c;

  int kerulet(){
    return a+b+c;
  }
}

A példaprogramban pedig közvetlenül adtunk értéket a három tulajdonságnak.

Az OO elvek szerint ez megsérti az egységbezárás elvét, azaz azt, hogy az objektum tulajdonságai (azaz az objektum állapota) csakis magára az objektumra tartozik, azokat csakis ő kezelheti. ez azt jelenti hogy a tulajdonságok értékei közvetlenül nem módosíthatók (pl h1.a=3 nem támogatott).
Ahhoz hogy letilthassuk az adattagok elérését be kell tudnunk állítani azt, hogy az adott adattagnak milyen a láthatósága.
A Java négyféle hozzáférési kategóriát ismer:
Nyilvános (public) : mindenhonnan elérhető
Rejtett (private): nem látható kívülről, csak az adott osztályon belül lehet elérni.
Védett (protected): az adott osztály és annak leszármazottai érhetik el.
Csomagszintű : ha nem adunk meg hozzáférést akkor csomagszintű lesz, azaz az ugyanabban a könyvtárban lévő osztályok láthatják.

Ezek alapján boncolgassuk tovább a példánkat, a Haromszog osztályt. Egyből tegyük priváttá az adattagokat a private módosítóval. Igen ám, de akkor nem fogjuk tudni elérni ezeket az adattagokat. Ez így van rendjén, éppen ezt akartuk, de akkor hogyan adjunk értéket nekik? Természetesen valamilyen metódussal amit viszont nyilvánosnak fogunk megadni.
Gondoljunk egy picit bele a példába. Egy háromszöget amikor a valóságban mondjuk megszerkesztünk, akkor a létrehozáskor adjuk meg az oldalhosszait. Ezek nem fognak a későbbiekben változni (alapesetben). Ez azt jelenti hogy a létrehozáskor, azaz a konstruktor hívásakor már ismerni kell a háromszög oldalait. Nos csináljuk ezt meg!
Az OO elvek között megtalálható a többalakúság (polimorfizmus) is. Ez azt jelenti hogy az objektum az üzenetekre (metódusokra) másként reagálhat, ha pl más paraméterezéssel hívjuk meg. Ezt hívjuk felültöltésnek (overload). Ha pl felültöltjük az alapértelmezett konstruktort és csinálunk egy olyat ami három int típusú paramétert vár, akkor máris lehetőségünk van a konstruáláskor az oldalhosszak beállítására. Azaz az objektum kezdeti állapotát be tudjuk állítani. Íme a fenti elvek szerint z átírt osztálydefiníció:

public class Haromszog {
  private int a;
  private int b;
  private int c;

  //konstruktornál nem szabad megadni visszatérési típust

  public Haromszog(int o1, int o2, int o3){
    a=o1;
    b=o2;
    c=o3;
  }

  public int kerulet(){
    return a+b+c;
  }
}

Ezek után az osztályból történő példányosítás így történik:

Haromszog h1 = new Haromszog(3,4,5);

Ezzel létrejött a h1 háromszög, aminek az adattagjai rejtettek és egy polimorf felültöltött konstruktoron keresztül inicializáltuk. A kerulet() metódust is elláttuk a public módosítóval így megadtuk pontosan az elérhetőségét.

Tegyük fel hogy el kell végeznünk különféle műveleteket, amik változtatják az objektumunk oldalainak hosszát. Például készítsünk egy nagyit() nevű metódust, azaz definiáljuk azt a műveletet, amivel meg tudjuk növelni egy háromszög oldalainak hosszát azonos arányban. A nagyít() metódus az aktuális objektumpéldányon végezze el a műveletet, tehát nem fog létrehozni új objektumot. Paraméterül adjuk át a nagyítás mértékét, ami pozitív szám kell hogy legyen.

Egészítsük ki az osztálydefiníciót az alábbi metódussal:

public void nagyit(float zoom){
   a = a * zoom;
   b = b * zoom;
   c = c * zoom;
}

Ezek után a következő programrészlettel létrehozzuk a háromszöget, majd kiíratjuk a kerületet. Utána megnöveljük duplájára és újból kiíratjuk a kerületet:

Haromszog h1 = new Haromszog(3,4,5);
System.out.println("Kerület: " + h1.kerulet());
h1.zoom(2);
System.out.println("Nagyítás után a kerület: " + h1.kerulet();


A következő cikkben megnézzük hogy hogyan is áll össze egy Java projekt. Milyen fordítási egységek vannak, milyen fájlokat kell létrehozni és azt hogyan tudjuk lefordítani és futtatni.

A Java programozási nyelv - Osztályok, objektumok

A Java nyelv legfontosabb elemét az osztályokat és az objektumokat nézzük most meg.
Azért ezek a legfontosabbak, mert osztálydefiníció nélkül nincs Java program.  Tehát hiába tudunk elemi adattípusú változókat deklarálni, meg ciklusokat szervezni, ha nincs osztály, amiben mindezt befoglaljuk. A Java-ban minden az osztálydefiníciókból indul. Az osztályok a Java nyelv alapelemei.

Egy osztályt a class kulcsszóval tudunk definiálni. A definícióban az osztály elérhetőségét is meg szoktuk adni (erről részletesebben később). Az általános szintaktika a következő:

class FirstClass {

}

Ezzel definiáltunk egy FirstClass osztályt, aminek nincs sem adattagja, sem metódusa. Van egy speciális metódusa minden osztálynak, ami automatikusan elérhető, akkor is ha nem adjuk meg. Ez a metódus a konstruktor. A szó jelentése "létrehozó". A konstruktor alapfeladata a memóriafoglalás. Ez mellett ezt ki szokjuk bővíteni a példányosításkor történő inicializálásokkal, azaz a példányosuló objektum kezdő állapotának beállításával. A FirstClass példában az alapértelmezett konstruktor memóriát foglal az objektumnak. Ennél többet ez az osztály egyenlőre nem tud, csupán példa értéke van.

Most tervezzünk meg egy osztályt, ami képes egy háromszög adatait tárolni.

class Haromszog {
  int a;
  int b;
  int c;
}

Az osztálydefiníció gyakorlatilag egy típus létrehozást jelent. Ahhoz hogy az osztályt használjuk, példányosítani kell belőle egy objektumot. Ez az osztály egy példánya lesz. A fenti példa alapján egy háromszög objektumot kapunk.
Mint írtam, a példányosításkor memóriafoglalás történik. Ahhoz hogy az objektumra hivatkozni tudjunk szükséges az úgynevezett objektumreferencia. Ezt a new operátorral lehet kinyerni.
Nézzünk egy példányosítást:

Haromszog h1 = new Haromszog();

Ezzel létrehoztunk egy h1 nevű referenciaváltozót, amibe a Haromszog osztály konstruktora által létrehozott objektumpéldány referenciáját rendeljük. Ezek után a h1 referenciaváltozón keresztül elérhetjük az objektum tulajdonságait és metódusait. Erre a . (pont) operátort használjuk. Az alábbi példában értéket adunk a háromszög tulajdonságaink (a, b, c oldal hossza), majd kiíratjuk a háromszög objektum kerületét.

h1.a = 3;
h1.b = 4;
h1.c = 5;

System.out.println("Kerület: "+h1.a+h1.b+h1.c);

Ezzel láthatjuk hogy az objektum adattagjai "működnek". Az objektumoknak nem csak adattagjai, hanem metódusai is vannak. A metódusok azok az eljárások, módszerek, amik az objektum adattagjaival végeznek műveleteket, információt adnak az objektumról.
Elsőként hozzunk létre egy olyan metódust, ami kiszámolja a háromszög kerületét.

class Haromszog {
  int a;
  int b;
  int c;

  int kerulet(){
    return a+b+c;
  }

}

Ezek után a háromszög kerülete a következő módon íratható ki. (a fentebbi példa nyomán)

System.out.println("Kerület: "+h1.kerulet());

Így már sokkal szebb. Ezzel gyakorlatilag elkészítettünk egy olyan osztályt, aminek tulajdonságai és metódusa is van. Példányosítottunk egy objektumot, majd használtuk azt.

Azt azért el kell hogy mondjam hogy ez a példa bár működik, de több sebből vérzik, keményen sérti az OO elveket és én úgy gondolom hogy a Java igazából az OOP miatt csodálatos világ. A következő cikkben tovább folytatjuk, még inkább OO elvekhez közelivé tesszük az osztályunkat.




2014. szeptember 22., hétfő

A Java programozási nyelv - ciklusszervezés (iterációk)

Az iterációk megvalósítására szintén a C/C++ megszokott eszközei állnak rendelkezésre. Az iteráció adott utasítás, vagy blokk ciklikus végrehajtása, melyet a ciklusfejben megadott kifejezés vezérel.

Két fő csoport és három konkrét megvalósítás létezik. A számláló ciklus, amit akkor célszerű használni ha megadott számszor kell végrehajtani a ciklusmagot. A másik fajta a tesztelő ciklusok, melyek adott logikai kifejezés alapján hajtódik végre vagy sem a ciklusmag.

A klasszikus iterációs utasítások közül elsőnek az elől tesztelő változatot nézzük:

while (<feltétel>) {
   utasítások;
}

Példaként nézzük a 1-100 közti számok kiíratását:

int i = 1;
while (i<=100) {
   System.out.println(i);
   i++;
}

Először kiértékelődik a feltétel, amennyiben igaz értéket kapunk, úgy végrehajtja a ciklusmagot, különben a while-t lezáró } utáni utasításra kerül a vezérlés. Figyelem! A while ciklus magja lehet hogy egyszer sem hajtódik végre, ugyanis ha első kiértékeléskor hamis lesz a feltétel akkor nem kerül a ciklusmagra a vezérlés!

Ha azt akarjuk hogy a ciklusmag legalább egyszer lefusson, akkor érdemes a hátul tesztelő ciklust választani:

do {
  utasítások;
} while (feltétel);

Nézzük a 1-100 kiírató példát:

int i = 0;
do {
      i++;
      System.out.println(i)
   }
while (i<=100)

Ebben az esetben a ciklusmag egyszer biztosan végrehajtásra kerül, majd utána kerül kiértékelésre a feltétel. Ha igaz értéket ad akkor újra a ciklusmagra kerül a vezérlés és így tovább.
A feltétel tehát a ciklusban maradás feltétele (mind elöl mind hátul tesztelő esetben. Nem minden programozási nyelvben van így)!
A while ciklusoknál mindig figyeljünk arra hogy a ciklust vezérlő feltétel hogyan és mitől változik. Pl. ha nem növeltük volna az i értékét, akkor végtelen ciklusba kerültünk volna!

A számláló ciklust a Java-ban is a for utasítással tudjuk megadni.

for(<ciklusváltozó>;<ciklusfeltétel>;<léptetés>) {
   <utasítások>;
}

Például ha ki akarjuk íratni a számokat 1-től 100-ig, akkor azt a következőképp fogalmazhatjuk meg:

for(int i = 1;i<=100;i++){
   System.out.println(i);
}

Mit látható a ciklusfejben definiáltuk a ciklusváltozót (int i). Ezzel egyértelműen megadtuk, hogy az i változó az csak és kizárólag a ciklusban használatos, a ciklusból kilépve már nem elérhető.

Egy rövid elvi értekezés következik arról, hogy gyakorlatilag a for ciklus is feltétel vezérelt és bármilyen feltételt megfogalmazhatunk a for-ban is, de azt nem arra találták ki. A for ciklust használjuk csak arra amire a jó öreg Basicben, Pascalban is használtuk. Adott számszor hajtassuk vele végre a ciklusmagot!

A ciklusok szervezésével kapcsolatosan még meg kell említeni még két utasítást.
A ciklusmagban a break utasítás hatására a vezérlés azonnal átadódik a ciklus utáni első utasításra. Tehát segítségével feltétel nélkül kiugrunk a ciklusmagból és befejezzük a ciklus végrehajtását is.
A ciklusmagban a continue utasítás hatására a vezérlés a ciklusfejre kerül és kiértékelődik a ciklusfeltétel. Tehát újra megvizsgálásra kerül hogy folytatjuk-e a ciklusmagot vagy sem.


A Java programozási nyelv - feltételes végrehajtás (szelekciók)

A Java a strukturált programozási nyelvekben elérhető vezérlési szerkezetekkel rendelkezik. A strukturált programozás alap vezérlési szerkezetei a szekvencia, a szelekció és az iteráció.

A szekvencia a különféle utasítások, utasítás blokkok sorban történő végrehajtása. Az utasításokat a Java-ban is a ; (pontosvessző) karakter zárja. Több utasítást a { és } jelek között tudunk egy utasításblokkba foglalni.

A szelekció valamilyen feltétel szerinti vezérlésátadást valósít meg.
Az if-then-else szerkezet Java-ban is létezik. Általános szintaktikája:

if (logikai kifejezés) 
   <utasítás, vagy utasítás blokk ha a kifejezés igaz>
else
   <utasítás, vagy utasítás blokk ha a kifejezés nem igaz>

Az else rész elhagyható.

A logikai kiértékelésnek van egy a C nyelvben is megvalósított, háromoperandusú kifejezés formája.

<feltétel> ? <kifejezés ha igaz> : <kifejezés ha hamis>;

Nézzünk egy példát! Ha x és y megegyezik, akkor a szorzatuk, különben az összegük legyen az eredmény:

z = ((x==y) ? x*y : x+y) ; // a zárójelezés megkönnyíti a kód olvashatóságát

Ugyan ez a példa if szerkezettel:

if (x==y) {
   z = x*y;
else {
   z = x+y;
}

A példában a fenti példában a {} jelek elhagyhatók, mivel az utasításblokk egyetlen kifejezést tartalmaz. Ennek ellenére én szeretem kitenni, mivel szintén olvashatóbb kódot kapunk (szerintem).

Még egy dologra hívom fel a figyelmet! Gyakori kezdő kódolási hiba (azért gyakorlottak is elkövetik néha) hogy a feltételben megfogalmazott kifejezésben egyenlőséget szeretnének vizsgálni (==), ehelyett értékadást használnak (=). Figyeljünk rá, hiszen az x=y és az x==y is érvényes kifejezés. Az első esetben egy értékadást (x megkapja y értékét), a másodikban pedig egy logikai kifejezést láthatunk.

Amennyiben többágú szelekciót kell megvalósítanunk, használhatunk egymásba ágyazott if szerkezetet is, de létezik a többágú szelekció azaz a switch() utasítás. Én magam úgy gondolom hogy két if egymásba ágyazva még olvasható, ha ennél több kell akkor már a switch()-et használom.

switch(<String vagy egész kifejezés>) {
   case <konstans_1>: 
        <utasítás>;
        break; //hogy kilépjen és ne hajtsa végre a többi ágat 
   case <konstans_2>: <utasítás>
        <utasítás>;
        break;
   ... 
   case <konstans_n>: <utasítás>
        <utasítás>;
        break;
   default
        <utasítás>;
}

A Java 7 verziótól kezdve van lehetőség a switch() utasításban String kifejezés kiértékelésére. A korábbi verziókban az úgynevezett sorszámozható típusok (char, byte, int, short, long) volt megengedett.
A kiértékelést követően az a case ág hajtódik végre, ahol a kiértékelt kifejezés megegyezik a case-ben megadott konstanssal. Ha nincs egyezés akkor a default utáni utasítás hajtódik végre. Amennyiben az egyik case ágba betalál a vezérlés, akkor onnantól kezdve végigcsinálja, minden utána lévő ág utasításait. Ezért amennyiben ezt nem akarjuk, úgy a break utasítással tudunk "kiugrani" a switch()-t záró blokk utáni pontra.