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:
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 |
---|---|
|
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:try
catch
finally
close()
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 output
vá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 output
vá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 catch
blokkot, 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 finally
szakaszt é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 |
---|---|
|
|
A -with-resources kódot használó kód try
sokkal 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.finally
try
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 |
---|---|
|
|
Nos, mit is mondhatnánk itt? try
-Az erőforrásokkal csodálatos dolog!
5. AutoCloseable
interfé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 try
mű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 try
mű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 AutoCloseable
interfé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.
GO TO FULL VERSION