Tantárgyi követelmények
Java SE 8 Documentation
Feladatok

Halló mindenki!

Írjuk meg Javaban a hagyományos "Hello world" programot!

/**
 * "Hello world" program.
 */

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

Fontos, hogy az állomány neve megegyezzen a benne definiált publikus osztály nevével. Vagyis, ha Foo.java az állomány neve, akkor legyen benne egy Foo nevű osztály, amelyet a public class Foo definícióval fejtünk ki.

Kiírás a konzolra

Hasonlóan más nyelvekhez, a konzolra itt is többféle módon lehet írni, a java.lang.System osztályon keresztül:

  • Szabványos bemenet ("standard input"): System.in objektum.
  • Szabványos kimenet ("standard output"): System.out objektum.
  • Szabványos hibakimenet ("standard error"): System.err objektum.
  • A kiírandó szövegben szerepelhetnek vezérlőkarakterek (escape sequence), pl. \r, \n, \t, \b, stb.
  • A System osztály további segédműveleteket is tartalmaz, pl. System.exit().

A környezet beállítása

A program lefordításához és futtatásához először gondoskodnunk kell a környezet beállításáról.

Windows esetén indítsuk el a parancssort, például a Windows + R gombkombináció lenyomása után a cmd.exe elindításával. Ezt követően a PATH környezeti változót kell módosítanunk:

$ PATH=%PATH%;"C:\Program Files\Java\jdk1.8.0_77\bin"
$ echo %PATH%
...;C:\Program Files\Java\jdk1.8.0_77\bin

Ekkor a következőnek működnie kell:

$ javac -version
javac 1.8.0_77

GNU/Linux vagy UNIX rendszereken is lényegében ugyanezt kell megcsinálni, de ennek konkrét szintaxisa az adott shelltől függ. Illetve jobb esetben a csomagkezelő erről gondoskodik a JDK telepítésekor.

Fordítás

A programokat a javac ("Java compiler") segítségével tudjuk lefordítani.

$ javac HelloWorld.java

Amennyiben a program fordítási hibákat tartalmaz, úgy a fordító erről tájékoztat:

$ javac HelloWorldBad.java
HelloWorldBad.java:1: error: class, interface, or enum expected
public HelloWorldBad {
       ^
HelloWorldBad.java:2: error: class, interface, or enum expected
    public static void main(String[] args) {
                  ^
HelloWorldBad.java:4: error: class, interface, or enum expected
    }
    ^
3 errors

ahol a hibás kód ez volt:

public HelloWorldBad {
    public static void main(String[] args) {
         return 0;
    }
}

Futtatás

A sikeres fordítás eredményétől függően nulla vagy több .class kiterjesztésű (byte-kódot tartalmazó) állomány keletkezik. Ezeket az állományokat lehet futtatni a Java virtuális gép (Virtual Machine, VM), azaz a java segítségével.

$ java HelloWorld
Hello World!

Megjegyzés: A java csak olyan .class állományt tud futtatni, amelynek van publikus statikus main() metódusa (ld. a fenti példaprogramot)!

Egy olyan program, ahol nincs main() metódus:

public class HelloWorldWrong {}

Lefordul hiba nélkül:

$ javac HelloWorldWrong.java

Azonban nem fog tudni futni:

$ java HelloWorldWrong
Error: Main method not found in class HelloWorldWrong, please define the main method as:
   public static void main(String[] args)

Dokumentáció generálása

A forráskódból, a megjegyzések segítségével a programhoz tartozó (API) dokumentáció készíthető a javadoc felhasználásával. Ehhez a programban speciális formátumú megjegyzéseket kell írni.

$ javadoc HelloWorld.java
Loading source file HelloWorld.java...
Constructing Javadoc information...
Standard Doclet version 1.8.0_77
Building tree for all the packages and classes...
Generating /HelloWorld.html...
Generating /package-frame.html...
Generating /package-summary.html...
Generating /package-tree.html...
Generating /constant-values.html...
Building index for all the packages and classes...
Generating /overview-tree.html...
Generating /index-all.html...
Generating /deprecated-list.html...
Building index for all classes...
Generating /allclasses-frame.html...
Generating /allclasses-noframe.html...
Generating /index.html...
Generating /help-doc.html...

Általános programozási konvenciók

package java.foo; // top-level domain (TLD), kisbetus karakterek

/**
 * Osztalyleiras
 *
 * @version  0.1.0
 * @author   Mr. T
 */
public class Bar extends Baz {

    /** classVar1 egysoros comment. */
    public int classVar1;

    /**
     * classVar2, aminek meg tobbsoros
     * a leirasa.
     */
    private static String classVar2;

    /**
     * Konstruktor komment.
     */
    public Bar() {
        // ...
    }

    /**
     * Fuggveny komment.
     */
    public void doSomething() {
        // ...
    }

    /**
     * Valami masik fuggveny komment.
     *
     * @param someParam valami parameter
     * @return valami ertek
     */
    public int returnSomeValue(Object someParam) {
        // ...
    }

    /**
     * Logikai fuggveny.
     */
    public boolean isSomething() {
       // ...
    }
}

A forráskódok elnevezésével, elrendezésével kapcsolatos konvenciók:

  • Osztálynév = állománynév, nagybetűvel kezdődik,
  • Csomagnév = könyvtárnév, kisbetűvel kezdődik, skatulyázható.

Megjegyzés: A Java ugyan megengedi az ékezetes karakterek használatát mind az állománynevekben, mind pedig az azonosítókban, azonban ez nem ajánlott!

Típusok

A nyelvben találhatóak beépített primitív típusok, néhány közülük: byte, short, int, long, float, double, char, boolean. Ezek rendelkeznek kezdőértékkel, amely a típustól függ (pl. 0 az int típus esetén, false a boolean esetén). Értékeik megadhatóak oktális (int octVal = 01), hexadecimális (byte hexVal = 0xff), vagy tudományos (double d = 1.23e4) jelöléssel.

A többi típust objektumosztályok írják le, illetve szükség esetén (akár automatikusan) a primitív típusú értékek maguk is objektumokba csomagolhatóak (java.lang.Byte, java.lang.Short, java.lang.Integer, stb.). Továbbá számos, a típusokhoz tartozó műveletet a megfelelő osztály statikus függvényeként implementálták.

Típusok közti konverzió:

  • bővítő konverzió, a fordító automatikusan engedi,
  • szűkítő konverzió, típuskényszerítéssel (byte b = (byte) 300) kérhető.

Konverzió szöveggel:

  • Szöveget a java.lang.String osztállyal lehet ábrázolni.
  • Számok szöveggé alakítása: String s = "" + 1;
  • Szöveg számmá alakítása: Integer.parseInt("1"), Double.parseDouble("2.0"), stb.

Vezérlési szerkezetek

A nyelv jellegében imperatív vezérlési szerkezeteket tartalmaz, amelyek mind utasítások. Lentebb találhatóak a fontosabb kategóriák. Ezeken kívül még a nyelv természetesen támogatja több utasítás összefogását egy blokkba. A blokkokat a { és } jelek határolják.

if, else

if ( /* elso feltetel */ ) {
    // ha az elso feltetel igaz
} else
if ( /* masodik feltetel */ ) {
    // ha a masodik feltetel igaz
} else
if ( /* harmadik feltetel */ ) {
    // ha a harmadik feltetel igaz
} else {
    // egyebkent
}

switch

A switch utasítással többágú elágazásokat hozhatunk létre, azonban nem feltételek, hanem egy kifejezés értéke alapján. Ennek típusa byte, short, char, int vagy ezek csomagoló osztálya (Byte, Short, Character, Integer) lehet.

int month = 8;

switch (month) {
    case 1:  System.out.println("Jan"); break;
    case 2:  System.out.println("Feb"); break;
    case 3:  System.out.println("Mar"); break;
    case 4:
    case 5:
    case 6:  System.out.println("Apr, May or Jun"); break;
    default: System.out.println("Other month"); break;
}

for, while, do

while ( /* logikai feltetel */ ) {
    // ciklusmag
}
do {
    // ciklusmag
} while ( /* logikai feltetel */ );
for (/* inicializalas */; /* megallasi feltetel */; /* leptetes */) {
    // ciklusmag
}
for (;;) {  // vegtelen ciklus
    // ciklusmag
}
for (String a : args) {  // tombokre, iteralhato adatszerkezetekre
    // ciklusmag
}

break, continue, return

A break utasítás segítségével kiléphetünk blokkok, például ciklusmagok belsejéből. Ilyenkor a vezérlés a blokkot követő utasításra kerül.

for (;;) {
    if (false)
        break;
}

A continue utasítással a ciklusmag fennmaradó részét ugorhatjuk át, vagyis használatakor a ciklus újrakezdődik és a vezérlés a feltétel kiértékeléséhez kerül.

for (int i = 0; i < 10; ++i) {
    if (i < 9)
        continue;
    System.out.println("i = " + i);
}

Megjegyzés: Létezik a nyelvben a goto kulcsszó, de nem használható. Ellenben a break és continue paraméterezhetők címkékkel.

A return segítségével léphetünk vissza alprogramokból (metódusokból). Ha van visszatérési érték, akkor itt adhatjuk meg.

static void main(String args) {
    if (args.length == 0)
        return;
    System.out.println("Never reached.");
}

static int succ(int i) {
    return (i + 1);
}

Valós számok

A valós számok (a float és double típusok) alkalmazása alapos körültekintést igényel -- nem csak a Java nyelvben, de általában minden más nyelven is. Ennek oka, hogy a gépen tárolt valós számok csak közelítések, amelyek fixpontos vagy lebegőpontos ábrázolásúak lehetnek.

  • Fixpontos ábrázolás esetén az előjelet, az egészrészt és a törtrészt adott mennyiségű biten tárolják, függetlenül a konkrét értéktől. Ez így elég nagy információvesztéssel is járhat, viszont gyors.

  • Lebegőpontos ábrázolás esetén az értékeket egy ún. normálalakban tárolják, amely tulajdonképpen a következő felírást takarja:

    (-1)^s \cdot m \cdot b^k

ahol s az előjel, a b az alap, 0 \leq |m| \leq b a mantissza, k pedig a karakterisztika. Az alap általában tízes vagy kettes.

Példa a reprezentációval járó numerikus hibára:

System.out.println(0.2 + 0.2 + 0.2 + 0.2 + 0.2);  // kis numerikus hiba: 1.0
System.out.println(0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f); // 1.0000001
System.out.println(0.1f + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d); // 1.000000001490116

Az == operátor

A fentiek miatt valós számok közvetlen összehasonlítása, különösen ciklusok feltételeiben, nem ajánlott.

System.out.println(0.3 == 0.1d + 0.1d + 0.1d);

vagy

for (double d = 0.0; d != 0.3; d += 0.1) {
    // Vegtelen ciklus lesz!
}

Ezt a hiányosságot ún. hibahatár definiálásával lehet kiküszöbölni. Vagyis két valós számot megegyezőnek tekint, amennyiben azok egy adott \epsilon távolságon belül találhatóak.

double delta = 1.0E-5;  // hibahatar (epszilon)
double d1    = 0.3
double d2    = 0.1 + 0.1 + 0.1;

if (Math.abs(d1 - d2) < delta) {
    System.out.println("d1 == d2");
}

Megjegyzés: Az abs() metódus a java.lang.Math osztály eleme, ahol további matematikai függvények is találhatóak.

Túl- és alulcsordulás

A gépi ábrázolás korlátai miatt minden számtípus rendelkezik egy minimális és maximális értékkel (például Integer.MIN_VALUE és Integer.MAX_VALUE), amelyek átlépésével alul- vagy túlcsordulás következik be. Ez nehezen észlelhető hibákhoz tud vezetni.

double big = 1.0e307 * 2000 / 2000;
System.out.println(big == 1.0e307);

Amikor beszorozzuk a számot, kimegyünk a double típus ábrázolható tartományából, és az Infinity értéket kapjuk, amelyet újból elosztva már nem az eredeti számot kapjuk vissza!

Ugyanígy előfordulhat, hogy két szám, d_1 és d_2, összeadása semmilyen változást nem eredményez, tehát a d_1 + d_2 \gt d_1 nem teljesül.

System.out.println(1234.0d + 1.0e-13d == 1234.0d);

Ha kiírunk a konzolra egy valós számot, akkor a megjelenő érték nem a reprezentációban használt közelített érték lesz.

System.out.println(0.1d);  // Megjeleno ertek: 0.1

viszont:

System.out.println(0.1 == 0.099999999999999998); // hamis
System.out.println(0.1 == 0.099999999999999999); // igaz
System.out.println(0.1 == 0.100000000000000001); // igaz

A közelített érték egy speciális osztály segítségével jeleníthető meg:

// A kiirt ertek: 0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal(0.1));

Tömbök

Minden T típushoz van [], vagyis tömb típus. A tömbök objektumok, ezért referenciákkal hivatkozunk rájuk, valamint vannak adattagjaik. Emiatt a tömbök mindig ismerik a saját hosszukat, valamint külön példányosítani kell ezeket.

int[] array1 = new int[5];
int array2[];
int array3[] = { 1, 2, 3, 4, 5 };

for (int i = 0; i < array3.length; ++i) {
    System.out.println(array3[i]);
}

Többdimenziós tömbök és létrehozhatóak a [] jelölés ismételt alkalmazásával. A példányosításkor az első dimenziót legalább meg kell adni.

Vesd össze:

int[][] multidim = new int[5][5];

vagy

int[][] multidim = new int[5][];

for (int i = 0; i < multidim.length; i++) {
    multidim[i] = new int[i + 1];
}

Operátorok

A nyelvben használhatóak a C-szerű nyelvekben már látott operátorok: ==, !=, &&, ||, +, -, *, /, %, ++, --, +=, -=, *=, /=, ?:, stb.

Az operátorok eredményének típusa mindig a bővebb paraméter típusa lesz, de legalább int, például:

double d = 1 / 2;  // eredmenye: 0.0
byte b = 1 + 2;    // explicit tipuskenyszerites kell

Prefix és postfix operátorok

A ++ operátort lehet prefix és postfix jelöléssel is alkalmazni, ez viszont befolyásolja a viselkedését is.

int i = 0;
System.out.println(i++); // kiir, megnovel: "0"
System.out.println(++i); // megnovel, kiir: "2"

Mi az eredménye (vö. C++)?

int i = 0;
System.out.println("" + i++ + ++i); // C++: architekturafuggo

Mi lesz az eredménye?

int i=0;

i=i++;
i=i++;
i=++i;
System.out.println(i);

Objektumok összehasonlítása

Objektumokat mindig az equals() metódussal tudunk összehasonlítani, az == operátor csak referencia szerinti összehasonlítást végez.

Például szövegeket is ezen a módon hasonlíthatunk össze:

boolean b1 = "a" == "a";       // lehet hamis!
boolean b2 = "a".equals("a");  // mindig igazat kell adnia

Összehasonlító operátor feltételekben

Amikor feltételekben adunk hasonlítunk össze kifejezéseket, akkor lehetőség szerint bal oldalra írjuk a konstansokat!

boolean b = false;

if (true == b) {
    // ...
}

Feladatok

  • Készítsünk egy olyan programot, amely kiírja a számokat 1-től 100-ig! A hárommal osztható számokhoz a szám helyett jelenjen meg Fizz, az öttel osztható számokhoz jelenjen meg Buzz, illetve a hárommal és öttel oszthatóakhoz pedig, hogy FizzBuzz!

  • Készítsünk egy hőmérsékleti skálák közt konvertáló programot! Ehhez olvassunk be egy számot és karaktert a szabványos bemenetről: ha karakter c akkor a számot Celsiusról Fahrenheit fokra számoljuk át az alábbi képlet alapján:

    C = \frac{(F - 32) \cdot 5}{9}

    ellenkező esetben Fahrenheitről Celsius értékre számoljunk.

  • Valósítsunk meg egy minimális konzolos számológépet! Olvassuk be az operátort, majd annak az operandusait és írassuk ki a konzolra a számolás eredményét! Támogassuk a következő operátorokat: összeadás (+), kivonás (-), szorzás (*) és osztás (/). Minden egyéb esetben adjunk hibajelzést és kérjünk be új adatokat.

  • Írjunk egy olyan programot, amely egy beolvasott számra eldönti, hogy tökéletes-e! Tökéletesnek nevezünk olyan egész számokat, amelyek megegyeznek az osztóik (az egyet beleértve, önmagukat kivéve) összegével. Az első négy ilyen szám a 6, 28, 496 és 8128.

  • Az előző megoldásunkat egészítsük ki úgy, hogy egytől a paraméterként megadott határig minden egész számra vizsgálja a tökéletességet és megadja, hogy abban az intervallumban mennyi ilyet talált! Ha nem talált egyetlen ilyen számot sem, akkor írja ki, hogy Egyetlen tökéletes szám sincs a megadott intervallumban.

  • írjunk olyan programot, amely előállítja a Collatz-sorozat tagjait! Ezt a sorozatot a következő képlet alapján lehet számolni:

    a_n = \left\{\begin{array}{ll}\frac{1}{2} \cdot a_{n - 1} & \text{ha } a_{n - 1} \text{ mod }\ 2\ =\ 0\\ 3 \cdot a_{n - 1} + 1 & \text{ha } a_{n - 1} \text{ mod }\ 2\ \neq\ 0\end{array}\right.}

    A program paraméterként kérje be az N értékét, ahol N < 100. Ettől függően a kiíratás tartson a_0 = N-től a_i = 1-ig!

Linkek

Kapcsolódó forráskódok
Oktatói honlap
Vissza