JavaRush /Blog Java /Random-PL /50 najpopularniejszych pytań i odpowiedzi do rozmów kwali...
Roman Beekeeper
Poziom 35

50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core. Część 1

Opublikowano w grupie Random-PL
Witam wszystkich, panie i panowie Inżynierowie Oprogramowania! Porozmawiajmy o pytaniach na rozmowie kwalifikacyjnej. O tym, na co trzeba się przygotować i co warto wiedzieć. Jest to doskonały powód, aby powtórzyć lub przestudiować te punkty od zera. 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 1Mam dość obszerny zbiór najczęściej zadawanych pytań dotyczących OOP, składni Java, wyjątków w Javie, kolekcji i wielowątkowości, które dla wygody podzielę na kilka części. Ważny:będziemy mówić tylko o wersjach Java do 8. Wszystkie innowacje z 9, 10, 11, 12, 13 nie będą tutaj brane pod uwagę. Wszelkie pomysły/komentarze dotyczące ulepszenia odpowiedzi są mile widziane . Miłej lektury, lecimy!

Wywiad w języku Java: pytania OOP

1. Jakie funkcje posiada Java?

Odpowiedź:
  1. Koncepcje OOP:

    1. orientacja obiektowa;
    2. dziedzictwo;
    3. kapsułkowanie;
    4. wielopostaciowość;
    5. abstrakcja.
  2. Wieloplatformowość: program Java można uruchomić na dowolnej platformie bez żadnych modyfikacji. Jedyne, czego potrzebujesz, to zainstalowana maszyna JVM (wirtualna maszyna Java).

  3. Wysoka wydajność: JIT (kompilator Just In Time) umożliwia wysoką wydajność. JIT konwertuje kod bajtowy na kod maszynowy, a następnie maszyna JVM rozpoczyna wykonywanie.

  4. Wielowątkowość: wątek wykonawczy znany jako Thread. JVM tworzy wątek o nazwie main thread. Programista może utworzyć wiele wątków, dziedzicząc z klasy Thread lub implementując interfejs Runnable.

2. Czym jest dziedziczenie?

Dziedziczenie oznacza, że ​​jedna klasa może dziedziczyć („ rozszerza ”) inną klasę. W ten sposób możesz ponownie wykorzystać kod z klasy, z której dziedziczysz. Istniejąca klasa jest znana jako superclass, a ta tworzona jest znana jako subclass. Mówią też parenti child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
gdzie Animaljest parenti Dog- child.

3. Co to jest enkapsulacja?

To pytanie często pojawia się podczas rozmów kwalifikacyjnych z programistami Java. Enkapsulacja ukrywa implementację za pomocą modyfikatorów dostępu, używając metod pobierających i ustawiających. Odbywa się to w celu zamknięcia dostępu do użytku zewnętrznego w tych miejscach, gdzie deweloperzy uznają to za konieczne. Dostępnym przykładem z życia jest samochód. Nie mamy bezpośredniego dostępu do pracy silnika. Dla nas praca polega na włożeniu kluczyka do stacyjki i włączeniu silnika. A jakie procesy będą zachodzić pod maską, to już nie nasza sprawa. Co więcej, nasza ingerencja w tę czynność może doprowadzić do nieprzewidywalnej sytuacji, w wyniku której możemy rozbić samochód i zrobić sobie krzywdę. Dokładnie to samo dzieje się w programowaniu. Dobrze opisane w Wikipedii . Istnieje również artykuł o enkapsulacji na JavaRush .

4. Co to jest polimorfizm?

Polimorfizm to zdolność programu do identycznego używania obiektów z tym samym interfejsem bez informacji o konkretnym typie tego obiektu. Jak to mówią, jeden interfejs - wiele wdrożeń. Dzięki polimorfizmowi możesz łączyć i używać różnych typów obiektów w oparciu o ich wspólne zachowanie. Na przykład mamy klasę Zwierzę, która ma dwóch potomków - Pies i Kot. Ogólna klasa Animal ma wspólne zachowanie dla wszystkich - wydaje dźwięk. W przypadku gdy musimy zebrać wszystkich potomków klasy Animal i wykonać metodę „wydaj dźwięk”, korzystamy z możliwości polimorfizmu. Oto jak to będzie wyglądać:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Zatem polimorfizm nam pomaga. Co więcej, dotyczy to również metod polimorficznych (przeciążonych). Praktyka stosowania polimorfizmu

Pytania do rozmowy kwalifikacyjnej — składnia języka Java

5. Czym jest konstruktor w Javie?

Obowiązują następujące cechy:
  1. Kiedy tworzony jest nowy obiekt, program używa w tym celu odpowiedniego konstruktora.
  2. Konstruktor jest jak metoda. Jego osobliwością jest to, że nie ma elementu powracającego (w tym void), a jego nazwa jest taka sama jak nazwa klasy.
  3. Jeśli żaden konstruktor nie zostanie zapisany jawnie, automatycznie zostanie utworzony pusty konstruktor.
  4. Konstruktor można zastąpić.
  5. Jeżeli utworzono konstruktor z parametrami, ale potrzebny jest także bez parametrów, należy go napisać osobno, gdyż nie jest on tworzony automatycznie.

6. Które dwie klasy nie dziedziczą po Object?

Nie dajcie się zwieść prowokacjom, takich klas nie ma: wszystkie klasy bezpośrednio lub poprzez przodków są dziedziczone z klasy Object!

7. Co to jest zmienna lokalna?

Kolejne popularne pytanie podczas rozmowy kwalifikacyjnej z programistą Java. Zmienna lokalna to zmienna, która jest zdefiniowana wewnątrz metody i istnieje do momentu wykonania metody. Po zakończeniu wykonywania zmienna lokalna przestanie istnieć. Oto program, który używa zmiennej lokalnej helloMessage w metodzie main():
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. Co to jest zmienna instancji?

Zmienna instancji jest zmienną zdefiniowaną wewnątrz klasy i istnieje do momentu istnienia obiektu. Przykładem jest klasa Bee, która posiada dwie zmienne nectarCapacity i maxNectarCapacity:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. Czym są modyfikatory dostępu?

Modyfikatory dostępu to narzędzie, które pozwala dostosować dostęp do klas, metod i zmiennych. Istnieją następujące modyfikatory, uporządkowane według rosnącego dostępu:
  1. private- używane dla metod, pól i konstruktorów. Poziom dostępu to tylko klasa, w ramach której jest zadeklarowany.
  2. package-private(default)- można wykorzystać na zajęciach. Dostęp tylko w konkretnym pakiecie, w którym zadeklarowana jest klasa, metoda, zmienna, konstruktor.
  3. protected— taki sam dostęp jak package-private+ dla tych klas, które dziedziczą z klasy z modyfikatorem protected.
  4. public- używany również na zajęciach. Pełny dostęp w całej aplikacji.
  5. 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 2

10. Co to są metody nadrzędne?

Przesłonięcie metody ma miejsce, gdy dziecko chce zmienić zachowanie klasy nadrzędnej. Jeśli chcesz, aby zostało wykonane to, co znajduje się w metodzie nadrzędnej, możesz użyć konstrukcji takiej jak super.methodName() w metodzie potomnej, która wykona działanie metody nadrzędnej, a dopiero potem doda logikę. Wymagania do spełnienia:
  • podpis metody musi być taki sam;
  • wartość zwracana powinna być taka sama.

11. Co to jest sygnatura metody?

50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 3Sygnatura metody to zbiór nazwy metody i argumentów akceptowanych przez metodę. Sygnatura metody jest unikalnym identyfikatorem metody podczas jej przeciążania.

12. Co to jest przeciążanie metod?

Przeciążanie metod to właściwość polimorfizmu, w której zmieniając sygnaturę metody, można utworzyć różne metody dla tych samych działań:
  • ta sama nazwa metody;
  • różne argumenty;
  • może istnieć inny typ zwrotu.
Na przykład to samo add()z ArrayListmożna przeciążyć w następujący sposób i wykona dodawanie w inny sposób, w zależności od przychodzących argumentów:
  • add(Object o)- po prostu dodaje obiekt;
  • add(int index, Object o)— dodaje obiekt do określonego indeksu;
  • add(Collection<Object> c)— dodaje listę obiektów;
  • add(int index, Collection<Object> c)— dodaje listę obiektów zaczynając od określonego indeksu.

13. Co to jest interfejs?

W Javie nie jest zaimplementowane dziedziczenie wielokrotne, więc aby przezwyciężyć ten problem, dodano znane nam interfejsy ;) Przez długi czas interfejsy posiadały jedynie metody bez ich implementacji. W ramach tej odpowiedzi porozmawiamy o nich. Na przykład:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Wynikają z tego pewne niuanse:
  • wszystkie metody w interfejsie są publiczne i abstrakcyjne;
  • wszystkie zmienne są publiczne i statyczne;
  • klasy nie dziedziczą ich (rozszerzają), ale implementują (implementują). Co więcej, możesz zaimplementować dowolną liczbę interfejsów.
  • klasy implementujące interfejs muszą zapewniać implementacje wszystkich metod interfejsu.
Lubię to:
public class Cat implements Animal {
   public void makeSound() {
       // implementacja metody
   }

   public void eat() {
       // realizacja
   }

   public void sleep() {
       // realizacja
   }
}

14. Jaka jest domyślna metoda w interfejsie?

Porozmawiajmy teraz o metodach domyślnych. Po co, dla kogo? Metody te zostały dodane, aby wszystko było „zarówno Twoje, jak i nasze”. O czym ja mówię? Tak, z jednej strony trzeba było dodać nową funkcjonalność: lambdy, Stream API, z drugiej strony trzeba było porzucić to, z czego słynie Java – kompatybilność wsteczną. Aby tego dokonać konieczne było wprowadzenie do interfejsów gotowych rozwiązań. W ten sposób przyszły do ​​nas metody domyślne. Oznacza to, że metodą domyślną jest metoda zaimplementowana w interfejsie, która zawiera słowo kluczowe default. Na przykład dobrze znana metoda stream()w Collection. Sprawdźcie sami, ten interfejs nie jest taki prosty jak się wydaje ;). Lub równie dobrze znana metoda forEach()z Iterable. Nie istniała również, dopóki nie dodano metod domyślnych. Swoją drogą, możesz o tym przeczytać także na JavaRush .

15. Jak zatem odziedziczyć dwie identyczne metody domyślne?

Bazując na poprzedniej odpowiedzi na temat metody domyślnej, możesz zadać kolejne pytanie. Jeśli potrafisz zaimplementować metody w interfejsach, to teoretycznie możesz zaimplementować dwa interfejsy tą samą metodą i jak to zrobić? Istnieją dwa różne interfejsy wykorzystujące tę samą metodę:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
Istnieje klasa, która implementuje te dwa interfejsy. Aby uniknąć niepewności i skompilować kod, musimy nadpisać metodę foo()w klasie Ci możemy po prostu wywołać metodę foo()dowolnego z zawartych w niej interfejsów - Alub B. Ale jak wybrać konkretną metodę interfejsu Аlub В? Jest do tego taka struktura A.super.foo():
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
Lub:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
Zatem metoda foo()klasy Cużyje albo metody domyślnej foo()z interfejsu A, albo metody foo()z interfejsu B.

16. Czym są metody i klasy abstrakcyjne?

Java ma zastrzeżone słowo abstractużywane do oznaczania abstrakcyjnych klas i metod. Najpierw kilka definicji. Metoda abstrakcyjna to metoda utworzona bez implementacji ze słowem kluczowym abstractw klasie abstrakcyjnej. Czyli jest to metoda taka jak w interfejsie, tylko z dodatkiem słowa kluczowego, np.:
public abstract void foo();
Klasa abstrakcyjna to klasa, która zawiera również abstractsłowo:
public abstract class A {

}
Klasa abstrakcyjna ma kilka cech:
  • na jego podstawie nie można stworzyć obiektu;
  • może mieć metody abstrakcyjne;
  • może nie mieć metod abstrakcyjnych.
Klasy abstrakcyjne są potrzebne, aby uogólnić jakiś rodzaj abstrakcji (przepraszam za tautologię), która w prawdziwym życiu nie istnieje, ale zawiera wiele typowych zachowań i stanów (czyli metod i zmiennych). Przykładów z życia jest więcej niż wystarczająco. Wszystko jest wokół nas. Może to być „zwierzę”, „samochód”, „figura geometryczna” i tak dalej.

17. Jaka jest różnica pomiędzy Stringiem, Konstruktorem String i Buforem String?

Wartości Stringprzechowywane są w stałej puli ciągów. Po utworzeniu wiersza pojawi się on w tej puli. I nie będzie możliwości jego usunięcia. Na przykład:
String name = "book";
...zmienna będzie odnosić się do puli ciągów Stała pula ciągów 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 4 Jeśli ustawisz nazwę zmiennej na inną wartość, otrzymasz następujące informacje:
name = "pen";
Stała pula ciągów 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 5Zatem te dwie wartości tam pozostaną. Bufor ciągów:
  • wartości Stringsą przechowywane na stosie. Jeżeli wartość ulegnie zmianie, nowa wartość zostanie zastąpiona starą;
  • String Bufferzsynchronizowane, a zatem bezpieczne dla wątków;
  • Ze względu na bezpieczeństwo gwintu, szybkość działania pozostawia wiele do życzenia.
Przykład:
StringBuffer name = "book";
50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 6Gdy tylko zmieni się wartość nazwy, zmieni się wartość na stosie: 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 7StringBuilder Dokładnie tak samo jak StringBuffer, z tą różnicą, że nie jest bezpieczny dla wątków. Dlatego jego prędkość jest wyraźnie większa niż w StringBuffer.

18. Jaka jest różnica pomiędzy klasą abstrakcyjną a interfejsem?

Klasa abstrakcyjna:
  • klasy abstrakcyjne mają konstruktora domyślnego; jest wywoływana za każdym razem, gdy tworzone jest dziecko tej klasy abstrakcyjnej;
  • zawiera zarówno metody abstrakcyjne, jak i nieabstrakcyjne. Ogólnie rzecz biorąc, może nie zawierać metod abstrakcyjnych, ale nadal być klasą abstrakcyjną;
  • klasa dziedzicząca po klasie abstrakcyjnej musi implementować tylko metody abstrakcyjne;
  • klasa abstrakcyjna może zawierać zmienną instancji (patrz pytanie nr 5).
Interfejs:
  • nie ma konstruktora i nie można go zainicjować;
  • należy dodać tylko metody abstrakcyjne (nie licząc metod domyślnych);
  • klasy implementujące interfejs muszą implementować wszystkie metody (nie licząc metod domyślnych);
  • interfejsy mogą zawierać tylko stałe.

19. Dlaczego dostęp do elementu tablicy wymaga O(1)?

To pytanie pochodzi dosłownie z ostatniego wywiadu. Jak dowiedziałem się później, to pytanie zadaje się, aby zobaczyć, jak dana osoba myśli. Oczywiste jest, że ta wiedza nie ma praktycznego znaczenia: wystarczy sama wiedza o tym fakcie. Na początek należy wyjaśnić, że O(1) jest oznaczeniem złożoności czasowej algorytmu, gdy operacja odbywa się w czasie stałym. Oznacza to, że to oznaczenie jest najszybszą realizacją. Aby odpowiedzieć na to pytanie, musimy zrozumieć, co wiemy o tablicach? Aby utworzyć tablicę int, musimy napisać, co następuje:
int[] intArray = new int[100];
Z tego nagrania można wyciągnąć kilka wniosków:
  1. Tworząc tablicę, znany jest jej typ.Jeśli typ jest znany, wtedy jest jasne, jaki rozmiar będzie miała każda komórka tablicy.
  2. Wiadomo, jaki rozmiar będzie miała tablica.
Wynika z tego: aby zrozumieć, do której komórki pisać, wystarczy obliczyć, do którego obszaru pamięci należy pisać. W przypadku samochodu nie może być łatwiej. Maszyna ma początek przydzielonej pamięci, liczbę elementów i pojedynczy rozmiar komórki. Z tego jasno wynika, że ​​przestrzeń zapisu będzie równa początkowej lokalizacji tablicy + rozmiar komórki pomnożony przez jej rozmiar.

Jak uzyskać O(1) w dostępie do obiektów na liście ArrayList?

To pytanie następuje bezpośrednio po poprzednim. Prawdą jest, że gdy pracujemy z tablicą i znajdują się tam prymitywy, to z góry wiemy, jaki jest rozmiar tego typu w momencie jego tworzenia. A co jeśli mamy schemat taki jak na obrazku: 50 najpopularniejszych pytań i odpowiedzi do rozmów kwalifikacyjnych dotyczących języka Java Core.  Część 1 - 8a my chcemy stworzyć kolekcję z elementami typu A i dodać różne implementacje - B, C, D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Jak w tej sytuacji zrozumieć, jaki rozmiar będzie miała każda komórka, ponieważ każdy obiekt będzie inny i może mieć inne dodatkowe pola (lub być zupełnie inny). Co robić? Tutaj pytanie jest postawione w taki sposób, że wprowadza zamieszanie i zamieszanie. Wiemy, że tak naprawdę kolekcja nie przechowuje obiektów, a jedynie linki do tych obiektów. A wszystkie linki mają ten sam rozmiar i jest to znane. Zatem liczenie spacji działa tutaj w taki sam sposób, jak w poprzednim pytaniu.

21. Autoboxing i unboxing

Tło historyczne: autoboxing i autounboxing to jedna z głównych innowacji JDK 5. Autoboxing to proces automatycznej konwersji z typu pierwotnego na odpowiednią klasę opakowania. Automatyczne rozpakowywanie — działa dokładnie odwrotnie do automatycznego rozpakowywania — konwertuje klasę opakowania na prymityw. Jeśli jednak istnieje wartość opakowania null, podczas rozpakowywania zostanie zgłoszony wyjątek NullPointerException.

Pasujący prymityw - opakowanie

Prymitywny Opakowanie klasowe
wartość logiczna Wartość logiczna
wew Liczba całkowita
bajt Bajt
zwęglać Postać
platforma Platforma
długi Długi
krótki Krótki
podwójnie Podwójnie

Automatyczne pakowanie następuje:

  • przy przypisywaniu prymitywu odwołanie do klasy opakowania:

    PRZED Javą 5:

    //ręczne pakowanie, czyli jak to było PRZED Javą 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // i tak dalej do innych typów
    }
    
    после Java 5:
    //automatyczne pakowanie, czyli jak to się stało w Javie 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // i tak dalej do innych typów
    }
  • podczas przekazywania prymitywu jako argumentu do metody oczekującej opakowania:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

Automatyczne rozpakowywanie następuje:

  • kiedy przypiszemy zmienną pierwotną do klasy opakowania:

    //przed Javą 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //i po JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • W przypadkach operacji arytmetycznych. Odnoszą się one tylko do typów pierwotnych; w tym celu należy rozpakować do pierwotnego.

    // Przed Javą 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // dla porównania trzeba było zrobić tak:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • po przekazaniu do opakowania w metodzie, która akceptuje odpowiedni element pierwotny:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. Jakie jest końcowe słowo kluczowe i gdzie go używać?

Słowo kluczowe finalmoże być używane do zmiennych, metod i klas.
  1. Zmiennej końcowej nie można przypisać do innego obiektu.
  2. ostatnia klasa jest sterylna)) nie może mieć spadkobierców.
  3. finalnej metody nie można zastąpić na przodku.
Omówiliśmy już górę, teraz omówmy to bardziej szczegółowo.

zmienne końcowe

;Java daje nam dwa sposoby na utworzenie zmiennej i przypisanie jej wartości:
  1. Możesz zadeklarować zmienną i zainicjować ją później.
  2. Możesz zadeklarować zmienną i natychmiast ją przypisać.
Przykład użycia zmiennej końcowej w tych przypadkach:
public class FinalExample {

   //końcowa zmienna statyczna, która jest natychmiast inicjowana:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final to zmienna, która nie jest inicjowana, ale będzie działać tylko wtedy, gdy
   // zainicjuj to w konstruktorze:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // pole końcowe FinalExample.FINAL_EXAMPLE_NAME nie może zostać przypisane
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // ostatnie pole Config.creationTime nie może zostać przypisane
//    finalExample.creationTime = 1L;
   }
}

Czy zmienną końcową można uznać za stałą?

Ponieważ nie będziemy mogli przypisać nowej wartości zmiennej końcowej, wydaje się, że są to zmienne stałe. Ale to tylko na pierwszy rzut oka. Jeśli typem danych, do którego odnosi się zmienna, jest immutable, to tak, jest to stała. Jeśli jednak typ danych mutablejest zmienny, za pomocą metod i zmiennych będzie można zmienić wartość obiektu, do którego finalodnosi się zmienna, i w tym przypadku nie można jej nazwać stałą. Przykład pokazuje, że niektóre z zmiennych końcowych są w rzeczywistości stałymi, ale inne nie i można je zmieniać.
public class FinalExample {

   // niezmienne zmienne końcowe:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // modyfikowalne zmienne filtrujące
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

Lokalne zmienne końcowe

Kiedy finalw metodzie tworzona jest zmienna, nazywa się ją local finalzmienną:
public class FinalExample {

   public static void main(String[] args) {
       // W ten sposób możesz
       final int minAgeForDriveCar = 18;

       // lub możesz to zrobić w ten sposób, w pętli foreach:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Możemy użyć słowa kluczowego finalw rozszerzonej pętli for, ponieważ po zakończeniu iteracji pętli forza każdym razem tworzona jest nowa zmienna. Nie dotyczy to jednak normalnej pętli for, więc poniższy kod zgłosi błąd w czasie kompilacji.
// nie można przypisać ostatecznego lokalnego zmienionego j
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

Ostatnia klasa

Nie można rozszerzyć klasy zadeklarowanej jako final. Mówiąc najprościej, żadna klasa nie może dziedziczyć po tej klasie. Świetnym przykładem finalklasy w JDK jest String. Pierwszym krokiem do stworzenia niezmiennej klasy jest oznaczenie jej jako final, aby nie można było jej rozszerzać:
public final class FinalExample {
}

// Błąd kompilacji tutaj
class WantsToInheritFinalClass extends FinalExample {
}

Metody końcowe

Kiedy metoda jest oznaczona jako ostateczna, nazywa się ją metodą ostateczną (logiczną, prawda?). Metody Final nie można zastąpić w klasie potomnej. Swoją drogą metody w klasie Object – wait() i notify() – są ostateczne, więc nie mamy możliwości ich przesłonięcia.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // błąd kompilacji tutaj
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

Jak i gdzie używać wersji final w Javie

  • użyj słowa kluczowego final, aby zdefiniować niektóre stałe na poziomie klasy;
  • utwórz zmienne końcowe dla obiektów, jeśli nie chcesz, aby były modyfikowane. Na przykład właściwości specyficzne dla obiektu, których możemy używać do celów rejestrowania;
  • jeśli nie chcesz, aby zajęcia były przedłużane, oznacz je jako ostateczne;
  • jeśli chcesz utworzyć niezmienną < klasę, musisz uczynić ją ostateczną;
  • jeśli chcesz, aby implementacja metody nie zmieniała się w jej potomkach, oznacz metodę jako final. Jest to bardzo ważne, aby zapewnić, że wdrożenie nie ulegnie zmianie.

23. Co jest zmienne i niezmienne?

Zmienny

Mutable to obiekty, których stany i zmienne można zmieniać po utworzeniu. Na przykład klasy takie jak StringBuilder, StringBuffer. Przykład:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // ten seter może zmienić pole nazwy
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // zaktualizuj pole nazwy, aby był to obiekt zmienny
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

Niezmienny

Niezmienne to obiekty, których stanów i zmiennych nie można zmienić po utworzeniu obiektu. Dlaczego nie świetny klucz do HashMap, prawda?) Na przykład String, Integer, Double i tak dalej. Przykład:
// uczynić tę klasę ostateczną, aby nikt nie mógł jej zmienić
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //usuń setera

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Dlatego nie zmieniaj tego pola w żaden sposób, więc jest to niezmienny obiekt
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. Jak napisać klasę niemodyfikowalną?

Kiedy już dowiesz się, czym są obiekty zmienne i niezmienne, naturalne staje się kolejne pytanie – jak to napisać? Aby napisać niezmienną klasę, musisz wykonać proste kroki:
  • uczynić zajęcia ostatecznymi.
  • uczyń wszystkie pola prywatnymi i utwórz dla nich tylko programy pobierające. Setery oczywiście nie są potrzebne.
  • Ustaw wszystkie modyfikowalne pola jako ostateczne, tak aby wartość mogła być ustawiona tylko raz.
  • zainicjuj wszystkie pola poprzez konstruktor, wykonując głęboką kopię (czyli kopiując sam obiekt, jego zmienne, zmienne zmiennych itd.)
  • klonuj zmienne obiekty w modułach pobierających, aby zwracać tylko kopie wartości, a nie odniesienia do rzeczywistych obiektów.
Przykład:
/**
* Przykład tworzenia niezmiennego obiektu.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Sklonuj obiekt przed zwróceniem go.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * W konstruktorze głęboko skopiuj zmienne obiekty.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println(„Wykonywanie głębokiej kopii w konstruktorze”);
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION