Képernyőről beolvasni, tömbök, debug
Tematika
- Képernyőről beolvasni:
Scanner - Tömbök: deklaráció, indexelés, bejárás
- Debug IntelliJ-ben: breakpoint, lépegetés, változók megfigyelése
Kontextus
A múlt órán a programjaink egyetlen módon kaptak adatot kívülről: parancssori argumentumként, még a program elindítása előtt megadva. Ma megnézzük, hogyan tudunk adatot bekérni futás közben — miközben a program már fut, megvárja, amíg begépeltek valamit.
Utána ezt kombináljuk a tömbökkel: nem csak
egy, hanem több értéket is be tudunk kérni és eltárolni. Végül,
ahelyett hogy System.out.println-ekkel próbálnánk
kitalálni, mi történik a programunkban, megtanuljuk, hogyan
lehet egy futó Java programot lépésről lépésre
megfigyelni IntelliJ-ben — ez lesz a mai óra
legnagyobb, gyakorlat-központú része.
Képernyőről beolvasni — Scanner
Scanner: A Scanner egy beépített
Java osztály, amellyel a program a szabványos bemenetről
(jellemzően a billentyűzetről) tud adatot beolvasni, amíg fut.
import java.util.Scanner;
public class GreetInput {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Mi a neved? ");
String name = scanner.nextLine();
System.out.println("Szia, " + name + "!");
}
}
Amit tisztázni kell:
import java.util.Scanner;— aScannernem automatikusan elérhető, importálni kell.new Scanner(System.in)— Példányosítjuk a Scanner-t (newkulcsszó); aSystem.ina szabványos bemenetet jelenti (billentyűzet); ebből hozunk létre egyScannerobjektumot.scanner.nextLine()— beolvas egy teljes sort, ésString-ként adja vissza.
Ez a múlt héthez képest: a múlt órán a
Greet program a nevet parancssori argumentumként
kapta (java Greet Anna), és a program elindulása
előtt már rendelkezésre állt. Most a program fut,
megáll, megvárja, amíg begépelünk valamit, és csak utána
folytatja a futást.
Számok beolvasása
Scanner scanner = new Scanner(System.in);
System.out.print("Hány éves vagy? ");
int age = scanner.nextInt();
System.out.println("Kor: " + age);
System.out.print("Mennyi a tanulmányi átlagod? ");
double average = scanner.nextDouble();
System.out.println("Átlag: " + average);
nextInt()— egész számot olvas be.nextDouble()— lebegőpontos számot olvas be.
Gyakori hiba (a "newline" csapda): ha
nextInt() vagy nextDouble() után egy
nextLine() következik, a nextLine()
sokszor egy üres sort fog visszaadni. Ennek az az oka, hogy a
nextInt() csak a számot olvassa be, a utána lévő
sortörés-karaktert a bemenetben hagyja — és a következő
nextLine() pont ezt az otthagyott sortörést olvassa
be, nem az újabb begépelt szöveget. Megoldás: szám beolvasása
után, ha utána nextLine()-t is hívunk, iktassunk
be egy plusz scanner.nextLine(); hívást, ami
"felemészti" az otthagyott sortörést.
Tömbök
Tegyük fel, hogy nem csak egy jegyet, hanem egy egész hallgatói
csoport jegyeit be szeretnénk kérni és tárolni. Egyesével
elnevezett változókkal (grade1, grade2,
grade3, ...) ez kezelhetetlen — pont erre valók a
tömbök.
Tömb (array): A tömb azonos típusú elemek rögzített méretű, sorszámozott (indexelt) gyűjteménye.
Deklaráció és inicializálás
int[] grades = new int[5]; // 5 elemű, egész számokat tároló tömb, kezdetben mind 0
int[] literalGrades = {5, 4, 5, 3, 4}; // közvetlenül megadott értékekkel létrehozott tömb
- A tömb mérete rögzített a létrehozáskor — utólag nem bővíthető.
- Az elemek indexelése 0-tól kezdődik.
Indexelés
grades[0] = 5; // az első elem beállítása
System.out.println(grades[0]); // az első elem kiolvasása
int size = grades.length; // a tömb mérete (nem metódus, hanem mező: nincs zárójel!)
Fontos: grades.length — nincsenek
zárójelek, mert ez egy mező, nem egy metódushívás (szemben pl. a
String length() metódusával, ahol van
zárójel).
Bejárás
for (int i = 0; i < grades.length; i++) {
System.out.println("grades[" + i + "] = " + grades[i]);
}
for (int grade : grades) {
System.out.println(grade);
}
- Az első forma (klasszikus
for) akkor kell, ha szükségünk van magára az indexre is. - A második forma (for-each) egyszerűbb, ha csak az értékekre van szükségünk, az indexre nem.
Példa: jegyek beolvasása és átlaga
import java.util.Scanner;
public class Grades {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Hány jegyet adsz meg? ");
int count = scanner.nextInt();
int[] grades = new int[count];
for (int i = 0; i < grades.length; ++i) {
System.out.print((i + 1) + ". jegy: ");
grades[i] = scanner.nextInt();
}
int sum = 0;
for (int grade : grades) {
sum += grade;
}
double average = (double) sum / grades.length;
System.out.println("Átlag: " + average);
}
}
Ezt a Grades programot fogjuk a következő részben debugolni.
Debug IntelliJ-ben
Eddig, ha egy programunk rosszul viselkedett,
System.out.println-eket szórtunk bele, hogy
lássuk, mi történik. Ez működik, de lassú, és utólag ki kell
törölni ezeket a sorokat. A debugger ennek a
szisztematikus, beépített változata: meg tudjuk állítani a
programot egy adott ponton, és élőben
megnézhetjük, mi van a változókban.
Egy hiba beültetése
Rontsuk el szándékosan a Grades programot —
cseréljük ki a ciklus feltételét egy hibás verzióra:
for (int i = 0; i <= grades.length; ++i) { // hiba: <= a < helyett
System.out.print((i + 1) + ". jegy: ");
grades[i] = scanner.nextInt();
}
Ez ArrayIndexOutOfBoundsException-t fog dobni, mert
a ciklus megpróbál a tömb méreténél eggyel nagyobb indexre is
írni.
Breakpoint elhelyezése
Breakpoint (töréspont): Egy jelölés egy kódsoron, amely jelzi az IntelliJ-nek, hogy futás közben itt álljon meg a program, mielőtt az adott sor lefutna.
- Kattintsatok a sorszám melletti szürke sávra a
grades[i] = scanner.nextInt();sor mellett — megjelenik egy piros pötty. - A
mainmetódus melletti zöld ▶ ikon helyett most a bogár ikonra kattintsatok (vagy jobb klikk a fájlon →Debug 'Grades.main()'). - A program elindul, és megáll a breakpointnál, mielőtt az adott sor lefutna.
Lépegetés
Az IntelliJ ablak alján megjelenik a Debug panel, benne lépegető gombokkal:
- Step Over (F8): végrehajtja az aktuális sort, és a következőre lép — ha a sor egy metódushívás, nem megy bele a metódus belsejébe.
- Step Into (F7): ha az aktuális sor egy metódushívást tartalmaz, belelép a metódus belsejébe is.
- Resume Program (F9): folytatja a futást a következő breakpointig (vagy a program végéig).
Változók megfigyelése
A Debug panel Variables fülén élőben látjátok:
- az
iaktuális értékét, - a
gradestömb tartalmát (kibontható, elemenként), - a
countéssumértékét, amint változnak.
Lépegessetek végig a ciklus néhány iterációján a Step
Over gombbal, és figyeljétek meg, ahogy i nő,
és hogyan íródnak be az értékek a grades tömbbe —
amikor i eléri a grades.length
értékét, a következő lépésnél lássátok meg élőben, ahogy a
hibás index miatt kivétel keletkezik.
Javítás
Cseréljétek vissza a feltételt i < grades.length-re,
futtassátok újra (most már Run-nal, nem
Debug-gal), és ellenőrizzétek, hogy a program
helyesen fut.
Gyakorlat
- Írjátok meg a
GreetInputprogramot, és próbáljátok ki anextInt()+nextLine()csapdát szándékosan — figyeljétek meg, mi történik, majd javítsátok ki egy extranextLine()hívással. - Írjátok meg a
Gradesprogramot (helyesen), és futtassátok néhány bemenettel. - Ültessetek bele szándékosan egy hibát (pl. rossz kezdőérték, vagy
<=a<helyett), tegyetek rá breakpointot, és debuggerrel derítsétek ki, hol romlik el. - Bővítsétek a
Gradesprogramot: írja ki a legnagyobb jegyet is a tömbből (ciklussal,Math.maxvagy manuális összehasonlítással) — nem kell hozzá rendezés, csak egy futó "eddigi maximum" változó.
Puffer (~30 perc)
Kérdések, esetleges környezet-/telepítési problémák megoldása, illetve további gyakorlási idő azoknak, akik lassabban haladnak — különösen a debugger használatának begyakorlására érdemes időt hagyni, mert ez sokaknak új eszköz.
Házi feladat
Ezek a feladatok a 2. órán tanultak (Scanner,
tömbök, IntelliJ debugger) önálló, otthoni elmélyítésére
szolgálnak.
1. feladat (Könnyű) — Három szám átlaga
Írjatok egy ThreeNumbersAverage nevű programot,
amely Scanner-rel egyesével bekér
három egész számot a felhasználótól, majd
kiírja az összegüket és az átlagukat.
2. feladat (Közepes) — Szavak visszafelé
Írjatok egy ReverseWords nevű programot, amely:
- bekéri
Scanner-rel, hány szót fog megadni a felhasználó (N), - beolvassa a szavakat egy
String[N]tömbbe, - kiírja a szavakat fordított sorrendben (az utolsóként megadott szó legyen az első a kiírásban).
Segítség: ehhez egy olyan for ciklus kell, amely a
tömb végétől indulva, csökkenő indexekkel
halad.
3. feladat (Közepes/Haladó) — Legkisebb és legnagyobb hőmérséklet
Írjatok egy Temperatures nevű programot, amely:
- bekéri
Scanner-rel, hány hőmérsékletadatot fog megadni a felhasználó (N), - beolvassa az értékeket egy
double[N]tömbbe, - kiírja a legkisebb és a legnagyobb beolvasott hőmérsékletet is.
4. feladat (Haladó) — Saját hibavadászat
Az alábbi program hibás — egy tömb elemeinek összegét próbálja kiszámolni, de valamit elrontottunk benne:
import java.util.Scanner;
public class BuggySum {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Hány számot adsz meg? ");
int count = scanner.nextInt();
int[] numbers = new int[count];
for (int i = 0; i <= count; i++) {
System.out.print((i + 1) + ". szám: ");
numbers[i] = scanner.nextInt();
}
int sum = 0;
for (int i = 1; i < numbers.length; i++) {
sum += numbers[i];
}
System.out.println("Összeg: " + sum);
}
}
Másoljátok be a kódot IntelliJ-be, tegyetek rá legalább egy breakpointot, és debug módban lépegetve derítsétek ki, mi a két külön hiba a programban. Javítsátok ki mindkettőt, és ellenőrizzétek, hogy helyes összeget ad néhány teszteléssel.
Hasznosnak találtad ezt a jegyzetet?
Ezek a jegyzetek minden hallgató számára ingyenesek. Ha időt spóroltál vele, fontold meg egy borravaló hagyását.
☕ Hívj meg egy kávéra