CodeGym /Kurslar /JAVA 25 SELF /try-with-resources ilə işləmək: resursların avtomatik bağ...

try-with-resources ilə işləmək: resursların avtomatik bağlanması

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

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
FileInputStream
Fayldan baytların oxunması
FileOutputStream
Fayla baytların yazılması
FileReader/FileWriter
Mətnin oxunması/yazılması
BufferedReader/Writer
Axınların buferlənməsi
PrintWriter
Formatlanmış mətnin yazılması
Scanner
Fayldan/konsoldan verilənlərin oxunması
ObjectInputStream/Output
Sərializasiya/desərializasiya
ZipInputStream/Output
ZIP arxivləri ilə iş
Socket, ServerSocket
Şə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.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Faylların oxunması və yazılması
Faylların oxunması və yazılması
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION