CodeGym /Blog Java /Random-PL /Przeglądanie pytań i odpowiedzi z rozmowy kwalifikacyjnej...
John Squirrels
Poziom 41
San Francisco

Przeglądanie pytań i odpowiedzi z rozmowy kwalifikacyjnej na stanowisko programisty Java. Część 7

Opublikowano w grupie Random-PL
Hej wszystkim! Programowanie jest pełne pułapek. I prawie nie ma tematu, przy którym nie potknąłeś się i nie uderzyłeś się w palec. Jest to szczególnie prawdziwe w przypadku początkujących. Jedynym sposobem na uratowanie palców u nóg jest nauka. W szczególności musisz zgłębić najbardziej podstawowe tematy. Dzisiaj będziemy kontynuować przegląd najpopularniejszych pytań podczas rozmów kwalifikacyjnych dla programistów Java. Te pytania do rozmowy kwalifikacyjnej doskonale radzą sobie z poruszaniem podstawowych tematów. Pamiętaj, że lista zawiera także kilka niestandardowych pytań, które pozwalają inaczej podejść do typowych problemów. Przeglądanie pytań i odpowiedzi z rozmowy kwalifikacyjnej na stanowisko programisty Java.  Część 7 - 1

62. Co to jest pula ciągów i dlaczego jest potrzebna?

Część pamięci udostępniona programowi Java nazywa się stertą (o czym porozmawiamy później), a część sterty nazywa się pulą ciągów . Służy do przechowywania wartości ciągów. Innymi słowy, gdy tworzysz ciąg znaków, na przykład używając podwójnych cudzysłowów w następujący sposób:
String str = "Hello world";
JVM sprawdza, czy pula ciągów ma już określoną wartość. Jeśli tak, do zmiennej str przypisywane jest odwołanie do tej wartości w puli. Jeśli tak się nie stanie, w puli tworzona jest nowa wartość, a do zmiennej str zostaje przypisana referencja do niej . Rozważmy przykład:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
prawda zostanie wyświetlona na ekranie. Pamiętaj, że == porównuje referencje i te dwie zmienne wskazują na tę samą wartość w puli ciągów. Pomaga to uniknąć tworzenia w pamięci wielu identycznych obiektów typu String . Możemy to zrobić, ponieważ, jak pamiętasz, String jest klasą niezmienną, więc nie ma nic złego w posiadaniu wielu odniesień do tej samej wartości. Nie ma już sytuacji, w której zmiana wartości w jednym miejscu powoduje zmiany w kilku innych odniesieniach. Jeśli jednak utworzymy ciąg znaków za pomocą new :
String str = new String("Hello world");
następnie w pamięci tworzony jest oddzielny obiekt, który przechowuje określoną wartość ciągu (nie ma znaczenia, czy ta wartość znajduje się już w puli ciągów). Aby potwierdzić to twierdzenie, rozważ to:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
Otrzymamy dwie linie wskazujące false , co oznacza, że ​​mamy trzy osobne ciągi znaków. Zasadniczo dlatego powinieneś tworzyć ciągi znaków po prostu używając podwójnych cudzysłowów. To powiedziawszy, możliwe jest dodanie (lub uzyskanie odniesienia) wartości w puli ciągów nawet podczas tworzenia obiektu przy użyciu słowa kluczowego new . W tym celu używamy metody intern() klasy String . Ta metoda gwarantuje, że albo utworzymy wartość w puli ciągów, albo otrzymamy odwołanie do niej, jeśli już tam jest. Oto przykład:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
Ten kod trzykrotnie zwraca wartość true do konsoli, co mówi nam, że wszystkie trzy zmienne odnoszą się do tego samego ciągu w pamięci.

63. Jakie wzorce projektowe GoF są używane w puli strun?

W puli strun wzór projektowy GoF jest wzorcem wagi muszej . Jeśli zauważyłeś tutaj inny wzór projektowy, podziel się nim w komentarzach. Tutaj porozmawiamy o wzorze projektowym wagi muszej. Jest to wzorzec projektu strukturalnego, w którym obiekt przedstawiający się jako unikalna instancja w różnych miejscach programu w rzeczywistości nie jest unikalny. Flyweight oszczędza pamięć, przechowując współdzielony stan obiektów zamiast przechowywać identyczne dane w każdym obiekcie. Aby zrozumieć istotę, rozważmy ten elementarny przykład. Załóżmy, że mamy interfejs pracownika :
public interface Employee {
   void work();
}
Ma kilka implementacji, takich jak klasa Lawyer :
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
Oraz zajęcia z Księgowego :
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
Metody są całkowicie dowolne — w tym przykładzie wystarczy sprawdzić, czy są wykonywane. To samo dotyczy konstruktora. Dane wyjściowe konsoli informują nas o utworzeniu nowych obiektów. Posiadamy również dział kadr, którego zadaniem jest zwrot zamówionego pracownika. Jeśli ten pracownik nie jest jeszcze zatrudniony, zostaje zatrudniony, a dział HR zwraca nowego pracownika:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
Zatem logika jest prosta: jeśli pożądany obiekt istnieje, zwróć go; jeśli nie, utwórz go, umieść w magazynie (coś w rodzaju pamięci podręcznej) i zwróć. Zobaczmy teraz jak to wszystko działa:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
Oto, co zobaczymy w konsoli:
Zatrudniono prawnika. Rozwiązywanie kwestii prawnych... Zatrudniono księgową. Prowadzenie ksiąg rachunkowych... Rozwiązywanie kwestii prawnych... Prowadzenie ksiąg rachunkowych... Rozwiązywanie kwestii prawnych... Prowadzenie ksiąg rachunkowych... Rozwiązywanie kwestii prawnych... Prowadzenie ksiąg rachunkowych... Rozwiązywanie kwestii prawnych... Prowadzenie księgowości dokumentacja…
Jak widać, stworzyliśmy tylko dwa obiekty i wykorzystaliśmy je wielokrotnie. Przykład jest bardzo prosty, ale pokazuje, jak ten wzorzec projektowy może oszczędzać nasze zasoby. Jak zapewne zauważyłeś, logika tego wzorca jest do bólu podobna do logiki puli ubezpieczeniowej. Przeglądanie pytań i odpowiedzi z rozmowy kwalifikacyjnej na stanowisko programisty Java.  Część 7 - 2

64. Jak podzielić ciąg na części? Podaj przykład odpowiedniego kodu

Oczywiście to pytanie dotyczy metody podziału . Klasa String ma dwie odmiany tej metody:
String split(String regex);
I
String split(String regex);
Parametr regex jest ogranicznikiem — pewnym wyrażeniem regularnym używanym do dzielenia ciągu na tablicę ciągów, na przykład:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
W konsoli wyświetli się:
Witaj, świecie, tu Amigo!
Zatem nasz ciąg znaków został podzielony na tablicę ciągów, używając spacji jako separatora (zamiast wyrażenia regularnego „\\s” mogliśmy również użyć zwykłego wyrażenia ciągowego „ „ ). Drugi wariant, przeciążony, posiada dodatkowy parametr ograniczający . limit to maksymalny dozwolony rozmiar wynikowej tablicy. Innymi słowy, gdy ciąg zostanie podzielony na maksymalną dozwoloną liczbę podciągów, dzielenie zostanie zatrzymane, a ostatni element będzie zawierał wszelkie „resztki” z prawdopodobnie niedzielonego ciągu. Przykład:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
Wyjście konsoli:
Witaj, świecie, tu Amigo!
Jak widać, gdyby nie limit = 2 , ostatni element tablicy można by podzielić na trzy podciągi.

65. Dlaczego tablica znaków jest lepsza do przechowywania hasła niż ciąg znaków?

Istnieje kilka powodów, dla których podczas przechowywania hasła wolimy tablicę od ciągu znaków:

1. Pula ciągów i niezmienność ciągów.

Używając tablicy ( char[] ), możemy jawnie usunąć dane po zakończeniu pracy z nimi. Możemy też dowolnie nadpisywać tablicę, eliminując hasło z systemu jeszcze przed wyrzuceniem śmieci (wystarczy zmienić kilka komórek na nieprawidłowe wartości). Natomiast String jest klasą niezmienną. Oznacza to, że jeśli będziemy chcieli zmienić wartość obiektu String , otrzymamy nowy, ale stary pozostanie w puli ciągów. Jeśli chcemy usunąć ciąg zawierający hasło, stoimy przed skomplikowanym zadaniem, ponieważ potrzebujemy modułu zbierającego elementy bezużyteczne, aby usunąć tę wartość z puli ciągów, ale ten ciąg prawdopodobnie pozostanie tam przez długi czas. Oznacza to, że jeśli chodzi o bezpieczne przechowywanie danych, String jest gorszy od tablicy char .

2. Jeśli wyślemy wartość String do konsoli (lub dziennika), otrzymamy:

String password = "password";
System.out.println("Password - " + password);
Wyjście konsoli:
Hasło - hasło
A jeśli zdarzy ci się wydrukować tablicę na konsoli:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
konsola wyświetli niezrozumiały bełkot:
Hasło - [C@7f31245a
Faktycznie, to nie jest bełkot. Oto jak zrozumieć to, co widzisz: [C to nazwa klasy - tablica znaków , @ to ogranicznik, a następnie 7f31245a to szesnastkowy kod skrótu.

3. Oficjalny przewodnik po architekturze Java Cryptography (JCA) wyraźnie wspomina o przechowywaniu haseł w znaku char[] zamiast ciągu znaków :

„Logiczne wydaje się gromadzenie i przechowywanie hasła w obiekcie typu java.lang.String . Jednak jest tu jedno zastrzeżenie: Obiekty typu String są niezmienne, tzn. nie ma zdefiniowanych metod, które pozwalają na zmianę (nadpisanie) lub wyzeruj zawartość String po użyciu. Ta funkcja sprawia, że ​​obiekty String nie nadają się do przechowywania informacji wrażliwych na bezpieczeństwo, takich jak hasła użytkowników. Zamiast tego powinieneś zawsze zbierać i przechowywać informacje wrażliwe na bezpieczeństwo w tablicy znaków. Przeglądanie pytań i odpowiedzi z rozmowy kwalifikacyjnej na stanowisko programisty Java.  Część 7 - 3

Wyliczenie

66. Podaj krótki opis Enum w Javie

Enum jest skrótem od wyliczenia, które jest zbiorem stałych łańcuchowych połączonych wspólnym typem. Deklarujemy go za pomocą słowa kluczowego enum . Oto przykład z enum : dozwolone role w niektórych kampusach szkolnych:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
Słowa pisane wielkimi literami są stałymi wyliczeniowymi. Deklaruje się je w sposób uproszczony, bez operatora new . Używanie wyliczeń znacznie ułatwia życie, ponieważ pomaga uniknąć błędów i zamieszania w nazwach (ponieważ lista definiuje jedyne prawidłowe wartości). Dla mnie są bardzo wygodne w konstrukcji przełącznika .

67. Czy Enum może implementować interfejsy (użyj słowa kluczowego implements)?

Tak. W końcu wyliczenia powinny reprezentować coś więcej niż tylko zbiory pasywne (takie jak role w kampusie szkolnym). W Javie mogą reprezentować bardziej złożone obiekty, więc może być konieczne dodanie do nich dodatkowej funkcjonalności. Pozwala to również skorzystać z polimorfizmu, podstawiając wartość wyliczeniową w miejscach, gdzie potrzebny jest zaimplementowany typ interfejsu.

68. Czy Enum może rozszerzyć klasę (użyj słowa kluczowego Extends)?

Nie, nie może, ponieważ wyliczenie jest podklasą domyślnej klasy Enum<T> , gdzie T jest typem wyliczenia. To nic innego jak wspólna klasa bazowa dla wszystkich typów wyliczeniowych w języku Java. Konwersja wyliczenia na klasę jest wykonywana przez kompilator Java w czasie kompilacji. Rozszerzenie nie jest wyraźnie wskazane w kodzie, ale zawsze jest sugerowane.

69. Czy możliwe jest utworzenie Enum bez żadnych instancji obiektów?

To pytanie jest nieco mylące i nie jestem pewien, czy je w pełni rozumiem. Mam dwie interpretacje: 1. Czy można mieć wyliczenie bez żadnych wartości? Tak, oczywiście, ale byłaby to pusta klasa – bezcelowa, np
public enum Role {
}
A jeśli zadzwonimy:
var s = Role.values();
System.out.println(s);
W konsoli otrzymujemy:
[Lwaga.Rola;@9f70c54
(pusta tablica wartości Role ) 2. Czy można utworzyć wyliczenie bez operatora new ? Oczywiście, że tak. Jak powiedziałem powyżej, nie używasz operatora new dla wartości wyliczeniowych, ponieważ są to wartości statyczne.

70. Czy możemy zastąpić metodę toString() Enum?

Tak, oczywiście możesz zastąpić metodę toString() w celu zdefiniowania sposobu wyświetlania wyliczenia po wywołaniu metody toString (na przykład podczas konwersji wyliczenia na zwykły ciąg znaków, na przykład w celu wyprowadzenia go do konsoli lub dzienników).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
To wszystko dla mnie dzisiaj. Do następnej części!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION