JavaRush /Blog Java /Random-PL /Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla ...

Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java. Część 16

Opublikowano w grupie Random-PL
Witaj przyjacielu! Jak długo trzeba czekać, aby zostać programistą? Pytałem wielu różnych ludzi i słyszałem wiele różnych odpowiedzi. Niektórym może wystarczyć nawet miesiąc, innym nawet rok nie wystarczy. Ale wiem na pewno, że zostanie programistą Java to ciernista i długa droga, niezależnie od twoich początkowych umiejętności. W końcu nie tyle umiejętności są ważne, co upór i ciężka praca. Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 1Dlatego dzisiaj nadal celowo analizujemy najpopularniejsze pytania podczas rozmów kwalifikacyjnych dla programisty Java. Ich studiowanie stopniowo przybliży Cię do upragnionego celu. Zacznijmy!

17. Podaj przykłady udanego i nieudanego użycia Opcjonalnego

Załóżmy, że mamy pewną serię wartości, przez które przechodzimy przez strumień, i na koniec otrzymujemy w efekcie Opcjonalne :
Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
   .filter(str -> str.length() >= 3)
   .findAny();
Zgodnie z oczekiwaniami musimy uzyskać wartość z tego Option . Samo użycie get() jest złym sposobem:
String result = stringOptional.get();
Ale ta metoda ma pobrać wartość z Opcjonalnego i zwrócić ją nam? To oczywiście prawda, ale jeśli ma to jakieś znaczenie. No cóż, jeśli wartości w strumieniu były różne i na koniec otrzymaliśmy pusty Opcjonalny , to przy próbie pobrania z niego wartości metodą get() zostanie wyrzucony komunikat: Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 2Co nie jest dobre. W takim przypadku lepiej zastosować następujące konstrukcje:
  1. String result = null;
    if (stringOptional.isPresent()) {
     stringOptional.get();
    }

    W tym przypadku sprawdzamy, czy element znajduje się w Opcjonalnym . Jeśli nie, wynikowy ciąg znaków ma swoją starą wartość.

  2. String result = stringOptional.orElse("default value");

    W tym przypadku podajemy jakąś wartość domyślną, która zostanie nadana wynikowemu stringowi w przypadku pustej Opcjonalnie .

  3. String result = stringOptional.orElseThrow(() -> new CustomException());

    W tym przypadku sami zgłaszamy wyjątek, gdy Option jest pusta .

Może to być wygodne w aplikacji, gdy na przykład używana jest metoda Spring JPA - findById() , która zwraca wartości opcjonalne . W tym przypadku za pomocą tej metody staramy się pobrać wartość, a jeśli jej nie ma, zgłaszamy jakiś wyjątek Runtime , który jest przetwarzany na poziomie kontrolera za pomocą modułu wyjątków i konwertowany na odpowiedź HTTP ze statusem 404 - NOT FOUND . Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 3

18. Czy można zadeklarować metodę główną jako ostateczną?

Tak, oczywiście, nic nie stoi na przeszkodzie, abyśmy zadeklarowali metodę main() jako ostateczną . Kompilator nie będzie generował błędów. Warto jednak pamiętać, że każda metoda po zadeklarowaniu jej jako ostateczna stanie się metodą ostatnią - niezastąpioną. Chociaż kto na nowo zdefiniuje main ??? Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 4

19. Czy można dwukrotnie zaimportować tę samą paczkę/klasę? Jakie mogą być konsekwencje?

Tak, możesz. Konsekwencje? Będziemy mieli kilka niepotrzebnych importów, które Intelijj IDEA wyświetli na szaro, tj. nie używany. Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 5Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 6

20. Co to jest casting? Kiedy możemy uzyskać wyjątek ClassCastException?

Rzutowanie lub rzutowanie typu to proces konwertowania jednego typu danych na inny typ danych: ręcznie (rzutowanie niejawne) lub automatycznie (rzutowanie typu jawnego). Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 7Konwersja automatyczna jest wykonywana przez kompilator, a konwersja ręczna przez programistę. Rzutowanie typów dla prymitywów i klas jest nieco inne, więc rozważymy je osobno. Typy pierwotne Przykład automatycznego rzutowania typów pierwotnych:
int value = 17;
double convertedValue = value;
Jak widać, nie są tu potrzebne żadne dodatkowe manipulacje poza znakiem = . Przykład ręcznego rzutowania typów pierwotnych:
double value = 17.89;
int convertedValue = (int)value;
W tym przypadku możemy zaobserwować ręczne rzutowanie, które jest realizowane za pomocą (int) , przy czym część znajdująca się po przecinku zostanie odrzucona, a konwertowanaValue będzie miała wartość -17. Więcej o rzutowaniu typów pierwotnych przeczytasz w tym artykule . Cóż, teraz przejdźmy do obiektów. Typy referencyjne W przypadku typów referencyjnych możliwe jest automatyczne rzutowanie klas potomnych na klasy nadrzędne. Nazywa się to również polimorfizmem . Załóżmy, że mamy klasę Lion , która dziedziczy po klasie Cat . W takim przypadku automatyczna konwersja będzie wyglądać następująco:
Cat cat = new Lion();
Ale w przypadku jawnego rzutowania wszystko jest nieco bardziej skomplikowane, ponieważ nie ma funkcji odcinania nadmiaru, jak w przypadku prymitywów. I po prostu robię jawną konwersję formularza:
Lion lion= (Lion)new Cat();
Otrzymasz błąd: Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 8W rzeczywistości możesz dodać do klasy potomnej Lion metody , które pierwotnie nie znajdowały się w klasie Cat , a następnie spróbować je wywołać, ponieważ typem obiektu stanie się Lion . No cóż, nie ma w tym żadnej logiki. Dlatego zawężanie typów jest możliwe tylko wtedy, gdy oryginalny obiekt był typu Lion , ale później został rzutowany na klasę nadrzędną:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
Ponadto, dla większej niezawodności, zaleca się zawężanie rzutowania obiektów przy użyciu konstrukcji instancyjnej :
if (cat instanceof Lion) {
 newLion = (Lion)new Cat();
}
Przeczytaj więcej na temat rzutowań typu referencyjnego w tym artykule .

21. Dlaczego współczesne frameworki używają głównie tylko niesprawdzonych wyjątków?

Myślę, że to wszystko dlatego, że obsługa sprawdzonych wyjątków to nadal kod spaghetti, który powtarza się wszędzie, ale nie jest tak naprawdę potrzebny we wszystkich przypadkach. Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 9W takich przypadkach łatwiej jest wykonać przetwarzanie wewnątrz frameworku, aby nie zrzucać tego ponownie na barki programistów. Tak, oczywiście, może wystąpić sytuacja awaryjna, ale te same niesprawdzone wyjątki można obsłużyć w wygodniejszy sposób, bez zawracania sobie głowy przetwarzaniem w trybie try-catch i bez przekazywania ich dalej przez metody. Wystarczy przekonwertować wyjątek na jakąś odpowiedź HTTP w wyjątkuHandler .

22. Co to jest import statyczny?

Korzystając z danych statycznych (metod, zmiennych) nie można stworzyć samego obiektu, lecz zrobić to po nazwie klasy, choć i w tym przypadku potrzebne jest odwołanie do klasy. Wszystko jest z nim proste: dodaje się go za pomocą zwykłego importu. Ale co, jeśli zaczniemy używać metody statycznej bez wpisywania nazwy klasy, tak jakby była to metoda statyczna bieżącej klasy? Jest to możliwe dzięki importowi statycznemu! W takim przypadku musimy napisać import statyczny i link do tej metody. Tak na przykład statyczna metoda klasy Math służąca do obliczania wartości cosinusa:
import static java.lang.Math.cos;
Dzięki temu możemy skorzystać z metody bez podawania nazwy klasy:
double result = cos(60);
Możemy także po prostu załadować wszystkie statyczne metody klasy na raz, używając importu statycznego:
import static java.lang.Math.*;
Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 10

23. Jaki jest związek pomiędzy metodami hashCode() i Equals()?

Według Oracle zasada jest następująca: jeśli dwa obiekty są równe (tj. metoda równości() zwraca wartość true ), muszą mieć ten sam kod skrótu. Jednocześnie nie zapominaj, że dwa różne obiekty mogą mieć ten sam kod skrótu. Aby zrozumieć, dlaczego równania() i hashCode() są zawsze nadpisywane parami, rozważ następujące przypadki:
  1. Obie metody są zastępowane.

    W tym przypadku dwa różne obiekty o tych samych stanach wewnętrznych zwrócą równa() - true , podczas gdy oba obiekty hashCode() zwrócą tę samą liczbę.

    Okazuje się, że wszystko jest w porządku, bo zasada jest przestrzegana.

  2. Obie metody nie są zastępowane.

    W tym przypadku dwa różne obiekty o tych samych stanach wewnętrznych zwrócą false , gdy równa się() , ponieważ porównanie odbywa się przez odniesienie za pomocą operatora == .

    Metoda hashCode() również zwróci inne wartości (najprawdopodobniej), ponieważ generuje przekonwertowaną wartość adresu lokalizacji pamięci. Ale dla tego samego obiektu wartość ta będzie taka sama, podobnie jak funkcjaquals() w tym przypadku zwróci wartość true tylko wtedy, gdy odniesienia wskazują na ten sam obiekt.

    Okazuje się, że w tym przypadku wszystko jest w porządku i zasada jest spełniona.

  3. Przesłonięty równa się() , nie zastąpiony hashCode() .

    W tym przypadku dla dwóch różnych obiektów o tych samych stanach wewnętrznych funkcjaquals() zwróci true , a hashCode() zwróci (najprawdopodobniej) różne wartości.

    Jest to naruszenie zasady, dlatego nie zaleca się tego robić.

  4. równa się() nie jest przesłonięta , hashCode() jest przesłonięta .

    W tym przypadku dla dwóch różnych obiektów o tych samych stanach wewnętrznych funkcjaquals() zwróci wartość false , a hashCode() zwróci te same wartości.

    Nastąpiło naruszenie zasady, więc podejście jest nieprawidłowe.

Jak widać, regułę można wykonać tylko wtedy, gdy funkcje równości() i hashCode() zostaną nadpisane lub w ogóle nie zostaną przesłonięte. Przeczytaj Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 11więcej o funkcji równości() i hashCode() w tym artykule .

24. Kiedy używane są klasy BufferedInputStream i BufferedOutputStream?

InputStream służy do odczytywania danych bajt po bajcie z jakiegoś zasobu, a OutputStream służy do zapisywania danych bajt po bajcie. Jednak operacje bajtowe mogą być bardzo niewygodne i wymagają dodatkowego przetwarzania (w celu normalnego odczytu/zapisu tekstów). Właściwie, aby uprościć takie rekordy bajtowe, wprowadzono BufferedOutputStream i BufferedInputStream do odczytu . Klasy te to nic innego jak bufory gromadzące dane, pozwalające na pracę z danymi nie bajt po bajcie, ale całymi pakietami danych (tablicami). Po utworzeniu BufferedInputStream przyjmuje do swojego konstruktora instancję typu WejścieStream , z której odczytywane są dane:
BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in jest obiektem wejściowym , który odczytuje dane z konsoli. Oznacza to, że używając tego obiektu BufferedInputStream , możemy odczytać dane z wejściowego strumienia , zapisując je do przekazanej tablicy. Okazuje się, że jest to rodzaj opakowania klasy WejścieStream . Tablica arr z tego przykładu to tablica, która odbiera dane z BufferedInputStream . To z kolei odczytuje dane ze strumienia wejściowego za pomocą innej tablicy, która domyślnie ma rozmiar 2048 bajtów. Podobnie jest w przypadku BufferedOutputStream : do konstruktora należy przekazać instancję typu OutputStream , do której zapiszemy dane w całych tablicach:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out to obiekt OutputStream , który zapisuje dane do konsoli. Metoda Flush() wysyła dane ze strumienia BufferedOutputStream do strumienia OutputStream , opróżniając w ten sposób strumień BufferedOutputStream . Bez tej metody nic nie zostanie zapisane. I podobnie jak w poprzednim przykładzie: arr to tablica, z której dane są zapisywane do BufferedOutputStream . Stamtąd są zapisywane do OutputStream w innej tablicy, która domyślnie ma rozmiar 512 bajtów. Więcej o tych dwóch klasach przeczytasz w artykule .

25. Jaka jest różnica pomiędzy klasami java.util.Collection i java.util.Collections?

Kolekcja to interfejs będący głową hierarchii kolekcji. Wprowadza klasy, które pozwalają tworzyć, zawierać i modyfikować całe grupy obiektów. Istnieje wiele metod, które służą do tego, np. add() , usuwania() , zawiera() i inne. Główne interfejsy klasy Collection :
  • Zestaw to interfejs opisujący zestaw zawierający nieuporządkowane, unikalne (niepowtarzające się) elementy.

  • Lista to interfejs opisujący strukturę danych przechowującą uporządkowaną sekwencję obiektów. Obiekty te otrzymują własny indeks (numer), za pomocą którego można z nimi wchodzić w interakcję: pobierać, usuwać, zmieniać, nadpisywać.

  • Kolejka to interfejs opisujący strukturę danych, w której przechowywane są elementy w postaci kolejki zgodnej z zasadą FIFO – First In First Out .

Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 12Przeczytaj więcej o Kolekcji . Kolekcje to klasa narzędziowa udostępniająca wiele różnych metod użytkowych. Na przykład:
  • addAll(Collection<? super T> kolekcja, T...element) - dodaje przekazane elementy typu T do kolekcji .

  • copy(List<? super T> dest, List<? Extends T> src) - kopiuje wszystkie elementy z listy src na listę w dest .

  • pustyList() - zwraca pustą listę.

  • max(Collection<? Extends T> kolekcja, Comparator<? super T> comp) - Zwraca maksymalny element danej kolekcji zgodnie z kolejnością określoną przez podany komparator.

  • unmodibleList(List<? Extends T> list) - zwraca niemodyfikowalną reprezentację przekazanej listy.

W Kolekcjach dostępnych jest wiele różnych, wygodnych metod . Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 13Pełną listę tych metod można znaleźć na stronie internetowej Oracle . Nie bez powodu mówię, że są wygodne. W końcu wszystkie są statyczne. Oznacza to, że nie trzeba za każdym razem tworzyć obiektu tej klasy, aby wywołać na nim niezbędną metodę. Wystarczy wpisać nazwę klasy, wywołać na niej żądaną metodę i przekazać wszystkie wymagane argumenty. Podsumowując, Kolekcja jest głównym interfejsem frameworku kolekcji. Kolekcje to klasa pomocnicza służąca do wygodniejszego przetwarzania obiektów należących do typu ze struktury kolekcji. Cóż, to wszystko na dzisiaj. Wszystkiego najlepszego!Analiza pytań i odpowiedzi z rozmów kwalifikacyjnych dla programisty Java.  Część 16 - 14
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION