103. Jakie są zasady sprawdzania wyjątków w spadku?
Jeśli dobrze rozumiem pytanie, pytają o zasady pracy z wyjątkami podczas dziedziczenia i są one następujące:- Przesłonięta lub zaimplementowana metoda w potomku/implementacji nie może generować sprawdzonych wyjątków, które znajdują się wyżej w hierarchii niż wyjątki w metodzie nadklasy/interfejsie.
public interface Animal {
void voice() throws IOException;
}
Implementując ten interfejs, nie możemy zgłosić bardziej ogólnego zgłoszonego wyjątku (na przykład wyjątkiem , Throwable ), ale możemy zastąpić go wyjątkiem potomnym, takim jak FileNotFoundException :
public class Cat implements Animal {
@Override
public void voice() throws FileNotFoundException {
// некоторая реализация
}
}
- Konstruktor podklasy musi uwzględnić w swoim bloku rzutów wszystkie klasy wyjątków zgłoszone przez konstruktor nadklasy wywoływany podczas tworzenia obiektu.
public class Animal {
public Animal() throws ArithmeticException, NullPointerException, IOException {
}
Następnie spadkobierca klasy musi je również wskazać w konstruktorze:
public class Cat extends Animal {
public Cat() throws ArithmeticException, NullPointerException, IOException {
super();
}
Lub, tak jak w przypadku metod, możesz określić nie te same wyjątki, ale bardziej ogólne. W naszym przypadku wystarczy podać bardziej ogólny wyjątek - Wyjątek , ponieważ jest to wspólny przodek wszystkich trzech rozważanych wyjątków:
public class Cat extends Animal {
public Cat() throws Exception {
super();
}
104. Czy mógłbyś napisać kod informujący, kiedy blok final nie zostanie wykonany?
Najpierw przypomnijmy sobie, co w końcu jest . Wcześniej przyjrzeliśmy się mechanizmowi wychwytywania wyjątków: blok try wyznacza obszar przechwytywania, natomiast blok(i) catch to kod, który zadziała, gdy zostanie zgłoszony określony wyjątek. Wreszcie jest trzeci blok kodu po końcu , który jest wymienny z catch , ale nie wyklucza się wzajemnie. Istotą tego bloku jest to, że zawarty w nim kod zawsze działa, niezależnie od wyniku try lub catch ( niezależnie od tego, czy został zgłoszony wyjątek, czy nie). Przypadki jego awarii są bardzo rzadkie i mają charakter nietypowy. Najprostszym przypadkiem niepowodzenia jest wywołanie w powyższym kodzie metody System.exit(0) , która kończy działanie programu (wygasza go):try {
throw new IOException();
} catch (IOException e) {
System.exit(0);
} finally {
System.out.println("Данное сообщение не будет выведенно в консоль");
}
Są też inne sytuacje, w których ostatecznie nie zadziała:
- Nieprawidłowe zakończenie programu spowodowane krytycznymi problemami z systemem lub pojawieniem się jakiegoś błędu, który spowoduje „zawieszenie” aplikacji (przykładem błędu może być ten sam błąd StackOwerflowError , który pojawia się, gdy pamięć stosu jest pełna).
- Kiedy wątek demona przechodzi przez blok ry...finally i równolegle z tym, program się kończy. W końcu wątek demona jest wątkiem dla działań w tle, czyli nie jest priorytetowy i obowiązkowy, a aplikacja nie będzie czekać na zakończenie swojej pracy.
- Najbardziej powszechna nieskończona pętla w try lub catch , w której przepływ pozostanie tam na zawsze:
try { while (true) { } } finally { System.out.println("Данное сообщение не будет выведенно в консоль"); }
105. Napisz przykład obsługi wielu wyjątków w jednym bloku catch
1) Być może pytanie zostało zadane niepoprawnie. O ile rozumiem, to pytanie oznacza wiele połowów dla jednego bloku próbnego :try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
System.out.print("Упс, у вас упало исключение - " + e);
}
Jeśli w bloku try wystąpi wyjątek , wówczas bloki catch naprzemiennie próbują go przechwycić od góry do dołu. Jeżeli określony blok catch powiedzie się, uzyskuje prawo do obsługi wyjątku, podczas gdy pozostałe bloki poniżej nie będą już w stanie spróbować go uchwycić i przetworzyć na swój własny sposób. Dlatego węższe wyjątki są umieszczane wyżej w łańcuchu bloków catch , a szersze wyjątki są umieszczane niżej. Przykładowo, jeśli w naszym pierwszym bloku catch zostanie przechwycony wyjątek klasy Wyjątek , to sprawdzone wyjątki nie będą mogły przedostać się do pozostałych bloków (pozostałe bloki z potomkami Wyjątków będą całkowicie bezużyteczne). 2) Pytanie zostało zadane poprawnie.W takim przypadku nasze przetwarzanie będzie wyglądało następująco:
try {
throw new NullPointerException();
} catch (Exception e) {
if (e instanceof FileNotFoundException) {
// некоторая обработка с сужением типа (FileNotFoundException)e
} else if (e instanceof ArithmeticException) {
// некоторая обработка с сужением типа (ArithmeticException)e
} else if(e instanceof NullPointerException) {
// некоторая обработка с сужением типа (NullPointerException)e
}
Po złapaniu wyjątku metodą catch staramy się poznać jego konkretny typ poprzez metodę instancjiof , która służy do sprawdzenia, czy obiekt należy do określonego typu, aby później móc zawęzić go do tego typu bez negatywnych konsekwencji. Obydwa rozważane podejścia można zastosować w tej samej sytuacji, przy czym stwierdziłem, że pytanie jest błędne, ponieważ drugiej opcji nie nazwałbym dobrą i nigdy się z nią nie spotkałem w swojej praktyce, a jednocześnie powszechna jest metoda pierwsza z multicatchami uwaga, rozprzestrzenianie się.
106. Który operator pozwala wymusić zgłoszenie wyjątku? Napisz przykład
Użyłem go już kilka razy powyżej, ale mimo to powtórzę to słowo kluczowe - rzut . Przykładowe użycie (wymuszenie wyjątku):throw new NullPointerException();
107. Czy metoda główna może zgłosić wyjątek zgłaszający? Jeśli tak, to gdzie zostanie przeniesiony?
Przede wszystkim chcę zauważyć, że main to nic innego jak zwykła metoda i tak, jest wywoływana przez maszynę wirtualną, aby rozpocząć wykonywanie programu, ale poza tym można ją wywołać z dowolnego innego kodu. Oznacza to, że podlega również zwykłym zasadom określania sprawdzanych wyjątków po rzutach :public static void main(String[] args) throws IOException {
W związku z tym mogą również wystąpić w nim wyjątki. Jeśli main nie został wywołany w jakiejś metodzie, ale został uruchomiony jako punkt uruchomienia programu, to zgłoszony przez niego wyjątek zostanie obsłużony przez przechwytywacz .UncaughtExceptionHandler . Ta procedura obsługi jest jedna na wątek (to znaczy jedna procedura obsługi w każdym wątku). Jeśli zajdzie taka potrzeba, możesz utworzyć własny moduł obsługi i ustawić go za pomocą metody setDefaultUncaughtExceptionHandler wywoływanej w obiekcie Thread .
Wielowątkowość
108. Jakie znasz narzędzia do pracy z wielowątkowością?
Podstawowe/podstawowe narzędzia do korzystania z wielowątkowości w Javie:- Synchronized to mechanizm zamykania (blokowania) metody/bloku, gdy wątek do niej wchodzi z innych wątków.
- Volatile to mechanizm zapewniający spójny dostęp do zmiennej przez różne wątki, czyli przy obecności tego modyfikatora na zmiennej wszystkie operacje przypisania i odczytu muszą być niepodzielne. Innymi słowy, wątki nie skopiują tej zmiennej do swojej pamięci lokalnej i nie zmienią jej, ale zmienią jej pierwotną wartość.
- Runnable to interfejs, który można zaimplementować (w szczególności jego metodę uruchamiania) w określonej klasie:
public class CustomRunnable implements Runnable {
@Override
public void run() {
// некоторая логика
}
}
Po utworzeniu obiektu tej klasy możesz rozpocząć nowy wątek, ustawiając ten obiekt w konstruktorze nowego obiektu Thread i wywołując jego metodę start() :
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
Metoda start uruchamia zaimplementowaną metodę run() w osobnym wątku.
- Thread jest klasą, z której dziedziczy (przy nadpisywaniu metody run ):
public class CustomThread extends Thread {
@Override
public void run() {
// некоторая логика
}
}
Tworząc obiekt tej klasy i uruchamiając go za pomocą metody start() , uruchomimy w ten sposób nowy wątek:
new CustomThread().start();
- Concurrency to pakiet z narzędziami do pracy w środowisku wielowątkowym.
- Kolekcje współbieżne - zestaw kolekcji wyspecjalizowanych do pracy w środowisku wielowątkowym.
- Kolejki - wyspecjalizowane kolejki dla środowiska wielowątkowego (blokujące i nieblokujące).
- Synchronizatory to wyspecjalizowane narzędzia do pracy w środowisku wielowątkowym.
- Executory to mechanizmy służące do tworzenia pul wątków.
- Blokady - mechanizmy synchronizacji wątków (bardziej elastyczne niż standardowe - synchronizacja, czekanie, powiadamianie, powiadamianie wszystkich).
- Atomics to klasy zoptymalizowane pod kątem wykonywania wielowątkowego; każda operacja jest niepodzielna.
109. Porozmawiajmy o synchronizacji pomiędzy wątkami. Do czego służą metody wait(), notify() - notifyAll() Join()?
O ile rozumiem pytanie, synchronizacja między wątkami dotyczy kluczowego modyfikatora - synchronized . Modyfikator ten można umieścić bezpośrednio obok bloku:synchronized (Main.class) {
// некоторая логика
}
Lub bezpośrednio w sygnaturze metody:
public synchronized void move() {
// некоторая логика}
Jak powiedziałem wcześniej, synchronized to mechanizm, który pozwala zamknąć blok/metodę z innych wątków, gdy jeden wątek już do niego wszedł. Pomyśl o bloku/metodzie jak o pokoju. Jakiś strumień, gdy do niego dotrze, wejdzie do niego i zamknie go, inne strumienie, przyjdą do pokoju i zobaczą, że jest zamknięte, będą czekać w pobliżu niego, aż będzie wolne. Po wykonaniu swoich obowiązków pierwszy wątek opuszcza pokój i zwalnia klucz. I nie bez powodu ciągle mówiłem o kluczu, ponieważ on naprawdę istnieje. Jest to specjalny obiekt, który ma stan zajęty/wolny. Obiekt ten jest dołączony do każdego obiektu Java, więc korzystając z bloku zsynchronizowanego, musimy wskazać w nawiasie obiekt, dla którego muteksu chcemy zamknąć drzwi:
Cat cat = new Cat();
synchronized (cat) {
// некоторая логика
}
Możesz także użyć muteksu klasy, tak jak to zrobiłem w pierwszym przykładzie ( Main.class ). Kiedy używamy synchronizacji w metodzie, nie określamy obiektu, na którym chcemy zamknąć, prawda? W tym przypadku, w przypadku metody niestatycznej, zostanie ona zamknięta na muteksie tego obiektu , czyli bieżącym obiekcie tej klasy. Statyczny zostanie zamknięty na muteksie bieżącej klasy ( this.getClass(); ). Więcej o mutexie możesz przeczytać tutaj . Cóż, przeczytaj o synchronizacji tutaj . Wait() to metoda, która zwalnia muteks i przełącza bieżący wątek w tryb gotowości, tak jakby był podłączony do bieżącego monitora (coś w rodzaju kotwicy). Z tego powodu tę metodę można wywołać tylko z zsynchronizowanego bloku lub metody (w przeciwnym razie, co powinna zwolnić i czego się spodziewać). Należy również pamiętać, że jest to metoda klasy Object . Dokładniej, nie jeden, ale nawet trzy:
-
Wait() — ustawia bieżący wątek w tryb oczekiwania, dopóki inny wątek nie wywoła metody notify() lub notifyAll() dla tego obiektu (o tych metodach porozmawiamy później).
-
Czekaj (długi limit czasu) — przełącza bieżący wątek w tryb oczekiwania do czasu, aż inny wątek wywoła metodę notify() lub notifyAll() na tym obiekcie lub upłynie określony limit czasu .
-
Wait (long timeout, int nanos) - podobnie jak poprzednio, tylko nanos pozwala na określenie nanosekund (bardziej precyzyjne ustawienie czasu).
-
Notify() to metoda pozwalająca obudzić jeden losowy wątek bieżącego bloku synchronizacji. Ponownie można go wywołać tylko w zsynchronizowanym bloku lub metodzie (w końcu w innych miejscach nie będzie miał kogo odmrozić).
-
NotifyAll() to metoda, która budzi wszystkie oczekujące wątki na bieżącym monitorze (używana również tylko w zsynchronizowanym bloku lub metodzie).
110. Jak zatrzymać przepływ?
Pierwszą rzeczą, którą należy powiedzieć, jest to, że gdy metoda run() zostanie w pełni wykonana , wątek zostanie automatycznie zniszczony. Ale czasami trzeba go zabić przed terminem, zanim ta metoda się zakończy. Co zatem powinniśmy zrobić? Być może obiekt Thread powinien mieć metodę stop() ? Nieważne jak to jest! Ta metoda jest uważana za przestarzałą i może prowadzić do awarii systemu. No i co wtedy? Można to zrobić na dwa sposoby: Pierwszym jest użycie wewnętrznej flagi logicznej. Spójrzmy na przykład. Mamy własną implementację wątku, który powinien wyświetlać określoną frazę na ekranie, aż do całkowitego zatrzymania:public class CustomThread extends Thread {
private boolean isActive;
public CustomThread() {
this.isActive = true;
}
@Override
public void run() {
{
while (isActive) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
public void stopRunningThread() {
isActive = false;
}
}
W przypadku użycia metody stopRunning() flaga wewnętrzna staje się fałszywa i metoda uruchamiania przestaje działać. Uruchommy to w trybie głównym :
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
W efekcie w konsoli zobaczymy coś takiego:
public class CustomThread extends Thread {
@Override
public void run() {
{
while (!Thread.interrupted()) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
}
Uruchom w głównym :
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
Wynik wykonania będzie taki sam jak w pierwszym przypadku, jednak bardziej podoba mi się to podejście: piszemy mniej kodu i korzystamy z większej ilości gotowych, standardowych funkcjonalności. Na tym dzisiaj się zatrzymamy.
GO TO FULL VERSION