CodeGym /Blog Java /Random-PL /Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Co...
John Squirrels
Poziom 41
San Francisco

Top 50 pytań i odpowiedzi na rozmowie o pracę dla 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 musisz się przygotować i co musisz wiedzieć. To świetny czas, aby przejrzeć lub przestudiować te punkty po raz pierwszy. Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 1 Skończyło się na dość obszernym zbiorze często zadawanych pytań dotyczących OOP, składni Javy, wyjątków Javy, kolekcji i wielowątkowości, które dla wygody podzielę na kilka części. Trudno omówić wszystko na raz, ale mam nadzieję, że ten materiał będzie dobrą podstawą dla osób przygotowujących się do znalezienia pierwszej pracy jako programista. Aby uzyskać najlepsze zrozumienie i zachowanie, radzę przeczesywać również inne źródła. Możesz uzyskać głębsze zrozumienie koncepcji, podchodząc do niej z kilku różnych punktów widzenia. Ważny:Będziemy mówić tylko o Javie przed wersją 8. Wszystkie innowacje, które pojawiły się w wersjach 9, 10, 11, 12 i 13, nie będą tutaj brane pod uwagę. Wszelkie pomysły/komentarze, jak ulepszyć odpowiedzi, są mile widziane . Ciesz się czytaniem. Chodźmy!

Wywiad Java: pytania o OOP

1. Jakie są cechy Javy?

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 zmian. Oczywiście wymaga to zainstalowanej maszyny JVM (wirtualnej maszyny Java).

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

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

2. Co to jest dziedziczenie?

Dziedziczenie oznacza, że ​​jedna klasa może dziedziczyć inną klasę (używając słowa kluczowego extends ). Oznacza to, że możesz ponownie użyć kodu z klasy, którą dziedziczysz. Istniejąca klasa jest znana jako , superclassa nowo utworzona klasa to subclass. Ludzie mówią również, że używają terminów rodzic i child.

public class Animal {
   private int age;
}

public class Dog extends Animal {

}
gdzie Animaljest parenti Dogjest child.

3. Co to jest enkapsulacja?

To pytanie jest często zadawane w rozmowach kwalifikacyjnych na stanowiska programistów Java. Hermetyzacja polega na ukryciu implementacji za pomocą modyfikatorów dostępu, metod pobierających i ustawiających. Ma to na celu uniemożliwienie dostępu z zewnątrz wszędzie tam, gdzie programiści uznają to za konieczne. Prostym przykładem z życia jest samochód. Nie mamy bezpośredniego dostępu do pracy silnika. Wszystko, co musimy zrobić, to włożyć kluczyk do stacyjki i włączyć silnik. Procesy zachodzące pod maską to nie nasza sprawa. Co więcej, ingerencja w pracę silnika może doprowadzić do nieprzewidywalnej sytuacji, w której może dojść do uszkodzenia samochodu i obrażeń ciała. Dokładnie to samo dzieje się w programowaniu. Jest to dobrze opisane na Wikipedii. Jest też artykuł o enkapsulacji na CodeGym .

4. Co to jest polimorfizm?

Polimorfizm to zdolność programu do traktowania obiektów z tym samym interfejsem w ten sam sposób, bez informacji o konkretnym typie obiektu. Jak mówi przysłowie „jeden interfejs — wiele wdrożeń”. Dzięki polimorfizmowi możesz łączyć i używać różnych typów obiektów w oparciu o wspólne zachowania. Na przykład mamy klasę Animal, która ma dwóch potomków: Dog i Cat. Ogólna klasa Animal ma zachowanie wspólne dla wszystkich, zdolność do wydawania dźwięku. Używamy możliwości polimorficznych, gdy musimy zebrać wszystko, co dziedziczy klasę Animal i wykonać metodę „make sound”. Oto jak to wygląda:

List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Innymi słowy, polimorfizm jest pomocny. Dotyczy to również metod polimorficznych (przeciążonych). Jak korzystać z polimorfizmu

Wywiad pytania dotyczące składni Java

5. Czym jest konstruktor w Javie?

Konstruktory mają następujące cechy:
  1. Gdy tworzony jest nowy obiekt, program używa odpowiedniego konstruktora do jego utworzenia.
  2. Konstruktor jest jak metoda. Jej charakterystyczną cechą jest brak wartości zwracanej (w tym również void) oraz to, że jej nazwa jest taka sama jak nazwa klasy.
  3. Jeśli żaden konstruktor nie zostanie jawnie utworzony, automatycznie tworzony jest pusty konstruktor.
  4. Konstruktor można przesłonić.
  5. Jeśli deklarujesz konstruktor z parametrami, ale potrzebujesz również konstruktora bez parametrów, musisz go utworzyć osobno, ponieważ nie zostanie on utworzony automatycznie.

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

Nie daj się zwieść podchwytliwym pytaniom — nie ma takich zajęć. Wszystkie klasy dziedziczą klasę Object bezpośrednio lub poprzez przodków!

7. Co to jest zmienna lokalna?

To kolejne popularne pytanie w rozmowach kwalifikacyjnych dla programistów Java. Zmienna lokalna to zmienna, która jest zdefiniowana wewnątrz metody i istnieje tak długo, jak długo metoda jest wykonywana. Po zakończeniu wykonywania zmienna lokalna przestaje istnieć. Oto program, który używa zmiennej lokalnej o nazwie 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 to zmienna zadeklarowana wewnątrz klasy. Istnieje tak długo, jak długo istnieje przedmiot. Na przykład mamy klasę Bee, która ma dwie zmienne instancji — nectarLoad i maxNectarLoad:

public class Bee {

   /**
    * Current nectar load
    */
   private double nectarLoad;

   /**
    * Maximum nectar that can the bee can collect.
    */
   private double maxNectarLoad = 20.0;
 
  ...
}

9. Czym są modyfikatory dostępu?

Modyfikatory dostępu to mechanizm dostosowywania dostępu do klas, metod i zmiennych. Istnieją następujące modyfikatory, wymienione w kolejności zwiększania dostępu:
  1. private— Ten modyfikator dostępu jest używany w metodach, polach i konstruktorach. Dostęp jest ograniczony do klasy, w której zostały zadeklarowane.
  2. package-private (default)— Jest to domyślny poziom dostępu do zajęć. Dostęp jest ograniczony do określonego pakietu, w którym zadeklarowana jest klasa, metoda, zmienna lub konstruktor.
  3. protected— Ten modyfikator dostępu zapewnia taki sam poziom dostępu, jak package-privatew przypadku dodania dostępu dla klas, które dziedziczą klasę z protectedmodyfikatorem.
  4. public— Ten poziom dostępu jest również używany do zajęć. Ten poziom dostępu oznacza pełny dostęp w całej aplikacji.
Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 2

10. Czym jest nadpisywanie metody?

Nadpisujemy metody, gdy klasa potomna chce zmienić zachowanie swojej klasy nadrzędnej. Jeśli musimy również zrobić to, co jest w metodzie nadrzędnej, możemy użyć super.methodName() w metodzie potomnej, która wykona metodę nadrzędną. Następnie możemy dodać naszą dodatkową logikę. Wymagania, których należy przestrzegać:
  • podpis metody musi być taki sam
  • zwracana wartość musi być taka sama

11. Czym są sygnatury metod?

Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 3Sygnatura metody jest kombinacją nazwy metody i argumentów, które metoda przyjmuje. Sygnatura metody jest unikatowym identyfikatorem metody podczas przeciążania metod.

12. Czym jest przeciążanie metody?

Przeciążanie metod to cecha polimorfizmu, w której zmieniamy sygnaturę metody, aby utworzyć wiele metod wykonujących to samo działanie:
  • to samo imię
  • różne argumenty
  • mogą istnieć różne typy zwrotów
Na przykład metoda ArrayListklasy add()może być przeciążona, co pozwala nam dodawać na różne sposoby w zależności od argumentów wejściowych:
  • add(Object o)— Ta metoda po prostu dodaje obiekt
  • add(int index, Object o)— Ta metoda dodaje obiekt o określonym indeksie
  • add(Collection<Object> c)— Ta metoda dodaje listę obiektów
  • add(int index, Collection<Object> c)— Ta metoda dodaje listę obiektów, zaczynając od określonego indeksu.

13. Co to jest interfejs?

Java nie obsługuje wielokrotnego dziedziczenia. Aby przezwyciężyć to ograniczenie, dodano interfejsy w postaci, którą znamy i kochamy ;) Przez długi czas interfejsy miały tylko metody bez żadnej implementacji. W kontekście tej odpowiedzi porozmawiajmy o nich. Na przykład:


public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Wynikają z tego pewne szczegóły:
  • Wszystkie metody w interfejsie są publiczne i abstrakcyjne
  • Wszystkie zmienne są publicznymi statycznymi wersjami końcowymi
  • Klasy nie dziedziczą interfejsów (tj. nie używamy słowa kluczowego extends). Zamiast tego klasy implementują je (tj. używamy słowa kluczowego implements). Co więcej, możesz zaimplementować dowolną liczbę interfejsów.
  • Klasy, które implementują interfejs, muszą zapewniać implementację wszystkich metod znajdujących się w interfejsie.
Lubię to:

public class Cat implements Animal {
   public void makeSound() {
       // Method implementation
   }

   public void eat() {
       // Implementation
   }

   public void sleep() {
       // Implementation
   }
}

14. Co to jest domyślna metoda w interfejsie?

Porozmawiajmy teraz o metodach domyślnych. Po co one są? Kim oni są dla? Metody te zostały dodane, aby służyć „obie ręce”. o czym ja mówię? Cóż, z jednej strony pojawiła się potrzeba dodania nowej funkcjonalności: lambd i Stream API. Z drugiej strony konieczne było zachowanie tego, z czego słynie Java — wstecznej kompatybilności. Aby to zrobić, interfejsy potrzebowały nowych, gotowych rozwiązań. W ten sposób przyszły do ​​nas metody domyślne. Metoda domyślna to metoda zaimplementowana w interfejsie, oznaczona słowem defaultkluczowym. Na przykład dobrze znana stream()metoda w Collectioninterfejsie. Uwierz mi, ten interfejs nie jest tak prosty, jak się wydaje. Lub też równie słynna forEach()metoda wIterableinterfejs. Nie istniało również, dopóki nie dodano domyślnych metod. Nawiasem mówiąc, możesz również przeczytać o tym na CodeGym tutaj .

15. Jak zatem dziedziczymy dwie identyczne metody domyślne?

Poprzednia odpowiedź na temat tego, czym jest domyślna metoda, nasuwa kolejne pytanie. Jeśli możesz zaimplementować metody w interfejsach, to teoretycznie możesz zaimplementować dwa interfejsy tą samą metodą. Jak to zrobimy? Oto dwa różne interfejsy z tą samą metodą:

interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
I mamy klasę, która implementuje te dwa interfejsy. Ale jak wybrać konkretną metodę w interfejsie A lub B? Pozwala na to następująca konstrukcja specjalna A.super.foo():

public class C implements A, B {
   public void fooA() {
       A.super.foo();
   }

   public void fooB() {
       B.super.foo();
   }
}
Zatem fooA()metoda użyje domyślnej foo()metody interfejsu A, podczas gdy fooB()metoda użyje foo()metody interfejsu B.

16. Czym są abstrakcyjne metody i klasy?

W Javie abstractjest słowem zastrzeżonym. Służy do oznaczania abstrakcyjnych klas i metod. Po pierwsze, potrzebujemy definicji. Metoda abstrakcyjna to metoda zadeklarowana przy użyciu abstractsłowa kluczowego bez implementacji w klasie abstrakcyjnej. Oznacza to, że jest to metoda jak w interfejsie, ale z dodatkiem słowa kluczowego, na przykład:

public abstract void foo();
Klasa abstrakcyjna to klasa oznaczona również słowem abstractkluczowym:

public abstract class A {

}
Klasa abstrakcyjna ma kilka cech:
  • nie można utworzyć obiektu klasy abstrakcyjnej
  • może mieć metody abstrakcyjne
  • może również nie mieć metod abstrakcyjnych
Klasy abstrakcyjne są potrzebne do abstrakcji (przepraszam za tautologię), która ma zestaw wspólnych zachowań i stanów (czyli metody i zmienne). Prawdziwe życie jest pełne przykładów. Wszystko wokół nas. „Zwierzę”, „Samochód”, „Figura geometryczna” i tak dalej.

17. Jaka jest różnica między String, StringBuilder i StringBuffer?

Stringwartości są przechowywane w stałej puli ciągów. Natychmiast po utworzeniu ciągu pojawia się on w tej puli. I nie możesz go usunąć. Na przykład:

String name = "book";
Zmienna będzie wskazywać na stałą pulę ciągów znaków. Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 4Ustawiając zmienną name na inną wartość, mamy:

name = "pen";
Stała pula ciągów znaków wygląda następująco: Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 5Innymi słowy, obie wartości tam pozostają. Bufor ciągów:
  • Stringwartości są przechowywane w stosie. Jeśli wartość zostanie zmieniona, nowa wartość zastąpi starą.
  • String Bufferjest zsynchronizowany i dlatego jest bezpieczny dla wątków.
  • Ze względu na bezpieczeństwo nici jego wydajność jest słaba.
Przykład:

StringBuffer name = “book”;
Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 6Gdy tylko zmieni się wartość zmiennej name, zmieni się również wartość na stosie: Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 7StringBuilder jest dokładnie taki sam jak StringBuffer, tylko że nie jest bezpieczny dla wątków. W rezultacie jest zauważalnie szybszy niż StringBuffer.

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

Klasa abstrakcyjna:
  • Klasy abstrakcyjne mają konstruktora domyślnego. Jest wywoływana za każdym razem, gdy tworzony jest potomek klasy abstrakcyjnej.
  • Mogą obejmować zarówno metody abstrakcyjne, jak i nieabstrakcyjne. Ogólnie rzecz biorąc, klasa abstrakcyjna nie musi mieć metod abstrakcyjnych.
  • Klasa, która dziedziczy klasę abstrakcyjną, musi implementować tylko metody abstrakcyjne.
  • Klasa abstrakcyjna może mieć zmienne instancji (patrz pytanie nr 5).
Interfejs:
  • Interfejs nie ma konstruktora i nie można go zainicjować.
  • Można dodawać tylko metody abstrakcyjne (z wyjątkiem metod domyślnych).
  • Klasy, które implementują interfejs, muszą implementować wszystkie metody (z wyjątkiem metod domyślnych).
  • Interfejsy mogą mieć tylko stałe.

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

To pytanie zostało dosłownie zadane w moim ostatnim wywiadzie. Jak dowiedziałem się później, celem tego pytania jest sprawdzenie, jak dana osoba myśli. Oczywiście ta wiedza nie ma praktycznej wartości. Wystarczy wiedzieć, że to wystarczy. Najpierw musimy wyjaśnić, że O(1) jest notacją złożoności czasowej algorytmu „stałego czasu”. Innymi słowy, to oznaczenie wskazuje najszybszy czas wykonania. Aby odpowiedzieć na to pytanie, musimy zastanowić się, co wiemy o tablicach. Aby utworzyć inttablicę, musimy napisać:

int[] intArray = new int[100];
Z tej składni można wyciągnąć kilka wniosków:
  1. Gdy deklarowana jest tablica, znany jest jej typ. Jeśli typ jest znany, znany jest rozmiar każdej komórki w tablicy.
  2. Rozmiar całej tablicy jest znany.
Wynika z tego, że aby zrozumieć, do której komórki należy pisać, wystarczy obliczyć, do którego obszaru pamięci należy pisać. Dla komputera jest to łatwe. Komputer wie, gdzie zaczyna się przydzielona pamięć, ile elementów i rozmiar każdej komórki. Wszystko to oznacza, że ​​miejsce do zapisu będzie równe miejscu startowemu tablicy + rozmiar każdej komórki pomnożonej przez indeks.

Jak więc dochodzimy do O(1) podczas uzyskiwania dostępu do obiektów w tablicy ArrayList?

To pytanie następuje bezpośrednio po poprzednim. Prawda jest taka, że ​​pracując z tablicą zawierającą prymitywy, znamy z góry (w momencie tworzenia) rozmiar typu elementu. Ale co zrobić, jeśli mamy tego rodzaju hierarchię dziedziczenia i Top 50 pytań i odpowiedzi na rozmowie o pracę dla Java Core.  Część 1 - 8chcemy stworzyć kolekcję dla elementów typu A i dodać różne implementacje (B, C i 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 obliczyć rozmiar każdej komórki? W końcu każdy obiekt będzie inny, ewentualnie z innymi polami dodatkowymi. Co robić? Tutaj pytanie jest postawione w sposób, który ma cię zmylić. Wiemy, że kolekcja nie przechowuje bezpośrednio obiektów. Przechowuje tylko odniesienia do obiektów. I wszystkie odniesienia mają ten sam rozmiar i jest to znane. W rezultacie obliczamy tutaj adresy w taki sam sposób, jak w poprzednim pytaniu.

21. Autoboxing i unboxing

Tło historyczne: autoboxing i unboxing to niektóre z głównych innowacji w JDK 5. Autoboxing to proces automatycznej konwersji z typu pierwotnego do odpowiadającej mu klasy opakowania. Unboxing jest dokładnym przeciwieństwem autoboxingu. Jest to proces przekształcania klasy opakowującej w prymityw. Ale jeśli wartość opakowania wynosi null, to NullPointerExceptionpodczas rozpakowywania zostanie rzucone a.

Prymitywy i odpowiadające im opakowania

Prymitywny Klasa opakowania
logiczna logiczne
int Liczba całkowita
bajt Bajt
zwęglać Postać
platforma Platforma
długi Długi
krótki Krótki
podwójnie Podwójnie

// Następuje autoboksowanie:

  • podczas przypisywania prymitywu do odwołania do klasy opakowania:

    PRZED Javą 5:

    
    // Manual boxing (the way it was BEFORE Java 5).
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // And so on for other types
    }
    
    After Java 5:
    // Automatic boxing (the way it became in Java 5).
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // And so on for other types
    }
    
  • gdy prymityw jest przekazywany jako argument do metody, która oczekuje opakowania:

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

// Następuje rozpakowanie:

  • kiedy przypiszemy instancję klasy opakowującej do zmiennej pierwotnej:

    
    // BEFORE Java 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();
    
    // And after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
    
  • Podczas operacji arytmetycznych. Operacje dotyczą tylko typów pierwotnych, więc konieczne jest rozpakowanie do elementów pierwotnych.

    
    // BEFORE Java 5:
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // A comparison used to require this:
    integerBox1.intValue() > integerBox2.intValue()
          
    // In Java 5
    integerBox1 > integerBox2
    
  • podczas przekazywania instancji klasy opakowującej do metody, która przyjmuje odpowiedni prymityw:

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

22. Co to jest ostatnie słowo kluczowe i gdzie jest używane?

Słowa finalkluczowego można używać w odniesieniu do zmiennych, metod i klas.
  1. Wartość zmiennej końcowej nie może zostać zmieniona po jej zainicjowaniu.
  2. Ostatnia klasa jest bezpłodna :) Nie może mieć dzieci.
  3. Ostateczna metoda nie może zostać zastąpiona przez potomka.
Omówiliśmy sprawy na wysokim poziomie. Teraz zanurzmy się głębiej.

Zmienne końcowe

Java daje nam dwa sposoby deklarowania zmiennej i przypisywania jej wartości:
  1. Możesz zadeklarować zmienną i zainicjować ją później.
  2. Możesz zadeklarować zmienną i od razu przypisać jej wartość.
Oto przykład demonstrujący te zastosowania zmiennych końcowych:

public class FinalExample {

   // A static final variable that is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";

   // A final variable that is not initialized, but will only work if you
   // initialize it in the constructor:
   final long creationTime;

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

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

       // The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // The final Config.creationTime field cannot be accessed
//    finalExample.creationTime = 1L;
   }
}

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

Ponieważ nie możemy przypisać nowych wartości zmiennym końcowym, wydaje się, że są to zmienne stałe. Ale tylko na pierwszy rzut oka: jeśli typem danych zmiennej jest immutable, to tak, jest to stała. Ale jeśli typem danych jest mutable, to znaczy zmiennym, wtedy będzie można użyć metod i zmiennych do zmiany wartości obiektu, do którego odwołuje się zmienna final. Z tego powodu nie można go nazwać stałą. Poniższy przykład pokazuje, że niektóre zmienne końcowe są naprawdę stałymi, a inne nie, ponieważ można je zmienić.

public class FinalExample {

   // Immutable final variables
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // Mutable final variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}

Lokalne zmienne końcowe

Kiedy finalzmienna jest tworzona w ramach metody, nazywa się ją zmienną local final:

public class FinalExample {

   public static void main(String[] args) {
       // You can do this
       final int minAgeForDriveCar = 18;

       // Or you can do this, in a for-each loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Możemy użyć słowa kluczowego final w rozszerzonej pętli for, ponieważ po każdej iteracji pętli tworzona jest nowa zmienna. Należy pamiętać, że nie dotyczy to normalnej pętli for, więc otrzymamy błąd kompilacji.

// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

Klasa końcowa

Klasa zadeklarowana jako finalnie może zostać rozszerzona. Mówiąc prościej, żadna inna klasa nie może go odziedziczyć. Doskonałym przykładem klasy finalw JDK jest String. Pierwszym krokiem do stworzenia niezmiennej klasy jest oznaczenie jej jako final, zapobiegając w ten sposób jej rozszerzeniu:

public final class FinalExample {
}

// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}

Ostateczne metody

Kiedy metoda jest oznaczona jako ostateczna, nazywana jest metodą ostateczną (ma sens, prawda?). Ostatecznej metody nie można przesłonić w klasie potomnej. Nawiasem mówiąc, metody wait() i notify() klasy Object są ostateczne, więc nie mamy możliwości ich nadpisania.

public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // Compilation error!
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

Jak i gdzie używać final w Javie

  • Użyj słowa kluczowego final, aby zdefiniować niektóre stałe na poziomie klasy;
  • Utwórz końcowe zmienne dla obiektów, których nie chcesz zmieniać. Na przykład właściwości specyficzne dla obiektu, których możemy użyć do celów rejestrowania.
  • Jeśli nie chcesz, aby zajęcia były przedłużane, zaznacz je jako końcowe.
  • 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 mieć pewność, że implementacja się nie zmieni.

23. Co to są typy zmienne i niezmienne?

Zmienny

Obiekty zmienne to obiekty, których stan i zmienne można zmieniać po utworzeniu. Przykłady modyfikowalnych klas to StringBuilder i StringBuffer. Przykład:

public class MutableExample {

   private String address;

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

   public String getAddress() {
       return address;
   }

   // This setter can change the name field
   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());

       // We are updating the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

Niezmienny

Obiekty niezmienne to obiekty, których stanu i zmiennych nie można zmienić po utworzeniu obiektu. Świetny klucz do HashMap, nie sądzisz? :) Na przykład String, Integer, Double i tak dalej. Przykład:

// We'll make this class final so no one can change it
public final class ImmutableExample {

   private String address;

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

   public String getAddress() {
       return address;
   }

   // We remove the setter

   public static void main(String[] args) {

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

       // There is no way to change this field, so it is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}
W następnej części rozważymy pytania i odpowiedzi dotyczące kolekcji. Mój profil na GitHub Top 50 pytań do rozmowy o pracę i odpowiedzi dla Java Core. Część 2
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION