![translation](https://cdn.durumis.com/common/trans.png)
Ez egy AI által fordított bejegyzés.
[Hatékony Java] 6. pont: Kerülje a felesleges objektum létrehozását
- Írás nyelve: Koreai
- •
-
Referencia ország: Minden ország
- •
- Informatika
Válasszon nyelvet
A durumis AI által összefoglalt szöveg
- A new kulcsszó használatával létrehozott String vagy Boolean példányok memória pazarláshoz vezetnek, ezért a literálok megadásával vagy a Boolean.valueOf() metódus használatával célszerűbb őket létrehozni.
- A String.matches() metódus reguláris kifejezéseket használ, ami teljesítményproblémákat okozhat. Javasolt a Pattern példányok gyorsítótárazása és újrafelhasználása.
- Ha a metódusok, mint például a keySet(), nézet objektumokat adnak vissza, akkor védő másolás használatával új objektumot kell visszaadni, ami biztonságosabb.
Ha szükségtelen objektumokat hozunk létre
`new String()` használata
String a = new String("hi");
String b = new String("hi");
Az `a`, `b` és `c` sztringek mind "hi" sztringet tartalmaznak. De mivel a három sztring különböző címeket mutat, a felesleges memória-allokáció miatt pazarlásnak minősül, mivel a sztringek ugyanazokat az adatokat tartalmazzák.
Ezért a sztringek deklarálásakor a `new` kulcsszó helyett szó szerint kell deklarálni.
String a = "hi";
String b = "hi";
A fenti kódrészlet csak egyetlen példányt használ. Továbbá, ha ezt a módszert használjuk, akkor garantáljuk, hogy ugyanazon JVM-en belül minden "hi" szó szerinti sztringet használó kód ugyanazt a példányt használja újra. Ez a Java konstans-pool tulajdonsága miatt van.
`new Boolean()` használata
A fenti kód egy `Boolean` példányt hoz létre egy sztringet tartalmazó konstruktor segítségével. A `Boolean` értéke csak `true` vagy `false` lehet, ezért minden alkalommal, amikor egy példányt hozunk létre, memóriapazarlásnak minősül. Ezért jobb, ha a `Boolean.valueOf()` statikus gyármetódust használjuk.
`String.matches()` használata
Ha a létrehozási költség magas, akkor a gyorsítótárazás és az újrafelhasználás jobb megoldás, de nem mindig tudjuk, mennyi a létrehozott objektum költsége. Például, ha olyan metódust szeretnénk írni, amely ellenőrzi, hogy egy adott sztring érvényes római szám-e, akkor a legegyszerűbb megoldás, ha a következőképpen használjuk a reguláris kifejezést.
public static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
De a `String.matches()` metódusnak vannak teljesítménybeli problémái. A metódus által belsőleg létrehozott reguláris kifejezéshez tartozó `Pattern` példány egy használat után eldobásra kerül, és gyorsan a szemétgyűjtő célpontjává válik. Ha a reguláris kifejezés gyakran használatos, akkor a `Pattern` példány létrehozásának és eldobásának költsége növekszik. Ezért célszerű előre gyorsítótárazni a `Pattern` példányt, és később minden alkalommal, amikor az `isRomanNumeral()` metódust hívják, újrahasználni a példányt.
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile(
"^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
public static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
Figyelem
A fenti példákban minden esetben, amikor szükségtelen objektumokat gyorsítótárazunk, a példányokat változatlanná tettük. Ez azért van, mert biztonságos az újrafelhasználás. De vannak olyan esetek, amelyek ellentmondanak a változatlan objektumok újrafelhasználásának intuíciójának.
Az adapter (nézet) olyan objektum, amely a tényleges munkát a háttérbeli objektumra delegálja, és maga egy második interfész szerepét játssza. Az adapternek csak a háttérbeli objektumot kell kezelnie, ezért elegendő, ha egy háttérbeli objektumhoz egy adaptert hozunk létre.
Például a `Map` interfész `keySet()` metódusa visszaad egy `Set` nézetet, amely tartalmazza a `Map` objektum összes kulcsát. A felhasználó azt gondolhatja, hogy minden alkalommal, amikor a `keySet()` metódust hívjuk, egy új `Set` példányt hozunk létre, de a valóságban a JDK megvalósításban minden alkalommal ugyanazt a változtatható `Set` példányt adja vissza.
Ez azért van, mert a visszaadott `Set` példány változtatható, de a végrehajtott funkciók azonosak, és minden `Set` példány képviseli a `Map` példányt. Ezért nem számít, ha a `keySet()` több nézetobjektumot hoz létre, de nincs szükség rá, és nincs előnye sem.
public class UsingKeySet {
public static void main(String[] args) {
Map menu = new HashMap<>();
menu.put("Burger", 8);
menu.put("Pizza", 9);
Set names1 = menu.keySet();
Set names2 = menu.keySet();
names1.remove("Burger");
System.out.println(names1.size()); // 1
System.out.println(names2.size()); // 1
}
Ezért, ha a `names1` példányt módosítjuk, a `names2` példányt is befolyásolja.
De szerintem jobb lenne, ha a `keySet()` metódus védő másolatot használnál, és minden alkalommal egy új objektumot adná vissza. Ha a `keySet()` metódus által kapott `Set` példányt máshol is használják, és a példány állapotát megváltoztató kód van, akkor nem lehetünk biztosak a jelenleg használt `Set` példány és a `Map` példány értékeiben.
Továbbá, ha nem túl gyakran használják a `keySet()` metódust, akkor a `Set` interfész létrehozása nem befolyásolja jelentősen a teljesítményt. Én inkább azt gondolom, hogy jobb lenne, ha a `Set` interfészt változatlanná tennék, így stabil módon lehetne karbantartani.
Automatikus dobozolása
Az automatikus dobozolása olyan technika, amely automatikusan konvertálja a primitív típusokat és a burkoló típusokat, ha a programozó keveri őket. De az automatikus dobozolása nem szünteti meg teljesen a primitív típusok és a burkoló típusok közötti különbséget, csak elhomályosítja.
public static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
Logikailag nincs probléma, de teljesítmény szempontból nagyon hatékonytalan kód. Ennek az oka a `sum` típusa és a cikluson belül lévő `i` típusa.
A `sum` típusa `Long`, az `i` típusa pedig `long`. Ez azt jelenti, hogy az `i` (ami `long` típusú) minden egyes ciklussal, amikor hozzáadódik a `sum`-hoz, egy új `Long` példányt hoz létre. Következésképpen, jobb a burkoló típusok helyett a primitív típusokat használni, és oda kell figyelni, hogy ne történjen nem kívánt automatikus dobozolása.
Dolgok, amelyeket nem szabad félreérteni
Nem szabad félreérteni, hogy a szükségtelen objektumok létrehozásának elkerülése azt jelenti, hogy az objektumok létrehozásának költségei magasak, és ezért kellene kerülni őket.
Különösen a modern JVM-ben a szükségtelenül létrehozott kis objektumok létrehozása és felszabadítása nem jelent nagyobb terhet. Ezért, hacsak nem olyan objektumról van szó, mint például a adatbázis-kapcsolat, amelynek a költsége nagyon magas, ne hozzunk létre egyéni objektum-poolt.
Továbbá ne felejtsük el, hogy a védő másolat szükséges helyzetekben az objektumok újrafelhasználásakor fellépő károk sokkal nagyobbak, mint ha szükségtelen objektumokat hoznánk létre. A megismétlődő létrehozásnak csak a kód formátumára és a teljesítményre van hatása, de ha a védő másolás sikertelen, akkor hibákhoz és biztonsági problémákhoz vezet.