제이온

[Skuteczne Java] Element 6. Unikaj niepotrzebnego tworzenia obiektów

  • Język oryginalny: Koreański
  • Kraj: Wszystkie krajecountry-flag
  • TO

Utworzono: 2024-04-28

Utworzono: 2024-04-28 13:40

Przykłady tworzenia niepotrzebnych obiektów

Używanie `new String()`


Ciągi znaków a, b i c będą zawierały ten sam ciąg znaków „hi”. Jednakże, ponieważ adresy, do których odwołują się te trzy ciągi znaków, są różne, prowadzi to do marnotrawstwa pamięci poprzez alokację oddzielnej pamięci dla tych samych danych.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa53d14aa-6abf-40f0-8441-435647d172fa%2FUntitled.png?table=block&id=f168859c-367c-4924-a4c6-5e04788fab67&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1146;" width="2000" height="1146"></span>


Dlatego podczas deklarowania ciągów znaków należy używać literałów, a nie słowa kluczowego `new`.



Powyższy kod używa tylko jednej instancji. Co więcej, użycie tej metody gwarantuje, że wszystkie fragmenty kodu w tej samej maszynie wirtualnej Java (JVM), które używają literału ciągu znaków „hi”, będą używać tego samego obiektu. Wynika to ze sposobu działania puli stałych (constant pool) w Javie.


Używanie `new Boolean()`

Powyższy kod tworzy instancję `Boolean` za pomocą konstruktora przyjmującego ciąg znaków jako parametr. `Boolean` może przyjmować tylko wartości `true` lub `false`, więc tworzenie instancji za każdym razem jest marnotrawstwem pamięci. Dlatego lepiej jest użyć statycznej metody fabrykującej `Boolean.valueOf()`.



Używanie `String.matches()`

Jeśli koszt tworzenia obiektu jest wysoki, warto go buforować i ponownie używać. Jednak nie zawsze znamy koszt tworzenia obiektu. Na przykład, jeśli chcemy napisać metodę, która sprawdza, czy dany ciąg znaków jest prawidłową liczbą rzymską, najłatwiej jest użyć wyrażeń regularnych.



Jednak metoda `String.matches()` ma problemy z wydajnością. Instancja klasy `Pattern`, tworzona wewnętrznie przez tę metodę do obsługi wyrażeń regularnych, jest używana tylko raz i szybko staje się kandydatem do zebrania przez garbage collector. Jeśli wyrażenie regularne jest używane wielokrotnie, koszt tworzenia i niszczenia tej samej instancji `Pattern` rośnie. Dlatego lepiej jest buforować instancję `Pattern` i używać jej ponownie za każdym razem, gdy wywoływana jest metoda `isRomanNumeral()`.



**Uwaga**

We wszystkich powyższych przykładach buforowane obiekty były obiektami niezmiennymi. Jest to konieczne, aby zapewnić bezpieczeństwo ponownego wykorzystania. Jednak w niektórych przypadkach intuicja może przeczyć ponownemu wykorzystaniu niezmiennych obiektów.


Adapter (widok) to obiekt, który deleguje rzeczywistą pracę do obiektu zaplecza, a sam pełni rolę drugiego interfejsu. Adapter musi zarządzać tylko obiektem zaplecza, więc dla każdego obiektu zaplecza należy utworzyć tylko jeden adapter.


Na przykład, metoda `keySet()` interfejsu `Map` zwraca widok `Set`, który zawiera wszystkie klucze z obiektu `Map`. Użytkownik może myśleć, że za każdym razem, gdy wywołuje metodę `keySet()`, tworzona jest nowa instancja `Set`. Jednak w rzeczywistości implementacja JDK zwraca ten sam, zmienny obiekt `Set`.


Wynika to z faktu, że wszystkie funkcje wykonywane przez zwracany obiekt `Set` są identyczne, a wszystkie instancje `Set` reprezentują obiekt `Map`. Dlatego tworzenie wielu obiektów widoku przez `keySet()` nie ma znaczenia, ani nie przynosi korzyści.



Dlatego zmiana instancji `names1` wpłynie również na instancję `names2`.


Osobiście uważam, że wartość zwracana przez metodę `keySet()` powinna być kopią obronną (defensive copy), która za każdym razem zwraca nowy obiekt. Jeśli instancja `Set` otrzymana z metody `keySet()` jest używana w innym miejscu i istnieje kod, który zmienia jej stan, to nie możemy mieć pewności co do stanu obecnej instancji `Set` i instancji `Map`.


Ponadto, chyba że `keySet()` jest używana bardzo często, tworzenie instancji interfejsu `Set` za każdym razem nie ma znaczącego wpływu na wydajność. Uważam, że lepiej jest uczynić interfejs `Set` niezmiennym, aby ułatwić konserwację.


Autoboxing

Autoboxing to technika, która automatycznie konwertuje typy podstawowe na typy opakowujące i odwrotnie, gdy programista używa ich zamiennie. Jednak autoboxing tylko zaciera różnicę między typami podstawowymi a opakowującymi, nie eliminuje jej całkowicie.



Logika jest poprawna, ale kod jest bardzo nieefektywny pod względem wydajności. Powodem jest typ zmiennej `sum` i typ zmiennej `i` w pętli `for`.


Typ `sum` to `Long`, a typ `i` to `long`. Oznacza to, że zmienna `i` typu `long` za każdym razem, gdy jest dodawana do `sum`, powoduje utworzenie nowej instancji `Long`. W rezultacie lepiej jest używać typów podstawowych niż opakowujących i zwracać uwagę na przypadki niezamierzonego autoboxingu.


Czego nie należy mylić

Nie należy mylić unikania tworzenia niepotrzebnych obiektów z prostym unikaniem tworzenia obiektów, ponieważ koszt ich tworzenia jest wysoki.


Szczególnie w nowoczesnych wersjach JVM tworzenie i zwalnianie małych, niepotrzebnych obiektów nie jest dużym obciążeniem. Dlatego nie należy tworzyć własnych puli obiektów, chyba że mamy do czynienia z obiektami o dużym koszcie tworzenia, takimi jak połączenia z bazą danych.


Co więcej, należy pamiętać, że szkody spowodowane brakiem kopii obronnej w przypadku ponownego użycia obiektu są znacznie większe niż szkody spowodowane powtarzalnym tworzeniem niepotrzebnych obiektów. Powtarzalne tworzenie obiektów ma wpływ tylko na formę kodu i wydajność, natomiast niepowodzenie kopii obronnej może prowadzić do błędów i problemów z bezpieczeństwem.


Źródła

Komentarze0

[Dla osób bez informatycznego wykształcenia, jak przetrwać jako programista] 14. Podsumowanie często zadawanych pytań na rozmowach kwalifikacyjnych dla początkujących programistówPodsumowując, przedstawiamy często zadawane pytania techniczne na rozmowach kwalifikacyjnych dla programistów (obszar pamięci, struktury danych, bazy danych itd.). Mamy nadzieję, że pomoże to w przygotowaniach do rozmowy kwalifikacyjnej.
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 3, 2024