Книга е още в много ранна фаза на написване



страница56/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   52   53   54   55   56   57   58   59   ...   73

Конструктори


Когато се пише код за изключения особено важно е да се питаме “ако се случи из­ключение, това ще бъде ли правилно почистено?” Повечето време е съвсем бе­зопасно, но с конструкторите има проблем. Конструкторът извежда обекта до безопасно стартово състояние, но би могъл да изпълнява и някаква операция – като отваряне на файл – която да не бъде почистена (откъм въздействията се - б.пр.) докато потребителят не изпълни специално написан за това код. Ако из­хвър­лите изключение извътре на конструктор този код може и да не се из­пъл­ни, почистването да не стане правилно. Това значи че трябва да бъдете особено при­лежни когато пишете своя конструктор.

Понеже току-що научихте за finally, може да помслите, че това е коректното решение. Не е така просто, обаче, понеже finally изпълнява кода всякога, даже и в ситуации, когато не искате да се изпълнява почистване докато не се пусне по­чистващия метод. Така, ако почиствате във finally, трябва да сложите ня­ка­къв флаг (семафор) ако конструкторът завърши нормално и само тогава да по­чист­вате (ако е сложен). Тъй като това не е много елегантно (свързвате едно мя­сто на кода си с друго), най-добре е да избягвате да правите този вид по­чист­ва­не във finally докато не ви се наложи.

В следващия пример се създава клас наречен InputFile, той отваря файл и дава въз­можност да се прочете един ред от него (превъърнат в String) наведнъж. Из­полз­вани са класовете FileReader и BufferedReader от стандартната входно-из­ход­на библиотека на Java която ще се разгледа в глава 10, но които са до­ста­тъч­но прости така че няма да имате проблеми с разбирането на основната им упо­тре­ба:

//: c09:Cleanup.java

// Paying attention to exceptions

// in constructors

import java.io.*;
class InputFile {

private BufferedReader in;

InputFile(String fname) throws Exception {

try {


in =

new BufferedReader(

new FileReader(fname));

// Other code that might throw exceptions

} catch(FileNotFoundException e) {

System.out.println(

"Could not open " + fname);

// Wasn't open, so don't close it

throw e;

} catch(Exception e) {

// All other exceptions must close it

try {


in.close();

} catch(IOException e2) {

System.out.println(

"in.close() unsuccessful");

}

throw e;


} finally {

// Don't close it here!!!

}

}

String getLine() {



String s;

try {


s = in.readLine();

} catch(IOException e) {

System.out.println(

"readLine() unsuccessful");

s = "failed";

}

return s;



}

void cleanup() {

try {

in.close();



} catch(IOException e2) {

System.out.println(

"in.close() unsuccessful");

}

}



}
public class Cleanup {

public static void main(String[] args) {

try {

InputFile in =



new InputFile("Cleanup.java");

String s;

int i = 1;

while((s = in.getLine()) != null)

System.out.println(""+ i++ + ": " + s);

in.cleanup();

} catch(Exception e) {

System.out.println(

"Caught in main, e.printStackTrace()");

e.printStackTrace();

}

}

} ///:~



Този пример използва класовете на Java 1.1 вход-изхода.

Конструкторът на InputFile взема String аргумент, който е името на файла, кой­то ще се отваря. Вътре в try блока той създава FileReader използвайки име­то. FileReader не е особено полезен докато не го използвате за създаване на BufferedReader на който фактическиможе да говорите – забележете че една от пол­зите от InputFile е че комбинира тези две действия.

Ако конструктора на FileReader е неуспешен, той изхвърля FileNotFoundException, което трябва да се хване отделно, понеже иначе ще ис­ка­те да затваряте файл който не е бил отворен. Всякакви други catch клаузи тряб­ва да затворят файла понеже той е бил отворен към времето, когато се е вли­зало в тях. (Разбира се, още по-измамно е ако повече от един метод би мо­гъл да изхвърли FileNotFoundException. В този сбучай може да поискате да раз­делите нещата в няколко try блока.) Методът close( ) изхвърля изключение кое­то се опитва и хваща даже и да е вътре в друга catch клауза – това е само дру­га двойка фигурни скоби за компилатора на Java. След изпълнение на ло­кал­ни­те опаерации изключението се преизхвърля, което е подходящо понеже този конструктор се е провалил, а вие не искате извикваният метод да смята, че всич­ко е наред.

В този пример, който не използва споменатата преди техника с флаговете, finally клаузата определено не е мястото за close( ) файла, повеже ще го затваря все­ки път когато конструкторът завършва работата си. Тъй като искаме файлът да бъде отворен за полезна работа времето на живот на InputFile обекта не би би­ло подходащо в такъв случай.

Методът getLine( ) връща String съдържащ следващия ред от файла. Той вика readLine( ), който може да изхвърли изключение, но то се хваща, така че getLine( ) не изхвърля никакви изключения. Един от въпросите къито въз­ник­ват с изключенията е дали да се обработват изцяло на това ниво, или да се обра­бо­тят частично и да се подаде същото изключение (или някое друго) нататък, или просто да се предаде нататък. Предаването му, когато е уместно, опре­де­ле­но може да опрости програмирането. getLine( ) методът става:

String getLine() throws IOException {

return in.readLine();

}

Но разбира се, извикващият метод сега е отговорен за бработката на всякакво IOException което би могло да възникне.



cleanup( ) методът трябва да бъде извикано от потребителя когато свърши използването на InputFile обекта за да се освободят заеманите системни ре­сур­си (като файлови манипулатори) заети чрез BufferedReader и/или FileReader обек­ти.6 Не искаме да правим това докато не свършим с InputFile обекта, точ­ка­та, в която го искаме. Може да се мисли за слагане на такава функционалност във finalize( ) метод, но както се спомена в глава 4 не може да бъдем сигурни, че ще се извика някога finalize( ) (даже и да можехте да бъдете сигурни че ще се извика, не знаете кога). Това е един от минусите на Java – всички финални дей­ствия освен освобождаването на паметта не стават автоматично, така че тряб­ва да информирате клиентите-програмисти че са отговорни, та може би да га­рантират нещата чрез използване на finalize( ).

В Cleanup.java се създава InputFile за да отвори сорса на тази програма, файлът се чете ред по ред, добавят се номера на редовете. Всички изключения се хващат родово в main( ), макар и да бихте избрали, може би, по-голяма неед­но­родност.

Една от ползите на този пример е да се покаже защо изключенията бяха въведени точно на това място в книгата. Изключенията са толкова интегрирани в програмирането на Java, особено понеже компилаторът ги прави задъл­жи­тел­ни, че може да свършите само толкова (колкото знаете до тази глава-б.пр.) без да ги познавате.




Сподели с приятели:
1   ...   52   53   54   55   56   57   58   59   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница