1. Külső erőforrások

Ahogy egy Java program fut, néha kölcsönhatásba lép a Java gépen kívüli entitásokkal. Például a lemezen lévő fájlokkal. Ezeket az entitásokat általában külső erőforrásoknak nevezik. A belső erőforrások a Java gépen belül létrehozott objektumok.

Az interakció általában a következő sémát követi:

Próbálja ki az erőforrásokkal nyilatkozatot

Erőforrások nyomon követése

Az operációs rendszer szigorúan nyomon követi a rendelkezésre álló erőforrásokat, és szabályozza a különböző programok megosztott hozzáférését is. Például, ha az egyik program módosít egy fájlt, akkor egy másik program nem tudja módosítani (vagy törölni) azt a fájlt. Ez az elv nem korlátozódik a fájlokra, de a legkönnyebben érthető példát nyújtják.

Az operációs rendszernek vannak olyan funkciói (API-k), amelyek lehetővé teszik a program számára, hogy erőforrásokat szerezzen be és/vagy felszabadítson. Ha egy erőforrás foglalt, akkor csak az azt megszerző program tud vele dolgozni. Ha egy erőforrás ingyenes, akkor bármelyik program megszerezheti.

Képzelje el, hogy az irodájában közös kávésbögrék vannak. Ha valaki elvesz egy bögrét, akkor mások már nem vehetik el. De ha a bögrét használtuk, kimostuk és visszatesszük a helyére, akkor bárki újra magával viheti. Ugyanez a helyzet a buszon vagy metrón lévő ülésekkel. Ha szabad az ülés, akkor bárki elfoglalhatja. Ha egy ülés foglalt, akkor azt az a személy irányítja, aki elfoglalta.

Külső erőforrások beszerzése .

Minden alkalommal, amikor a Java program elkezd dolgozni egy fájllal a lemezen, a Java gép kizárólagos hozzáférést kér az operációs rendszertől. Ha az erőforrás szabad, akkor a Java gép beszerzi.

De miután végzett a fájllal, ezt az erőforrást (fájlt) fel kell szabadítani, azaz értesítenie kell az operációs rendszert, hogy már nincs rá szüksége. Ha ezt nem teszi meg, akkor az erőforrás továbbra is a program birtokában marad.

Az operációs rendszer egy listát vezet az egyes futó programok által elfoglalt erőforrásokról. Ha a program túllépi a hozzárendelt erőforrás-korlátot, akkor az operációs rendszer többé nem ad új erőforrásokat.

A jó hír az, hogy ha a program leáll, minden erőforrás automatikusan felszabadul (ezt maga az operációs rendszer teszi).

A rossz hír az, hogy ha kiszolgálóalkalmazást írunk (és sok szerveralkalmazás Java nyelven íródott), akkor a szerverünknek napokig, hetekig és hónapokig megállás nélkül kell működnie. És ha naponta 100 fájlt nyit meg, és nem zárja be, akkor néhány hét múlva az alkalmazás eléri az erőforrás-korlátot és összeomlik. Ez messze elmarad a több hónapos stabil munkától.


2. close()módszer

A külső erőforrásokat használó osztályoknak speciális módszerük van a felszabadításukra: close().

Az alábbiakban egy példát mutatunk be egy olyan programra, amely egy fájlba ír valamit, majd ha ez kész, bezárja a fájlt, azaz felszabadítja az operációs rendszer erőforrásait. Valahogy így néz ki:

Kód jegyzet
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
A fájl elérési útja.
Szerezze be a fájlobjektumot: szerezze be az erőforrást.
Írás a fájlba
Zárja be a fájlt - engedje fel az erőforrást

Miután egy fájllal (vagy más külső erőforrással) dolgozott, meg kell hívnia a close()metódust a külső erőforráshoz csatolt objektumon.

Kivételek

Egyszerűnek tűnik az egész. A program futása közben azonban előfordulhatnak kivételek, és a külső erőforrás nem szabadul fel. És ez nagyon rossz.

Annak érdekében, hogy a metódus mindig meghívásra kerüljön, a kódunkat egy - - blokkba close()kell csomagolnunk , és hozzá kell adnunk a metódust a blokkhoz. Valahogy így fog kinézni:trycatchfinallyclose()finally

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Ez a kód nem fordítható le, mert a outputváltozó a blokkon belül van deklarálva try {}, ezért nem látható a blokkban finally.

Javítsuk ki:

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Rendben van, de nem működik, ha hiba történik az objektum létrehozásakor FileOutputStream, és ez könnyen megtörténhet.

Javítsuk ki:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Még mindig van néhány kritika. Először is, ha hiba történik az objektum létrehozásakor FileOutputStream, akkor a outputváltozó null lesz. Ezzel a lehetőséggel a blokkban számolni kell finally.

Másodszor, a close()metódust mindig a blokkban hívják meg finally, ami azt jelenti, hogy a blokkban nem szükséges try. A végső kód így fog kinézni:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output != null)
      output.close();
}

Ha nem is vesszük figyelembe a catchblokkot, ami kihagyható, akkor a 3 kódsorunk 10 lesz. De lényegében csak megnyitottuk a fájlt és 1-et írtunk. Kicsit körülményes, nem gondolod?


3. try-forrásokkal

És itt a Java alkotói úgy döntöttek, hogy ránk szórnak egy kis szintaktikai cukrot. A 7. verziótól kezdve a Java új try-with-resources utasítással rendelkezik.

Pontosan azért hozták létre, hogy megoldja a problémát a metódus kötelező meghívásával close(). Az általános eset meglehetősen egyszerűnek tűnik:

try (ClassName name = new ClassName())
{
     Code that works with the name variable
}

Ez egy másik változata az try állításnak . A kulcsszó után zárójeleket kell hozzáadnia try, majd a zárójelben lévő külső erőforrásokkal objektumokat kell létrehoznia. A fordító minden zárójelben lévő objektumhoz hozzáad egy finallyszakaszt és egy hívást a metódushoz close().

Az alábbiakban két egyenértékű példa látható:

Hosszú kód Kód kipróbálható erőforrásokkal
FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
finally
{
   if (output != null)
   output.close();
}
try(FileOutputStream output = new FileOutputStream(path))
{
   output.write(1);
}

A -with-resources kódot használó kód trysokkal rövidebb és könnyebben olvasható. És minél kevesebb kódunk van, annál kisebb az esélye annak, hogy elírást vagy egyéb hibát vétünk.

A -with-resources utasításhoz egyébként hozzáadhatunk catchés blokkokat is . Vagy nem adhatja hozzá őket, ha nincs rájuk szükség.finallytry



4. Több változó egyszerre

Egyébként gyakran találkozhat olyan helyzettel, amikor több fájlt kell egyszerre megnyitnia. Tegyük fel, hogy egy fájlt másol, tehát két objektumra van szüksége: arra a fájlra, amelyből adatokat másol, és arra a fájlra, amelybe adatokat másol.

Ebben az esetben a try-with-resources utasítással egy, de több objektumot hozhat létre benne. Az objektumokat létrehozó kódot pontosvesszővel kell elválasztani. Íme az állítás általános megjelenése:

try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
   Code that works with the name and name2 variables
}

Példa fájlok másolására:

Hosszú kód Rövid kód
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

FileInputStream input = null;
FileOutputStream output = null;

try
{
   input = new FileInputStream(src);
   output = new FileOutputStream(dest);

   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
finally
{
   if (input != null)
      input.close();
   if (output != null)
      output.close();
}
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}

Nos, mit is mondhatnánk itt? try-Az erőforrásokkal csodálatos dolog!


5. AutoCloseableinterfész

De ez még nem minden. A figyelmes olvasó azonnal elkezdi keresni azokat a buktatókat, amelyek korlátozzák ennek az állításnak az alkalmazását.

De hogyan tryműködik a -with-resources utasítás, ha az osztálynak nincs metódusa close()? Nos, tegyük fel, hogy semmit sem fognak hívni. Nincs módszer, nincs probléma.

De hogyan tryműködik a -with-resources utasítás, ha az osztálynak több close()metódusa van? És érvekre van szükségük, hogy átadják nekik? És az osztálynak nincs close()paraméter nélküli metódusa?

Remélem, valóban feltetted magadnak ezeket a kérdéseket, és talán még másoknak is.

Az ilyen problémák elkerülése érdekében a Java készítői egy speciális felülettel rukkoltak elő AutoCloseable, melynek egyetlen metódusa van – close()aminek nincsenek paraméterei.

Hozzátették azt a korlátozást is, hogy csak a megvalósított osztályok objektumaiAutoCloseable deklarálhatók erőforrásként a try-with-resources utasításban. Ennek eredményeként az ilyen objektumok mindig rendelkeznek close()paraméter nélküli metódussal.

Egyébként szerinted lehetséges, hogy egy try-with-resources utasítás erőforrásként deklarál egy olyan objektumot, amelynek az osztályának saját, close()paraméterek nélküli metódusa van, de amely nem implementálja a -t AutoCloseable?

A rossz hír: A helyes válasz nem – az osztályoknak implementálniuk kell az AutoCloseableinterfészt.

A jó hír: a Java-nak nagyon sok osztálya van, amelyek ezt a felületet implementálják, így nagyon valószínű, hogy minden úgy fog működni, ahogy kell.