W poprzednich dwóch artykułach omówiliśmy kilka ważnych pytań, które najczęściej zadawane są na rozmowach kwalifikacyjnych. Czas przejść dalej i przyjrzeć się pozostałym pytaniom.
Głębokie kopiowanie i płytkie kopiowanie
Dokładną kopią oryginału jest jego klon. W Javie oznacza to możliwość stworzenia obiektu o strukturze podobnej do obiektu oryginalnego. Metodaclone()
zapewnia taką funkcjonalność. Płytkie kopiowanie powoduje utworzenie jak najmniejszej ilości informacji. Domyślnie klonowanie w Javie jest płytkie, tj. Object class
nie wie o strukturze klasy, którą kopiuje. Podczas klonowania maszyna JVM wykonuje następujące czynności:
- Jeśli klasa zawiera tylko elementy typów pierwotnych, zostanie utworzona całkowicie nowa kopia obiektu i zwrócone zostanie odwołanie do tego obiektu.
- Jeśli klasa zawiera nie tylko elementy typów pierwotnych, ale także dowolny inny typ klasy, wówczas kopiowane są odniesienia do obiektów tych klas. Dlatego oba obiekty będą miały te same odniesienia.
- Nie ma potrzeby osobnego kopiowania prymitywnych danych;
- Wszystkie klasy członkowskie w klasie oryginalnej muszą obsługiwać klonowanie. Dla każdego członka klasy musi zostać wywołana,
super.clone()
gdy metoda zostanie zastąpionaclone()
; - Jeśli któryś element klasy nie obsługuje klonowania, to w metodzie clone należy utworzyć nową instancję tej klasy i po jednym na raz skopiować każdego jej członka ze wszystkimi atrybutami do nowego obiektu klasy.
Co to jest synchronizacja? Blokowanie na poziomie obiektu i blokowanie na poziomie klasy?
Synchronizacja odnosi się do wielowątkowości. Zsynchronizowany blok kodu może być wykonywany tylko przez jeden wątek na raz. Java umożliwia jednoczesne przetwarzanie wielu wątków. Może to spowodować, że dwa lub więcej wątków będzie chciało uzyskać dostęp do tego samego pola. Synchronizacja pomaga uniknąć błędów pamięci występujących w przypadku nieprawidłowego użycia zasobów pamięci. Kiedy metoda jest zadeklarowana jako zsynchronizowana, wątek wstrzymuje swój monitor. Jeśli w tym momencie inny wątek spróbuje uzyskać dostęp do metody zsynchronizowanej, wątek zostanie zablokowany i czeka, aż monitor się zwolni. Synchronizacja w Javie odbywa się za pomocą specjalnego słowa kluczowego synchronized . Możesz w ten sposób oznaczać poszczególne bloki lub metody w swojej klasie. Słowo kluczowe synchronized nie może być używane w połączeniu ze zmiennymi lub atrybutami klas. Blokowanie na poziomie obiektu to mechanizm, który pozwala zsynchronizować niestatyczną metodę lub niestatyczny blok kodu, tak aby tylko jeden wątek mógł wykonać blok kodu w danej instancji klasy. Należy to zawsze robić, aby wątek instancji klasy był bezpieczny. Blokowanie na poziomie klasy uniemożliwia wielu wątkom wejście do zsynchronizowanego bloku dla wszystkich dostępnych instancji klasy. Na przykład, jeśli istnieje 100 instancji klasy DemoClass, to tylko 1 wątek będzie mógł w danym momencie wykonać demoMethod() przy użyciu jednej ze zmiennych. Należy to zawsze robić, aby zapewnić bezpieczeństwo statyczne wątku. Dowiedz się więcej o synchronizacji tutaj.Jaka jest różnica między funkcjami Sleep() i Wait()?
Sleep()
to metoda stosowana do opóźnienia procesu o kilka sekund. W przypadku wait()
wątek znajduje się w stanie oczekiwania do momentu wywołania metody notify()
or notifyAll()
. Główna różnica polega na tym, że wait()
zwalnia blokadę monitora, podczas gdy sleep()
nie zwalnia blokady. Wait()
używany w aplikacjach wielowątkowych, sleep()
używany po prostu do wstrzymywania wykonywania wątku. Thread.sleep()
ustawia bieżący wątek w stan „Nie można uruchomić” na określony czas. Wątek zapisuje stan monitora, jaki był przed wywołaniem tej metody. Jeśli inny wątek wywoła t.interrupt()
, wątek, który „zasnął” się obudzi. Należy pamiętać, że sleep()
jest to metoda statyczna, co oznacza, że zawsze wpływa na bieżący wątek (ten, który wykonuje metodę sleep()
). Częstym błędem jest wywoływanie t.sleep()
gdzie t
jest inny wątek; nawet jeśli bieżący wątek wywołujący metodę sleep()
nie jest t
wątkiem. Object.wait()
wysyła bieżący wątek do stanu „Nie można uruchomić” na chwilę, podobnie jak sleep()
, ale z pewnymi niuansami. Wait()
wywołany obiekt, a nie wątek; nazywamy ten obiekt „obiektem blokującym”. Przed wywołaniem lock.wait()
bieżący wątek musi zostać zsynchronizowany z „obiektem blokady”; wait()
następnie zwalnia tę blokadę i dodaje wątek do „listy oczekujących” powiązanej z tą blokadą. Później inny wątek może zsynchronizować się z tym samym obiektem blokady i wywołać metodę lock.notify()
. Ta metoda „obudzi” oryginalny wątek, który wciąż czeka. W zasadzie wait()
/ notify()
można porównać do sleep()
/ interrupt()
, tylko aktywny wątek nie potrzebuje bezpośredniego wskaźnika do uśpionego wątku, wystarczy znać obiekt blokady współdzielonej. Przeczytaj szczegółową różnicę tutaj.
Czy można przypisać wartość null do zmiennej referencyjnej?
Nie, nie możesz. W Javie lewa strona operatora przypisania musi być zmienną. „This” to specjalne słowo kluczowe, które zawsze podaje bieżącą instancję klasy. To nie jest byle jaka zmienna. Podobnie wartości null nie można przypisać do zmiennej za pomocą słowa kluczowego „super” ani żadnego innego podobnego słowa kluczowego.Jaka jest różnica między && i &?
&
- bitowo i &&
- logicznie.
&
ocenia obie strony operacji;&&
ocenia lewą stronę operacji. Jeśli to prawda, nadal ocenia prawą stronę.
Jak zastąpić metody równości() i hachCode()?
hashCode()
i equals()
metody są zdefiniowane w klasie Object
, która jest klasą nadrzędną dla obiektów Java. Z tego powodu wszystkie obiekty Java dziedziczą domyślną implementację metod. Metoda hashCode()
służy do uzyskania unikalnej liczby całkowitej dla danego obiektu. Ta liczba całkowita służy do określenia lokalizacji obiektu, kiedy obiekt ten musi być przechowywany, na przykład HashTable
. Domyślnie hashCode()
zwraca integer
reprezentację adresu lokalizacji pamięci, w której przechowywany jest obiekt. Metoda equls()
, jak sama nazwa wskazuje, służy po prostu do sprawdzenia, czy dwa obiekty są równe. Domyślna implementacja sprawdza odniesienia do obiektów, aby sprawdzić, czy są równe. Poniżej znajdują się ważne wskazówki dotyczące ponownego ładowania tych metod:
- Zawsze używaj tych samych atrybutów obiektu podczas generowania
hashCode()
iequals()
; - Symetria. Te. jeśli dla niektórych obiektów
x
zwróci wartość truey
x.equals(y)
, toy.equals(x)
powinno zwrócić wartość true; - Refleksyjność. Ponieważ każdy obiekt
x
x.equals(x)
musi zwracać wartość true; - Konsystencja. Dla dowolnych obiektów
x
iy
x.equals(y)
zwraca to samo, jeśli informacje użyte w porównaniach nie ulegną zmianie; - Przechodniość. Dla dowolnych obiektów i
x
, jeśli zwróci wartość true i zwróci wartość true, wówczas powinno zwrócić wartość true;y
z
x.equals(y)
y.equals(z)
x.equals(z)
- Ilekroć metoda zostanie wywołana na tym samym obiekcie podczas wykonywania aplikacji, powinna zwrócić ten sam numer, chyba że użyte informacje ulegną zmianie.
hashCode
może zwracać różne wartości dla identycznych obiektów w różnych instancjach aplikacji; - Jeśli dwa obiekty są równe, zgodnie z
equals
, tohashCode
muszą zwracać te same wartości; - Odwrotny wymóg jest opcjonalny. Dwa nierówne obiekty mogą zwrócić ten sam hashCode. Aby jednak poprawić wydajność, lepiej jest, aby różne obiekty zwracały różne kody.
Opowiedz nam o modyfikatorach dostępu
Klasy, pola, konstruktory i metody Java mogą mieć jeden z czterech różnych modyfikatorów dostępu: prywatny Jeśli metoda lub zmienna jest oznaczona jako prywatna , tylko kod w tej samej klasie może uzyskać dostęp do zmiennej lub wywołać metodę. Kod znajdujący się w podklasach nie może uzyskać dostępu do zmiennej lub metody, ani nie może uzyskać do nich dostępu z żadnej innej klasy. Modyfikator dostępu prywatnego jest najczęściej używany w przypadku konstruktorów, metod i zmiennych. default Domyślny modyfikator dostępu jest deklarowany, jeśli w ogóle nie określono modyfikatora. Modyfikator ten oznacza, że dostęp do pól, konstruktorów i metod danej klasy można uzyskać poprzez kod wewnątrz samej klasy, kod wewnątrz klas w tym samym pakiecie. Podklasy nie mogą uzyskać dostępu do metod i zmiennych składowych nadklasy, jeśli są zadeklarowane jako domyślne , chyba że podklasa znajduje się w tym samym pakiecie co nadklasa. chroniony Modyfikator chroniony działa tak samo jak modyfikator domyślny , z tą różnicą, że podklasy mogą również uzyskiwać dostęp do chronionych metod i zmiennych nadklasy. To stwierdzenie jest prawdziwe nawet jeśli podklasa nie znajduje się w tym samym pakiecie co nadklasa. public Modyfikator dostępu publicznego oznacza, że cały kod może uzyskać dostęp do klasy, jej zmiennych, konstruktorów lub metod, niezależnie od tego, gdzie znajduje się ten kod.Co to jest śmieciarz? Możemy do niego zadzwonić?
Odśmiecanie to funkcja automatycznego zarządzania pamięcią w wielu nowoczesnych językach programowania, takich jak Java i języki w NET.Framework. Języki korzystające z odśmiecania często interpretują odśmiecanie na maszynie wirtualnej, takiej jak JVM. Wyrzucanie elementów bezużytecznych ma dwa cele: należy zwolnić nieużywaną pamięć i nie należy zwalniać pamięci, jeśli program nadal jej używa. Czy można ręcznie uruchomić zbieranie śmieci? Nie,System.gc()
daje Ci tak duży dostęp, jak to tylko możliwe. Najlepszą opcją jest wywołanie metody System.gc()
, która zasygnalizuje modułowi wyrzucania elementów bezużytecznych, że musi zostać uruchomiony. Nie ma możliwości jego natychmiastowego uruchomienia, ponieważ moduł zbierający elementy bezużyteczne jest niedeterministyczny. Ponadto zgodnie z dokumentacją OutOfMemoryError
nie zostanie on przekazany, jeśli maszynie wirtualnej nie udało się zwolnić pamięci po pełnym odśmieceniu. Więcej o śmieciarce dowiesz się tutaj.
Co oznacza natywne słowo kluczowe? Wyjaśnij szczegółowo
Słowo kluczowe native wskazuje, że metoda jest zaimplementowana w języku programowania innym niż plik Java. W przeszłości stosowano metody rodzime . W obecnych wersjach Javy jest to potrzebne rzadziej. Obecnie metody natywne potrzebne są gdy:- Musisz wywołać bibliotekę z Java napisaną w innym języku.
- Potrzebujesz dostępu do zasobów systemowych lub sprzętowych, do których można uzyskać dostęp tylko w innym języku (zwykle C). W rzeczywistości wiele funkcji systemowych wchodzących w interakcję z prawdziwym komputerem (takich jak dyski lub dane sieciowe) można wywołać wyłącznie metodą natywną.
- JNI/JNA może zdestabilizować maszynę JVM, szczególnie jeśli spróbujesz zrobić coś złożonego. Jeśli Twoja natywna metoda zrobi coś złego, istnieje możliwość awarii maszyny JVM. Ponadto złe rzeczy mogą się zdarzyć, jeśli Twoja natywna metoda zostanie wywołana z wielu wątków. I tak dalej.
- Trudniej jest debugować program za pomocą kodu natywnego .
- Kod natywny wymaga osobnej budowy frameworków, co może powodować problemy przy przenoszeniu na inne platformy.
Co to jest serializacja?
W informatyce, w kontekście przechowywania i transmisji danych, serializacja to proces tłumaczenia struktury danych lub stanu obiektu na format, który można zapisać, a następnie odtworzyć w innym środowisku komputerowym. Po otrzymaniu serii bitów są one przeliczane zgodnie z formatem serializacji i można je wykorzystać do stworzenia semantycznie identycznego klonu oryginalnego obiektu. Java zapewnia automatyczną serializację, która wymaga od obiektu implementacji interfejsujava.io.Serializable
. Implementacja interfejsu oznacza klasę jako „możliwą do serializacji”. Interfejs java.io.Serializable nie zawiera metod serializacji, ale klasa możliwa do serializacji może opcjonalnie definiować metody, które będą wywoływane w ramach procesu serializacji/diserializacji. Dokonując zmian w klasach, należy rozważyć, które z nich będą, a które nie będą kompatybilne z serializacją. Pełną instrukcję możesz przeczytać tutaj. Podam najważniejsze punkty: Niekompatybilne zmiany:
- Usuń pole;
- Przesuń klasę w górę lub w dół w hierarchii;
- Zmiana pola niestatycznego na statyczne lub nieprzejściowego na przejściowe;
- Zmiana zadeklarowanego pierwotnego typu danych;
- Zmiana metody
WriteObject
takReadObject
, aby domyślnie nie zapisywała ani nie czytała pól; - Zmiana klasy
Serializable
naExternalizable
lub odwrotnie; - Zmiana klasy wyliczeniowej na klasę inną niż wyliczeniowa i odwrotnie;
- Usuwanie
Serializable
lubExternalizable
; - Dodanie
writeReplace
metodyreadResolve
do klasy.
- Dodawanie pól;
- Dodawanie/usuwanie klas;
- Dodawanie metod
WriteObject/ReadObject
[metodydefaultReadObject
lubdefaultWriteObject
należy wywołać na początku]; - Usuwanie metod
WriteObject/ReadObject
; - Dodatek
java.io.Serializable
; - Zmiana dostępu do pola;
- Zmiana pola statycznego na niestatyczne lub przejściowego na nieprzejściowe .
GO TO FULL VERSION