![translation](https://cdn.durumis.com/common/trans.png)
To jest post przetłumaczony przez AI.
Wybierz język
Tekst podsumowany przez sztuczną inteligencję durumis
- Tworzenie instancji String lub Boolean za pomocą słowa kluczowego new jest marnotrawstwem pamięci, dlatego zaleca się deklarowanie ich jako literałów lub użycie metody Boolean.valueOf().
- Metoda String.matches() wykorzystuje wyrażenia regularne, co może prowadzić do problemów z wydajnością. Lepiej jest buforować instancje Pattern i ponownie ich używać.
- W przypadku zwracania obiektów widoku, takich jak metoda keySet(), bezpieczniej jest użyć kopii obronnej, aby zwrócić nowy obiekt.
W przypadku tworzenia niepotrzebnych obiektów
Użycie new String()
String a = new String("hi");
String b = new String("hi");
Ciągi znaków a, b i c będą zawierały ten sam ciąg znaków „hi”. Jednak, ponieważ adresy, na które odwołują się te trzy ciągi znaków, są różne, występuje marnotrawstwo, ponieważ przydzielana jest oddzielna pamięć dla tych samych danych.
Dlatego podczas deklarowania ciągów znaków, zamiast słowa kluczowego new należy użyć literału.
String a = "hi";
String b = "hi";
Powyższy kod wykorzystuje tylko jeden egzemplarz. Co więcej, użycie tej metody gwarantuje, że wszystkie kody korzystające z literału ciągu znaków „hi” w tej samej maszynie wirtualnej Java będą współdzielić ten sam obiekt. Jest tak ze względu na funkcję puli stałych w Javie.
Użycie new Boolean()
Powyższy kod tworzy instancję Boolean za pomocą konstruktora, który przyjmuje ciąg znaków jako argument. Boolean może być tylko true lub false, a tworzenie instancji za każdym razem jest marnotrawstwem pamięci. Dlatego lepiej jest użyć statycznej metody fabrycznej Boolean.valueOf().
Użycie String.matches()
Jeśli koszt tworzenia jest duży, lepiej jest je buforować i ponownie wykorzystywać, ale nie zawsze wiemy, jaki jest koszt tworzenia przez nas obiektów. Na przykład, jeśli chcesz napisać metodę sprawdzającą, czy dany ciąg znaków jest prawidłową liczbą rzymską, najłatwiej jest skorzystać z wyrażeń regularnych, jak pokazano poniżej.
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})$");
Jednak String.matches() jest metodą, która ma problemy z wydajnością. Instancja Pattern, która jest tworzona wewnętrznie przez tę metodę dla wyrażeń regularnych, jest używana tylko raz i jest natychmiast usuwana przez kolekcjoner śmieci. Jeśli częstotliwość wykorzystywania wyrażeń regularnych jest duża, koszt tworzenia i usuwania tych samych instancji Pattern wzrasta. Dlatego lepiej jest buforować instancję Pattern z wyprzedzeniem i ponownie ją wykorzystywać przy każdym wywołaniu metody isRomanNumeral().
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();
}
Uwaga
We wszystkich powyższych przykładach, podczas buforowania niepotrzebnych obiektów, wszystkie obiekty zostały utworzone jako niezmienne. Jest tak dlatego, że tylko wtedy są one bezpieczne do ponownego użycia. Jednak, istnieją przypadki, które są sprzeczne z intuicją, że ponowne użycie jest możliwe tylko wtedy, gdy obiekt jest niezmienny.
Adapter (widok) to obiekt, który deleguje rzeczywistą pracę do obiektu zaplecza, a sam działa jako drugi interfejs. Adapter musi zarządzać tylko obiektem zaplecza, więc wystarczy stworzyć jeden adapter na każdy obiekt zaplecza.
Na przykład metoda keySet() interfejsu Map zwraca widok Set zawierający wszystkie klucze obiektu Map. Można by pomyśleć, że za każdym wywołaniem metody keySet() tworzona jest nowa instancja Set, ale w rzeczywistości, patrząc na implementację JDK, za każdym razem zwracana jest ta sama zmienna instancja Set.
Jest tak dlatego, że chociaż zwracana instancja Set jest zmienna, funkcje, które wykonuje, są identyczne dla wszystkich instancji Set, a wszystkie instancje Set reprezentują instancję Map. Dlatego, nawet jeśli metoda keySet() utworzy wiele obiektów widoku, nie ma powodu ani korzyści z tego.
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
}
Dlatego modyfikując instancję names1, jak pokazano powyżej, wpływa to również na instancję names2.
Osobiście jednak uważam, że wartość zwracana przez metodę keySet() powinna być zawsze nowym obiektem, utworzonym za pomocą kopiowania obronnego. Jeśli instancja Set, która została odebrana z metody keySet(), jest również używana gdzie indziej i istnieje kod, który zmienia jej stan, to nie można być pewnym stanu bieżącej używanej instancji Set i instancji Map.
Ponadto, o ile środowisko nie jest nadmiernie obciążone używaniem metody keySet(), utworzenie interfejsu Set za każdym razem nie ma negatywnego wpływu na wydajność. Myślę, że lepiej jest tworzyć interfejs Set jako obiekt niezmienny, aby zapewnić stabilność i łatwość konserwacji.
Autoboxing
Autoboxing to technika, która automatycznie konwertuje typy podstawowe i typy opakowaniowe, gdy programista używa ich razem. Jednak autoboxing nie eliminuje całkowicie różnicy między typami podstawowymi i typami opakowaniowymi, a jedynie zaciera ją.
public static long sum() {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
Logicznie rzecz biorąc, nie ma tu problemu, ale jest to kod bardzo nieefektywny pod względem wydajności. Przyczyną tego jest typ sum i typ i znajdujący się w pętli for.
Typ sum to Long, a i to long. Oznacza to, że long typu i podczas iterowania przez pętlę i dodawania do sum, tworzy nową instancję Long. W rezultacie, należy używać typów podstawowych zamiast typów opakowaniowych i zwracać uwagę na to, aby niepotrzebne autoboxingi nie były używane.
Części, których należy unikać
Nie należy mylić uniknięcia niepotrzebnego tworzenia obiektów z tym, że koszty tworzenia obiektów są wysokie, więc należy ich unikać.
Szczególnie w dzisiejszych czasach w maszynach wirtualnych Java, tworzenie i usuwanie małych obiektów, które są niepotrzebne, nie jest zbyt uciążliwe. Dlatego nie należy tworzyć własnej puli obiektów, chyba że jest to obiekt o dużym koszcie, taki jak połączenie z bazą danych.
Co więcej, należy pamiętać, że szkoda spowodowana ponownym użyciem obiektów w sytuacji, w której potrzebne jest kopiowanie obronne, jest znacznie większa niż szkoda spowodowana powtarzającym się tworzeniem obiektów. Powtarzające się tworzenie wpływa tylko na formę kodu i wydajność, ale awaria kopiowania obronnego prowadzi bezpośrednio do błędów i problemów z bezpieczeństwem.