Post on 22-Jan-2020
OBJEKTNO ORIJENTISANO PROGRAMIRANJE
PREDAVANJE 13: OBRADA GREŠAKA I ULAZNO-IZLAZNE OPERACIJE (TEKSTUALNI FAJLOVI, TASTATURA I EKRAN)
Miloš Kovačević
Đorđe Nedeljković
1 /21
OSNOVNI KONCEPTI- Priroda grešaka u programu
- Klijent-server koncept i obaveštavanje klijenta o greški
- Izuzeci
- Obrada izuzetaka (try/catch/finally)
- Tekstualni fajlovi – čitanje i pisanje
- Čitanje sa tastature
- Ispis na ekran
2 /21
PRIRODA GREŠAKA U PROGRAMU- Sintaksne greške
- nepravilna upotreba delova jezika (ključne reči, operatori, ...) pri kodiranju klasa
- otklanjaju se jednostavno pri prevođenju na upozorenje prevodioca
- if(a = 5){...} //treba == umesto =
- Logičke greške (bugs) - greške u logičkom postupku (sledu akcija) pri rešavanju problema
- otklanjaju se ne tako lako prilikom testiranja rada programa
- primeri: deljenje sa 0, objekat se nalazi u nepredviđenom stanju, korisnik unosi neadekvatne ulazne podatke, itd.
- Značajan utrošak programerskog vremena odlazi na testiranje implementacije klasa u cilju otklanjanja logičkih grešaka
- Programski modeli složenih sistema skoro uvek sadrže greške, od kojih se najveći broj otkloni pre puštanja na tržište, a ostale se otklanjaju tokom životnog ciklusa softvera
- Java olakšava proces prevazilaženja grešaka primenom koncepta izuzetaka
3 /21
KLIJENT-SERVER KONCEPT- Kada se nad objektom poziva neki njegov javni metod,
onda se on nalazi u ulozi servera (pružaoca usluge), a objekat koji poziva metod naziva se klijent (onaj koji potražuje uslugu)
- Isti objekat u toku vremena može se naći u obe uloge
- Primeri softverskih komponenti koje se nalaze u klijent-server relaciji:- Apache web server (isporučuje stranice sajta na zahtev) – Chrome web browser (klijent)
- Exchange mail server (čuva i isporučuje poštu) – Outlook mail klijent (pregleda i pravi mailove)4 /21
LOGIČKE GREŠKE – KAKO IH OTKLONITI
5 /21
public class RangLista{ // serverska klasa
private String[] ime; // indeks u nizu odnosi se
private double[] poeni; // na mesto u rang listi
public RangLista(int brojMesta){
ime = new String[brojMesta];
poeni = new double[brojMesta];
}
public void ubaci(int rb, String i, double p){
ime[rb - 1] = i; poeni[rb - 1] = p;
}
...
}
public class Klijent{ // klijentska klasa
public static void testRangLista(){
RangLista rl = new RangLista(10);
rl.ubaci(1, "Pera Perin", 100);
rl.ubaci(15, "Mika Mikic", 2);
}
}
Moguća greška ukoliko je redni broj van broja predviđenih mesta na listi.Server klase RangLista ovde “puca“ za nekorektan ulaz rb!
Poruka o greški u BlueJ-u
- Server se štiti od grešaka uzrokovanih pogrešnim ulaznim podacima (parametri metoda) primenom defanzivnog programiranja (ekstenzivna upotreba if naredbi)
- U gornjem primeru korisnik dobija poruka o greški na ekranu. Ali, šta ako je klijent softverska komponenta koja ne čita poruke sa ekrana?
- Kako objekat klase RangLista vraća informaciju klijentu da je došlo do greške?
LOGIČKE GREŠKE – DEFANZIVNO PROGRAMIRANJE
6 /21
public void ubaci(int rb, String i, double p){
if(rb >= 1 && rb <= ime.length){
ime[rb - 1] = i; poeni[rb - 1] = p;
} else{ System.out.println("Nedozvoljeni redni broj"); }
}
OBAVEŠTAVANJE KLIJENTA O NASTALOJ GREŠKI
7 /21
- Vraćanje informaciju klijentu da je došlo do greške preko povratne vrednosti metode
- Pristup obaveštavanja putem povratne vrednosti ima tri nedostatka:- (1) Šta ako su sve povratne vrednosti metode mogući rezultati njenog izvršavanja?
- (2) Čak i kada (1) ne predstavlja problem, programer klijentske aplikacije nije u obavezida testira povratnu vrednost (u gornjem primeru da stavi if(s.skloni()...))
- (3) Šta se dešava kada konstruktor ne može pravilno da inicijalizuje objekat u početno stanje? (npr. kada u klasi RangLista neko prosledi negativan broj mesta na rang listi)
- Problemi obaveštavanja iz (1), (2) i (3) mogu se elegantno rešiti primenom koncepta izuzetaka (Exceptions)
// u serveru
public boolean ubaci(int rb){
if(rb >= 1 && rb <= ime.length){
...
return true;
} else { ... ; return false; }
}
// u klijentu
if(rl.ubaci(...)){
// operacija uspela
...
} else{
// operacija nije uspela
}
- Java obavezuje programera da predvidi mogućnost nastanka određenih tipova greški i da ih po nastanku obradi u cilju oporavka programa: greške koje se moraju obraditi- Primer: prilikom otvaranja datoteke sa diska, datoteka sa datim imenom ne postoji,
prilikom čitanja podataka koji dolaze sa mreže prekida se veza, ...
- Postoje i tipovi grešaka čiji nastanak nije obavezno predvideti i obraditi: greške koje se ne moraju obraditi- Primer: pristupanje elementu niza sa pogrešnim indeksom,
poziv metode nad objektnom promenljivom sa vrednošću null, ...
- Izuzetak je objekat specijane klase koji reprezentuje detalje greške nastale u programu (nosi informacije o uzroku greške, mestu i vremenu nastanka, itd.)
- Generiše se od strane virtuelne mašine (JVM) unutar one metode na mestu na kome nastaje greška
IZUZECI (EXCEPTION)
8 /21
- Svi izuzeci su instance klase Exception ili su nasleđeni iz nje
- Izuzeci koje virtuelna mašina generiše prilikom nastanka greški koje se moraju obraditi zovu se izuzeci sa proverom (checked exceptions)
- Izuzeci koje virtuelna mašina generiše prilikom nastanka greški koje ne se moraju obraditi zovu se izuzeci bez provere (unchecked exceptions), nasleđeni iz klase RuntimeException
TIPOVI IZUZETAKA
9 /21
OBRADA IZUZETAKA KOJI SE PROVERAVAJU
10/21
- Prilikom prevođenja programa prevodilac proverava mogućnost nastajanja greški koje moraju biti obrađene i u skladu sa tim da li je programer predvideo i obradio takve greške u kodu
- Provera i obrada obavljaju se primenom try-catch naredbe:
- Ukoliko se u try bloku ne desi greška, tok se prebacuje na prvu naredbu posle catchbloka: (redosled izvršavanja naredbi je 1, 2, 3, 6)
try {
// blok naredbi u kojima moze nastati greska koja mora biti obradjena
} catch(KlasaIzuzetka e) {
// obrada izuzetka e
}
try {
naredba 1;
naredba 2;
naredba 3;
} catch(KlasaIzuzetka e) {
naredba 4;
naredba 5;
}
naredba 6;
Greška nastala prilikom izvršenja naredbe!Tok izvršavanje prebacuje se na naredbu 4 u catch bloku(redosled izvršavanja naredbi je 1, 2, 4, 5, 6)
tip izuzetka (greške) koji se mora obraditi
PISANJE U TEKSTUALNU DATOTEKU
11/21
- Za pisanje u tekstualnu datoteku koristi se klasa FileWriter (iz paketa java.io)
- Prilikom pisanja, datoteku treba otvoriti (FileWriter konstruktor), pisati po njoj (metoda write() ), i po završetku je zatvoriti (metoda close() )
import java.io.FileWriter; import java.io.IOException;
...
public void snimi(String imeDatoteke){
try{
FileWriter fw = new FileWriter(imeDatoteke)); // otvaranje datoteke
// moguci IOException
fw.write("" + ime.length + “\n"); // pisanje u datoteku (IOException)
for(int i = 0; i < ime.length; i++){
fw.write(ime[i] + ":" + poeni[i] + "\n"); // pisanje (IOException)
}
fw.close(); // zatvaranje datoteke (IOException)
} catch(IOException e){
System.out.println("Greska pri snimanju u " + imeDatoteke);
}
}
blok u kome se izuzetak tipa IOException hvata
oporavak nakon greške
ČITANJE IZ TEKSTUALNE DATOTEKE
12/21
- Za čitanje iz tekstualne datoteke koriste se klase FileReader i BufferedReader
import java.io.FileWriter; import java.io.IOException;
...
public static RangLista ucitaj(String imeDatoteke){
RangLista rl;
try{
BufferedReader r = new BufferedReader(new FileReader(imeDatoteke));
String linija; int i = 0;
rl = new RangLista(Integer.parseInt(r.readLine()));
while( (linija = r.readLine()) != null ) {
String[] x = linija.split(":");
rl.ubaci(i, x[0], Double.parseDouble(x[1]));
i++;
}
r.close();
} catch(FileNotFoundException e) {
System.out.println("Ne postoji datoteka sa imenom " + imeDatoteke);
rl = null;
} catch(IOException e) {
System.out.println("Greska pri citanju " + imeDatoteke);
rl = null;
}
return rl;
}
obrada greške zavisi od tipa izuzetka
otvaranje datoteke
čitanje kraj datoteke?
zatvaranje
- Vreme čitanja sa diska ~ ms, a iz memorije ~ ns (105 - 106 puta sporije u odnosu na mem.)
- Metod read() klase FileReader čita datoteku sa diska karakter po karakter
- Čitanja se ubrzava korišćenjem memorijskog bafera (BufferedReader) koji se jednom operacijom čitanja puni sa diska, a onda iz njega program čita po potrebi
- Kada program učita ceo bafer inicira se njegovo ponovno punjenje
- Metod readLine() klase BufferedReader omogućava čitanje cele linije
KORIŠĆENJE BAFERA ZA ČITANJE
13/21BufferedReader r = new BufferedReader(new FileReader(imeDatoteke));
- Slično kao i pri čitanju iz datoteke sa baferom, realizuje se pomoću BufferedWriter klase
- Bafer se automatski prazni kada se napuni. Poslednji segment ispisa verovatno ne zauzima ceo bafer. Pražnjenje bafera po završetku ispisa obavlja se pri zatvaranju (w.close()) ili na zahtev (w.flush()).
PISANJE U TEKSTUALNU DATOTEKU – KORIŠĆENJE BAFERA
14/21
import java.io.FileWriter; import java.io.IOException;
...
public void snimiV2(String imeDatoteke){
try{
BufferedWriter w = new BufferedWriter( // otvaranje datoteke
new FileWriter(imeDatoteke)); // moguci IOException
w.write("" + ime.length); // pisanje u datoteku (IOException)
w.newLine(); // pisanje u datoteku (IOException)
for(int i = 0; i < ime.length; i++){
w.write(ime[i] + ":" + poeni[i]); // pisanje u datoteku (IOException)
w.newLine(); // pisanje u datoteku (IOException)
}
w.close(); // zatvaranje datoteke i praznjenje bafera (IOException)
} catch(IOException e){
System.out.println("Greska pri snimanju u " + imeDatoteke);
}
}
SAŽIMANJE CATCH BLOKOVA U JEDAN
15/21
- Kada nije potrebno različite izuzetke obradititi na različite načine hvatamo izuzetak klase Exception (princip polimorfne varijable)
import java.io.FileWriter; import java.io.IOException;
...
public static RangLista ucitaj(String imeDatoteke){
RangLista rl;
try{
BufferedReader r = new BufferedReader(new FileReader(imeDatoteke));
String linija; int i = 0;
rl = new RangLista(Integer.parseInt(r.readLine()));
while( (linija = r.readLine()) != null ) {
String[] x = linija.split(":");
rl.ubaci(i, x[0], Double.parseDouble(x[1]));
i++;
}
r.close();
} catch(Exception e) {
// dva izuzetka obradjena na isti nacin
System.out.println(e.getMessage());
rl = null;
}
return rl;
}metod koji vraća sistemskuporuku o greški
- Izuzeci koje virtuelna mašina generiše na greške koje se ne moraju obraditi zovu se izuzeci bez provere (npr., pristup elementu niza van opsega)
- Možemo ih, ali i ne moramo obrađivati kroz try-catch naredbu
- Izuzeci bez provere su objekti nasleđeni iz klase RuntimeException
IZUZECI BEZ PROVERE
16/21
try{
BufferedReader r = new BufferedReader(new FileReader(imeDatoteke));
String linija; int i = 0;
rl = new RangLista(Integer.parseInt(r.readLine()));
while( (linija = r.readLine()) != null ) {
String[] x = linija.split(":");
rl.ubaci(i, x[0], Double.parseDouble(x[1]));
i++;
}
r.close();
} catch(FileNotFoundException e) {
System.out.println("Ne postoji datoteka sa imenom " + imeDatoteke);
rl = null;
} catch(IOException e) {
System.out.println("Greska pri citanju " + imeDatoteke);
rl = null;
}
Šta se dešava ako x[1] ne predstavlja broj?Generiše se NumberFormatException
NumberFormatException se ovde ne obrađuje – program prestaje sa radom uz poruku o greški
- Izuzeci omogućavaju da se spreči kreiranje objekata u nedozvoljenom stanju
- Metoda koja je generisala izuzetak može obraditi izuzetak i izvršiti oporavak na način koji je za dati kontekst najlogičniji
public class RangLista{ // serverska klasa
private String[] ime; // indeks u nizu odnosi se
private double[] poeni; // na mesto u rang listi
public RangLista(int brojMesta){
ime = new String[brojMesta];
poeni = new double[brojMesta];
}
public void ubaci(int rb, String i, double p){
ime[rb - 1] = i; poeni[rb - 1] = p;
}
...
}
IZUZECI BEZ PROVERE
17/21
Šta se dešava ako je brojMesta negativan broj?Generiše se NegativeArraySizeException
double d;
try{ d = Double.parseDouble(x[1]); }
catch(NegativeArraySizeException e){ d = -1; }
- finally blok odrađuje se na kraju try-catch naredbe, bilo da je izuzetak nastao ili ne. Obično se u finally bloku oslobađaju resursi (zatvaranje datoteka, veza ka bazi i sl.)
- Sadržaj finally bloka mogao bi da stoji na istom mestu i bez finally naredbe, pa zašto se onda finally koristi?- Sadržaj finally bloka izvršava se čak i ako se ako se izvrši return u try ili catch blokovima
- Ako se izuzetak desio u try bloku, ali nije uhvaćen, sadržaj finally bloka se opet izvršava
NAREDBA FINALLY
18/21
FileWriter fw = null;
try{
fw = new FileWriter(imeDatoteke));
fw.write("" + ime.length + “\n");
for(int i = 0; i < ime.length; i++){
fw.write(ime[i] + ":" + poeni[i] + "\n");
}
// fw.close(); // prebaceno u finally blok
} catch(IOException e){
System.out.println("Greska pri snimanju u " + imeDatoteke);
} finally{
try { fw.close(); } catch(IOException e1) {}
}
NAREDBA FINALLY
19/21
import java.io.FileWriter; import java.io.IOException;
...
public static RangLista ucitajV3(String imeDatoteke){
RangLista rl;
BufferedReader r = null;
try{
r = new BufferedReader(new FileReader(imeDatoteke));
String linija; int i = 0;
rl = new RangLista(Integer.parseInt(r.readLine()));
while( (linija = r.readLine()) != null ) {
String[] x = linija.split(":");
rl.ubaci(i, x[0], Double.parseDouble(x[1]));
i++;
}
} catch(FileNotFoundException e) {
System.out.println("Ne postoji datoteka sa imenom " + imeDatoteke);
rl = null;
} catch(IOException e) {
System.out.println("Greska pri citanju " + imeDatoteke);
rl = null;
} finally { try{ r.close(); } catch(IOException e){} }
return rl;
}
Ako se desi izuzetak koji nijeuhvaćen, opet se pre prekida programa izvršava finally blok(NumberFormatException)
- Ukoliko se ne obrade unutar metode M na mestu gde nastaju, izuzeci se mogu proslediti iz M u pozivajuću metodu da ih ona obradi
- Prosleđivanje se vrši onda kada metoda nema dovoljno informacija da obradi izuzetak
- Prosleđivanje može da se proteže kroz više nivoa
PROSLEĐIVANJE IZUZETAKA – THROWS NAREDBA
20/21
public void snimiV4(String imeDatoteke) throws IOException {
FileWriter fw = new FileWriter(imeDatoteke);
fw.write("" + ime.length + "\n");
for(int i = 0; i < ime.length; i++){
fw.write(ime[i] + ":" + poeni[i] + "\n");
}
fw.close();
}public static void testSnimiV4(){
RangLista rl = new RangLista(10);
rl.ubaci(1, "Pera Perin", 100);
rl.ubaci(2, "Mika Mikic", 90);
try{
rl.snimiV4("/test/rlista.txt");
} catch(IOException e){
System.out.println("Problem pri snimanju");
}
}
Metoda snimiV4 prosleđuje izuzetak u metodu testSnimiV4