Elméleti alapozás

Java-nyelv néhány összetett adattípusa

 

String

 

Egy program működéséhez gyakran van szükségünk a felhasználótól érkező vagy felé irányuló szöveges üzenetekre, ezek általában szavak, kifejezések, mondatok, egyéb szimbólumok. A számítógép számára azonban tökéletesen mindegy, hogy ezen üzenetek értelmesek-e vagy sem, alapértelmezésben ezt nem maga a számítógép, azaz az operációs rendszer dönti el, hanem a program megírásakor -önmaga jól felfogott érdekében-, a programozó kezeli le.

 

(Mostanában kezdenek elterjedni a verbális szövegértelmező alkalmazások, amelyek már rendszerszinten képesek ilyen funkciót ellátni, például Win7-ben a Speech Recognition. A beszédfelismerés lehetősége egyébként már felveti a mesterséges intelligencia kérdését is.)

 

A kommunikációs szövegek, szakszóval stringek, karakterfüzérek értelmezése  a programírás egyik fontos és fárasztó művelete, amely során a programot "bolondbiztossá" kell tennünk, hogy ne kerülhessen a rendszerbe rossz vagy értelmezhetetlen bemeneti adat.

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

Természetesen rengetegféle olyan program létezik, amelyik nem végez szöveges kommunikációt a felhasználóval, de a String adattípusra ilyen-olyan okok miatt legtöbbször szükségünk van. Éppen ezért az egyik legtöbbet használt, immár összetett adattípus a String, amelyet tehát leginkább karakterfüzérnek, karaktersorozatnak fordíthatunk. Valóban: a String valamilyen belső logika szerint összetartozó karakterek csoportja (amely tehát a felhasználó számára értelmes szöveggé áll össze).

 

www.informatika-programozas.hu - További információk!

A Java nyelv egyik ősében, a C nyelvben még nem volt String-kezelés, hanem a char* deklarációval került megvalósításra, amely számos hibalehetőség forrása volt: vagy kicsi lett a String számára lefoglalt memóriahely vagy egyszerűen elfelejtettük a helyfoglalást.

 

Mivel a String összetett adattípus, a Java már objektumként kezeli és így is hozzuk létre a new paranccsal, szigorúan kettős aposztrófok (" ") közé téve a szöveget:

 

String st = new String("Java");

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st = new String("Java");
    System.out.println(st);
    }
}

 

Végeredmény:

Java

 

A deklaráció -amint azt a Javában már megszokhattuk-, szigorú szabályok szerint történik, erre egyetlen kivétel a keletkezett objektum neve (referenciája), a fenti esetben st, ezt a szokásos programozási névkonvenciók figyelembevételével mi adtuk neki. De természetesen String objektumot létrehozhatunk üresen is: ekkor nem töltjük fel karakterekkel, hanem csak a memória-címfoglalás történik meg:

 

String st = new String();

 

A létrehozott String egy különálló, csakis a String osztály által használható memóriaterületre kerül (String pool). Mi történik akkor, ha itt összehasonlítás céljából létrehozunk egy másik, ugyanolyan példányt?

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

Az összehasonlítás memóriacím és tartalom alapján is megtörténhet.

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st1 = new String("Java");
    String st2 = new String("Java");
    System.out.println(st1 == st2);
    }
}

 

Végeredmény:

false

 

Ebben az esetben a Java pusztán lokáció, azaz memóriahely-foglalás, valójában memóriacím szerint hasonlította össze a példányokat. A végeredmény ebből a szempontból csakis false lehet, mert a 2 objektum memóriacíme különböző (hiszen különálló objektumok).

Ha tartalmilag szeretnénk az összehasonlítást megtenni, az equals() függvényt kell bevetnünk:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st1 = new String("Java");
    String st2 = new String("Java");
    System.out.println(st1.equals(st2));
    }
}

 

Végeredmény:

true

 

Tartalmilag a 2 objektum valóban azonos, a végeredmény tehát true lesz.

 

A nemzetközi helyzet feszülődik, ugyanis String objektumot egyszerű érték-hozzárendeléssel (=) is létrehozhatunk...

 

String st = "Java";

 

...és ekkor 2 azonos tartalmú String objektumot létrehozva...

 

String st1 = "Java";

String st2 = "Java";

 

...a Java-nyelv egyszerű összehasonlítással is azonosnak fog értékelni, mert a 2 objektum (a String pool-ban) egyetlen memóriaterületre mutat (ahol éppen a "Java" String tárolódik):

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st1 = "Java";
    String st2 = "Java";
    System.out.println(st1 == st2);
    }
}

 

Végeredmény:

true

 

A deklaráció még kissé tovább is bonyolítható:

 

String st1 = "Java";

String st2 = "Ez a " + st1 + " java";

 

Az st1 objektum létrehozása után értéke ("Java") néhány további karakterfüzérrel bekerül st2 objektumba és ez lesz kiírva:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {

    String st1 = "Java";

    String st2 = "Ez a " + st1 + " java!";

    System.out.println(st2);
    }
}

 

Végeredmény:

Ez a Java java!

 

String objektum akkor is létrejön, ha például a System.out.println() függvényben kerül felhasználásra:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    System.out.println("Java");
    String st = "Java";
    System.out.println("Java" == st);
    }
}

 

Végeredmény:

Java

true

 

Nyilvánvalóan egy String objektum csakis karaktereket tartalmaz, ám abból általában többet. Ebből következve van lehetőségünk létrehozandó String objektum bemeneti paramétereként karaktertömböt (char[]) megadnunk:

 

char[] java = new char[]{'J','a','v','a'};

String st2 = new String(java);

 

Az összehasonlítások a már megállapított eredményeket fogják adni:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st1 = new String("Java");
    char[] java = new char[]{'J','a','v','a'};
    String st2 = new String(java);
    System.out.println(st1 == st2);
    System.out.println(st1.equals(st2));
    }
}

 

Végeredmény:

false

true

 

Ha a Java API-dokumentációba kicsit beleássuk magunkat, akkor felfedezhetjük, hogy a Java minden keletkezett String objektumot különálló konstans karaktertömbökben tárol (private final char array[]).

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

A karakterek indexelése a tömbökhöz hasonlóan mindig 0-val fog kezdődni, ezt az indexszámot kapja majd a String 1. karaktere.

 

A keletkezett objektum a private és final hozzáférés-módosítóknak köszönhetően megváltoztathatatlan lesz (inmutable), ha ezt igényeljük, akkor az objektumról másolat fog létrejönni és az lesz megváltoztatva.

 

Ez a tulajdonság teszi lehetővé, hogy egy String objektumhoz további, beépített metódusokat kapcsolhassunk és ezek számunkra korrekt eredményeket szolgáltassanak. A legfontosabb String metódusok:

Az összehasonlítások a már megállapított eredményeket fogják adni:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st1 = new String("Cirbolya és Borbolya");
    String st2 = new String("Cirbolya");
    System.out.println(st1.equals(st2));
    System.out.println(st1.length());
    System.out.println(st1.isEmpty());
    System.out.println(st1.indexOf("C"));
    System.out.println(st1.charAt(0));
    System.out.println(st1.substring(9, 11));
    System.out.println(st1.contains("bolya"));
    System.out.println(st1.replace('o', 'i'));
    System.out.println(st2.compareTo("Cirbolya"));
    System.out.println(st2.concat(" és barátai"));
    }
}

 

Végeredmény:

false

20

false

0

C

és

true

Cirbilya és Birbilya

0 (azaz hasonlóak)

Cirbolya és barátai

 

A fenti metódusok kiválóan teljesítik feladatukat, hiszen folyamatosan követik azt a belső logikát, egyúttal elvárást, miszerint csakis azonos vagy valamilyen módon hasonló adattípusokat, illetve közös tulajdonságaikat érdemes egymással összehasonlítanunk. Így működhet együtt például egy összetett String objektum és egy karakter, mint egyszerű adattípus.

 

Ez a belső logika azonban gyakran szenved törést, sokszor önnön tudatlanságunk miatt; ezt már minden programozó megtapasztalta. Adott például egy String objektum:

 

String st = "1";

 

Vajon ez a deklaráció szám-e vagy String?

Természetesen az utóbbi, mert ha megpróbáljuk őt és egy számot összehasonlítani, akkor a fordító azonnal visítani fog:

 

www.informatika-programozas.hu

 

Sőt, "hagyományosnak" mondható típuskényszerítés (casting - kasztolás) sem fog működni:

 

System.out.println(szam == (int)st);

 

Végeredmény:

hibaüzenet

 

A megoldás -hogyha ez egyáltalán lehetséges-, hogy a bevitt String-et át kell alakítani, konvertálni számmá (Integer.parseInt()):

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st = new String("1");
    int szam = 1;
    int atalakitottSzam = Integer.parseInt(st);
    System.out.println(szam == atalakitottSzam);
    }
}

 

Végeredmény:

true

 

Egy kissé másképpen:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    String st = new String("1");
    int szam = 1;
    System.out.println(szam == Integer.parseInt(st));
    }
}

 

Végeredmény:

true

 

A fenti kódokban tehát a számok összehasonlítása már működni fog és korrekt eredményeket fog szolgáltatni.

 

A Java-nyelven belül természetesen többféle, legtöbbször magasszintű lehetőség áll rendelkezésre a felvetődött problémák megoldására. A fenti int-String típusütközés például sok mással együtt igen kényelmesen lekezelhető a beépített függvényeknél még nem említett valueOf() függvény segítségével. Ez a metódus a következő adattípusokat képes zökkenőmentesen egy String objektumba irányítani:

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    char karakter = 'a';
    int szam = 2;
    boolean valasztas = true;
    String st1 = String.valueOf(karakter) + String.valueOf(szam) + String.valueOf(valasztas);
    System.out.println(st1);
    }
}

 

Végeredmény:

a2true

 

További érdekes, de kissé problematikus adalék, ha String objektum létrehozásához a fenti valueOf() függvény kizárásával byte típusú tömböket használunk fel, mert ebben az esetben ügyelnünk kell az alapértelmezett karakterkódolásra, amely gépenként, operációs rendszerenként, fejlesztési projektenként különböző lehet. Az alábbi kódban egy byte típusú tömböt hozunk létre és kezdeti értékeit ({65, 66, 67, 68, 69}) átadjuk az st1 objektumnak, amelynek hatására kimeneti adatként ABCDE karaktereket fogunk kapni:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

public class Main {
    public static void main(String[] args) {
    byte[] tomb = {65, 66, 67, 68, 69};
    String st1 = new String(tomb);
    System.out.println(st1);
    }
}

 

Végeredmény:

ABCDE

 

Ám ha rosszul van beállítva karakterkódolás vagy olyan karaktereket adunk be, amelyek az alapértelmezett karakterkódolás alapján nem értelmezhetők, akkor vagy értelmezhetetlen kimeneti karaktereket, vagy "nem támogatott kódolási kivétel" című hibaüzenetet fogunk kapni (UnsupportedEncodingException).