1. Giriş
Java-da fayllarla işləmək (və təkcə bu deyil!) — həmişə xarici resurslarla iş deməkdir. Faylı açdığınız zaman əməliyyat sistemi proqramınız üçün “deskriptor” — faylı oxumağa və yazmağa imkan verən xüsusi identifikator ayırır. Belə deskriptorların sayı məhduddur: faylları bağlamasanız, proqram tez bir zamanda bütün əlçatan resursları “yeyə” və "Too many open files" tipli müəmmalı xətalar atmağa başlaya bilər.
Daha da önəmlisi, fayl bağlanmazsa, o, digər proqramlar üçün kilidli qala bilər. Məsələn, faylı yazmaq üçün açdınız, onu bağlamağı unutdunuz və indi nə siz, nə də başqası onu dəyişə və ya silə bilir. Fayl sistemləri dünyasında “əbədi girov götürmə” kimi bir şey.
Həyatdan nümunə
FileInputStream fis = new FileInputStream("data.txt");
int b = fis.read();
// ... nəsə edirik, sonra isə fis.close()-u unutduq
Əgər close() metodunu çağırmasanız, fayl proqram bitənədək “asanacaq”. Böyük tətbiqlərdə bu, resurs sızmalarına və hətta tətbiqin çökməsinə səbəb ola bilər.
2. Köhnə üsul: finally + close()
Java 7-yə qədər faylı zəmanətli bağlamağın klassik üsulu belə görünürdü:
FileInputStream fis = null;
try {
fis = new FileInputStream("data.txt");
// faylı oxuyuruq
int b = fis.read();
// ...
} catch (IOException e) {
System.out.println("Fayl oxunarkən xəta: " + e.getMessage());
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.out.println("Fayl bağlanarkən xəta: " + e.getMessage());
}
}
}
Bu yanaşmanın mənfi cəhətləri
- finally blokunu unutmaq asandır və resurs sızmasına səbəb ola bilər.
- Artıq kod çoxdur, xüsusən də bir neçə axın olduqda.
- Bağlanma zamanı istisna baş verərsə, onu da ayrıca tutmaq lazımdır.
- Kod həddən artıq həcmli və az oxunaqlı olur.
3. Müasir yanaşma: try-with-resources
Xoşbəxtlikdən, Java 7-də bu problemləri zərif və avtomatik həll edən sintaksis peyda oldu — try-with-resources.
Bu necə görünür
try (FileInputStream fis = new FileInputStream("data.txt")) {
int b = fis.read();
// faylla işləyirik
} catch (IOException e) {
System.out.println("Faylla işləyərkən xəta: " + e.getMessage());
}
// Burada fis artıq avtomatik bağlanıb!
Əsas üstünlük: try-dan sonra dırnaqarası mötərizələrdə elan olunan bütün resurslar blok bitdikdən sonra avtomatik bağlanacaq — hətta ortada istisna yaranarsa belə. finally yazmağa ehtiyac yoxdur, bağlama xətalarını ayrıca tutmağa gərək yoxdur — Java hər şeyi sizin yerinizə edəcək.
try-with-resources-da hansı siniflərdən istifadə etmək olar?
AutoCloseable (və ya köhnə yaxşı Closeable) interfeysini reallaşdıran istənilən sinif. Bu, demək olar ki, bütün standart giriş-çıxış axınlarını əhatə edir: FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, Scanner, PrintWriter və daha başqaları.
4. try-with-resources sintaksisi: detallar və nümunələr
Tək resurs
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("Xəta: " + e.getMessage());
}
// reader avtomatik bağlanıb!
Bir neçə resurs
Resursları nöqtəli vergül vasitəsilə bir neçə dənə birbaşa elan etmək olar:
try (
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
System.out.println("Kopyalama zamanı xəta: " + e.getMessage());
}
// hər iki axın bağlanıb!
Bağlanma qaydası: resurslar onların elan olunma sırasının əksinə bağlanır. Əvvəlcə writer.close(), sonra reader.close() çağırılacaq. Bu, bir axın digərindən asılı olduqda vacibdir.
Öz siniflərinizlə istifadə
Əgər resurslarla işləyən öz sinifinizi yazırsınızsa, sadəcə AutoCloseable interfeysini reallaşdırın:
class MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("Resursla işləyirik!");
}
@Override
public void close() {
System.out.println("Resurs bağlandı!");
}
}
try (MyResource res = new MyResource()) {
res.doSomething();
}
// Blokdan çıxanda belə çap olunacaq: "Resurs bağlandı!"
5. Bu necə işləyir: sxem
flowchart TD
A[Resursun try-with-resources içində açılması] --> B{try blokunda istisna yarandı?}
B -- Xeyr --> C[Resurs avtomatik bağlanır]
B -- Bəli --> D[Resurs avtomatik bağlanır]
D --> E[İstisna irəli ötürülür]
C --> F[Proqram icranı davam etdirir]
Nəticə: xəta olub-olmamasından asılı olmayaraq, resurs həmişə bağlanacaq!
6. Nümunələr: kodu “yeni üsulla” yenidən yazırıq
Əvvəl (köhnə tərz):
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("input.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("Xəta: " + e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.out.println("Bağlanarkən xəta: " + e.getMessage());
}
}
}
Sonra (try-with-resources):
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("Xəta: " + e.getMessage());
}
// Bu qədər, finally-ə ehtiyac yoxdur!
7. Bağlama zamanı xəta baş verərsə nə olur?
Bəzən resursun bağlanması əməliyyatı özü də istisna ata bilər (məsələn, fayl birdən-birə yox olarsa). try-with-resources-də belə istisnalar itməz: əgər try blokunda artıq bir istisna baş veribsə və resursun bağlanması zamanı ikinci bir istisna yaranarsa, o, əsas istisnaya “basdırılmış” (suppressed exception) kimi əlavə ediləcək. Bunu Throwable.getSuppressed() metodu ilə görmək olar.
Nümunə
try (MyResource res = new MyResource()) {
throw new IOException("try blokunda xəta");
} catch (IOException e) {
System.out.println("Əsas xəta: " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Basdırılmış istisna: " + suppressed.getMessage());
}
}
8. Hansı siniflər try-with-resources-u dəstəkləyir?
Hər şey sadədir: AutoCloseable interfeysini reallaşdıran istənilən sinif. Standartlardan yalnız bəziləri:
| Sinif | Təyinat |
|---|---|
|
Fayldan baytların oxunması |
|
Fayla baytların yazılması |
|
Mətnin oxunması/yazılması |
|
Axınların buferlənməsi |
|
Formatlanmış mətnin yazılması |
|
Fayldan/konsoldan verilənlərin oxunması |
|
Sərializasiya/desərializasiya |
|
ZIP arxivləri ilə iş |
|
Şəbəkə əlaqələri |
Əgər üçüncü tərəf kitabxanaları istifadə edirsinizsə — sənədləşməyə baxın: close() metodu varsa, çox güman ki, sinif try-with-resources-u dəstəkləyir.
9. Məsləhətlər və faydalı nüanslar
Dəyişənləri try-dan kənarda elan etmək olar (Java 9-dan): artıq elan olunmuş resursları, əgər onlar final və ya “effectively final”dırsa, istifadə etmək mümkündür:
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try (reader) {
// ...
}
Yalnız fayllarla məhdudlaşmır: try-with-resources istənilən resurs üçün əlverişlidir: şəbəkə bağlantıları, verilənlər bazaları, close() metodu olan hər hansı obyektlər.
İstisnaları görməzlikdən gəlməyin: try-with-resources olsa belə, istisnaları tutub emal etməyi unutmayın — bu, bütün problemlər üçün panacea deyil, sadəcə sızmaların qarşısını almağın rahat yoludur.
Resursu try daxilində əl ilə bağlamayın: buna ehtiyac yoxdur — Java hər şeyi sizin yerinizə edəcək! Əgər close()-u əl ilə çağırsanız və sonra try bloku bitərsə, artıq bağlanmış resursu yenidən bağlamağa cəhd ediləcək. Adətən bu, təhlükəsizdir, amma çaşqınlıq yarada bilər.
10. try-with-resources istifadə edərkən tipik səhvlər
Səhv №1: ümumiyyətlə try-with-resources-dan istifadə etməyi unutmusunuz. Hələ də finally { resource.close(); } yazırsınızsa — ya 2011-ci ildəsiniz, ya da bu mühazirəni oxumamısınız! Müasir sintaksisdən istifadə edin.
Səhv №2: resursu try-dan kənarda elan etmisiniz, içəridə isə sadəcə istifadə edirsiniz. Belə kod resursu avtomatik bağlamayacaq:
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
try {
// ... reader-dən istifadə edirik
} finally {
// Bax burada bağlamağı unutduq!
}
Səhv №3: close()-u try blokunun içində əl ilə çağırırsınız. Bu, kritik deyil, amma artıqdır və ikiqat bağlanmaya gətirib çıxara bilər. Sadəcə Java-ya etibar edin.
Səhv №4: yalnız Exception-u tutursunuz, IO spesifikliyini nəzərə almırsınız. Daha yaxşısı konkret istisnaları tutmaqdır (FileNotFoundException, IOException), istifadəçiyə anlaşılan mesajlar vermək üçün.
Səhv №5: basdırılmış istisnaları emal etmirsiniz. Resursun bağlanması zamanı xəta yaranarsa, o “basdırıla” bilər. Xətaları təhlil edirsinizsə, getSuppressed()-i unutmayın.
GO TO FULL VERSION