Gyakorlati alapok II.
Néhány tipikus probléma
Ebben a fejezetben okulásul megnézünk néhány olyan tipikus programozási hibát, amelyet garantáltan a programozó követ el.
Ez a durva programozási hiba már ismertetésre került az előző fejezetben:
public class Main {
public static void main(String[] args) {
System.out.println(4 / 0);
}
}
Végeredmény:
Exception in thread "main" java.lang.ArithmeticException:
/ by zero
at Main.main(Main.java:152)
Az olvashatóság és a továbbiak érdekében egy kicsit manikűrözzük ki a kódot, bár funkcionálisan ugyanaz lesz:
public class Main {
public static void main(String[] args) {
int a = 4;
int b = 0;
double c = a / b;
System.out.println(c);
}
}
Végeredmény:
Exception in thread "main" java.lang.ArithmeticException:
/ by zero
at Main.main(Main.java:152)
A negatív számoknak nincs négyzetgyökük, a művelet rajtuk matematikailag nem értelmezhető:
import java.math.*;
public class Main {
public static void main(String[] args) {
double szam = -1;
System.out.println(Math.sqrt(szam));
}
}
Végeredmény:
NaN
Valójában nem generáltunk kivételt, mert már maga a művelet alkalmazása is hiba. Ezen okból csupán egy szabványos üzenet kapunk:
NaN - Not a Number - nem szám
Ezt a hibát többféleképpen is el tudjuk követni. A hiba lényege, hogy nem egyezik az előzetesen deklarált tömbméret és (bármilyen jellegű) tömbművelet iterálásának (tipikusan a for ciklus) hossza.
Az alábbi futtatható Java-kódban a for ciklusban +1 értékkel futunk túl a tömbméreten, még pontosabban: a tömbelem-indexelésen:
public class Main {
public static void main(String[] args) {
int[] tomb = new int[]{0, 1, 2, 3, 4};
for(int i = 0; i <= tomb.length; i++){
System.out.println(tomb[i]);
}
}
}
Végeredmény:
0
1
2
3
4
Exception in thread "main"
java.lang.ArrayIndexOutOfBoundsException: 5
at Main.main(Main.java:5)
Ezzel a deklarációval azonban már jó lesz:
public class Main {
public static void main(String[] args) {
int[] tomb = new int[]{0, 1, 2, 3, 4};
for(int i = 0; i < tomb.length; i++){
System.out.println(tomb[i]);
}
}
}
Végeredmény:
0
1
2
3
4
Vagy:
public class Main {
public static void main(String[] args) {
int[] tomb = new int[]{0, 1, 2, 3, 4};
for(int i = 0; i <= tomb.length-1; i++){
System.out.println(tomb[i]);
}
}
}
Végeredmény:
0
1
2
3
4
Tipikus hiba a String adattípus és a numerikus adattípusok (Numerikus egész és Numerikus lebegőpontos) közti átváltáskor, konvertáláskor szokott előfordulni. Itt is többféle hibalehetőségből válogathatunk. Az egyik leggyakoribb, hogy összekeverjük a tizedespontot (.) és a tizedesvesszőt (,). A hétköznapi életben valóban keverve használjuk őket, de a Java-béli Double.parseDouble() függvény és hasonló funkciójú testvérei csakis tizedespontot hajlandók fogadni. Az alábbi futtatható Java-kódban a 3,14 értékű szamString még gond nélkül ki lesz írva a konzolra, de a konvertáló függvény már dob egy NumberFormatException kivételt:
public class Main {
public static void main(String[] args) {
String szamString = "3,14";
System.out.println(szamString);
double PI = Double.parseDouble(szamString);
}
}
Végeredmény:
3,14
Exception in thread "main"
java.lang.NumberFormatException: For input string: "3,14"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at Main.main(Main.java:5)
A tizedespont használata már sokat javít az összképen:
public class Main {
public static void main(String[] args) {
String szamString = "3.14";
System.out.println(szamString);
double PI = Double.parseDouble(szamString);
System.out.println(PI);
}
}
Végeredmény:
3.14
3.14
Elméletileg üres referencia olyankor keletkezik, ha változót nem inicializáltunk, azaz nem adtunk neki kezdőértéket. A manapság elterjedt fejlesztési eszközök azonban már egyre magasabb szintű automatikával képesek ezt a hibát detektálni. Így tesz az Eclipse is: a változó pontatlan létrehozásakor már valós időben visítozik és jól érthető hibaüzeneteket küld (a kurzort a sor melletti x fölé pozícionálva a következő szöveggel szembesülünk: The local variable nev may not have been initialized - a nev változó nem lett inicializálva):
A kód erőltetett futtatásakor azonnal ugyanilyen jellegű kivétellel szembesülünk:
public class Main {
public static void main(String[] args){
String nev;
System.out.println(nev);
}
}
Végeredmény:
Exception in
thread "main" java.lang.Error: Unresolved compilation problem:
The local variable nev may not have been initialized at Main.main(Main.java:4)
A may not have been initialized üzenet mindenkor és mindenképpen a megfelelő inicializálás hiányára utal.
További érdekesség, hogy a keletkezett hibát a JVM Error-nak minősíti, ez azonban felvet egy érdekes kvízkérdést: egy másik, régebben keletkezett példakódban viszont még RuntimeException kivétel keletkezik.
Vajon ez a fejlesztői felületek (NetBeans és Eclipse) vagy a Java-verziók különbözősége miatt alakult így?
A null referencia azt jelenti, hogy a hivatkozás mögött nincs értelmezhető adattartalom, vagy az adattartalom 0 (illetve null), amely alapjában véve nem probléma egyszerű adattípusok esetében, de néhány speciális és komplex adatszerkezetnél már komoly fennakadásokat okoz. Egy ilyet láthatunk az alábbi, NullPointerException kivételt generáló Java-kódban, hiszen a létrehozott o objektum lényegében üres, ezért nincs alaposztálya sem (amely információt a getClass() metódussal próbálnánk elérni):
public class Main {
public static void main(String[] args){
Object o = null;
System.out.println(o.getClass());
}
}
Végeredmény:
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:11)