Неконтролируемые исключения unchecked обрабатывают ошибки

Время на прочтение
9 мин

Количество просмотров 275K

Это вторая часть статьи (первая часть — try-catch-finally), посвященной такому языковому механизму Java как исключения. Она имеет вводный характер и рассчитана на начинающих разработчиков или тех, кто только приступает к изучению языка.

Также я веду курс «Scala for Java Developers» на платформе для онлайн-образования udemy.com (аналог Coursera/EdX).

1. Магия checked/unchecked
2. Пессимистичный механизм
3. throws с непроверяемым (unckecked) исключением
4. Множественные исключения
5. Или catch, или throws
6. Поведение компилятора/JVM
7. Overriding и throws
8. Передача свойства по наследству

1. «Магия» checked/unchecked

Механизм исключительных ситуация в Java связан с двумя элементами «магии», т.е. поведения, которое никак не отражено в исходном коде:
1. «Магию» java.lang.Throwable — в throw, catch и throws могут стоять исключительно Throwable или его наследники (мы уже разбирали в предыдущей лекции). Это «право» находиться в throw, catch и throws никак не отражено в исходном коде.
2. Все исключительные ситуации делятся на «проверяемые» (checked) и «непроверяемые» (unchecked). Это свойство присуще «корневищу» (Throwable, Error, Exception, RuntimeException) и передается по наследству. Никак не видимо в исходном коде класса исключения.

В дальнейших примера просто учтите, что
— Throwable и Exception и все их наследники (за исключением наследников Error-а и RuntimeException-а) — checked
— Error и RuntimeException и все их наследники — unchecked

checked exception = проверяемое исключение, проверяемое компилятором.
Что именно проверяет компилятор мы разберем на этой лекции.

Напомним иерархию исключений

                    Object
                      |
                  Throwable
                  /      
              Error     Exception
                            |
                    RuntimeException

Расставим значение свойства checked/unchecked

                    Object
                      |
               Throwable(CHECKED)
               /            
     Error(UNCHECKED)    Exception(CHECKED)
                            |
                  RuntimeException(UNCHECKED)

2. Пессимистичный механизм

Я называю связь между проверяемыми исключениями и throws — «пессимистичной», польку мы можем «напугать» о большем, чем может произойти на самом деле, но не наоборот

Мы не можем бросать, но не предупредить

public class App {
    public static void main(String[] args) {
        throw new Exception(); // тут ошибка компиляции
    }
}

>> COMPILATION ERROR: unhandled exception: java.lang.Exception

Мы не можем бросать, но предупредить о «меньшем»

import java.io.IOException;

public class App {
    public static void main(String[] args) throws IOException {
        throw new Exception(); // тут ошибка компиляции
    }
}

>> COMPILATION ERROR: unhandled exception: java.lang.Exception

Мы можем предупредить точно о том, что бросаем

public class App {
    public static void main(String[] args) throws Exception { // предупреждаем о Exception
        throw new Exception(); // и кидаем Exception
    }
}

Мы можем предупредить о большем, чем мы бросаем

public class App {
    public static void main(String[] args) throws Throwable { // предупреждаем "целом" Throwable
        throw new Exception(); // а кидаем только Exception
    }
}

Можем даже предупредить о том, чего вообще нет

public class App {
    public static void main(String[] args) throws Exception { // пугаем
        // но ничего не бросаем
    }
}

Даже если предупреждаем о том, чего нет — все обязаны бояться

public class App {
    public static void main(String[] args) {
        f(); // тут ошибка компиляции
    }

    public static void f() throws Exception {
    }    
}

>> COMPILATION ERROR: unhandled exception: java.lang.Exception

Хотя они (испугавшиеся) могут перепугать остальных еще больше

public class App {
    // они пугают целым Throwable
    public static void main(String[] args) throws Throwable { 
        f();
    }
    // хотя мы пугали всего-лишь Exception
    public static void f() throws Exception {
    }    
}

В чем цель «пессимистичности»? Все достаточно просто.
Вы в режиме протипирования «набросали», скажем, класс-утилиту для скачивания из интернета

public class InternetDownloader {
    public static byte[] (String url) throws IOException { 
        return "<html><body>Nothing! It's stub!</body></html>".getBytes();
    }
}

и хотели бы «принудить» пользователей вашего класса УЖЕ ОБРАБАТЫВАТЬ возможное исключение IOException, хотя из реализации-пустышки вы ПОКА НЕ ГЕНЕРИРУЕТЕ такое исключение. Но в будущем — собираетесь.

3. throws с непроверяемым (unckecked) исключением

Вызов метода, который «пугает» unchecked исключением не накладывает на нас никаких обязанностей.

public class App {
    public static void main(String[] args) { 
        f();
    }
    public static void f() throws RuntimeException {
    }    
}

Эта конструкция служит цели «указать» программисту-читателю кода на то, что ваш метод может выбросить некоторое непроверяемое (unchecked) исключение.

Пример (java.lang.NumberFormatException — непроверяемое исключение):

package java.lang;

public final class Integer extends Number implements Comparable<Integer> {
    ...
    /**
     * ...
     *
     * @param s    a {@code String} containing the {@code int}
     *             representation to be parsed
     * @return     the integer value represented by the argument in decimal.
     * @exception  NumberFormatException  if the string does not contain a
     *               parsable integer.
     */
    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }
    ...
}

Integer.parseInt() может бросить unchecked NumberFormatException на неподходящем аргументе (int k = Integer.parseInt(«123abc»)), это отразили
— в сигнатуре метода: throws NumberFormatException
— в документации (javadoc): @ exception
но это ни к чему нас не обязывает.

4. Множественные исключения

Рассмотрим ситуацию с кодом, который может бросать проверяемые исключения разных типов.
Далее учитывайте, что EOFException и FileNotFoundException — потомки IOException.

Мы можем точно указать, что выбрасываем

import java.io.EOFException;
import java.io.FileNotFoundException;

public class App {
    // пугаем ОБОИМИ исключениями
    public static void main(String[] args) throws EOFException, FileNotFoundException {
        if (System.currentTimeMillis() % 2 == 0) {
            throw new EOFException();
        } else {
            throw new FileNotFoundException();
        }
    }
}

или вот так

import java.io.EOFException;
import java.io.FileNotFoundException;

public class App {
    // пугаем ОБОИМИ исключениями
    public static void main(String[] args) throws EOFException, FileNotFoundException {
        f0();
        f1();
    }
    public static void f0() throws EOFException {...}
    public static void f1() throws FileNotFoundException {...}
}

А можем «испугать» сильнее (предком обоих исключений)

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;

public class App {
    // пугаем ПРЕДКОМ исключений
    public static void main(String[] args) throws IOException {
        if (System.currentTimeMillis() % 2 == 0) {
            throw new EOFException();
        } else {
            throw new FileNotFoundException();
        }
    }
}

или вот так

import java.io.EOFException;
import java.io.FileNotFoundException;

public class App {
    // пугаем ПРЕДКОМ исключений
    public static void main(String[] args) throws IOException {
        f0();
        f1();
    }
    public static void f0() throws EOFException {...}
    public static void f1() throws FileNotFoundException {...}
}

Можно и вот так: EOFException и FileNotFoundException «обобщаем до» IOException, а InterruptedException «пропускаем нетронутым» (InterruptedException — НЕ потомок IOException)

import java.io.EOFException;
import java.io.FileNotFoundException;

public class App {
    public static void main(String[] args) throws IOException, InterruptedException {
        f0();
        f1();
        f2();
    }
    public static void f0() throws EOFException {...}
    public static void f1() throws FileNotFoundException {...}
    public static void f2() throws InterruptedException {...}
}

5. Или catch, или throws

Не надо пугать тем, что вы перехватили
так

public class App {
    public static void main(String[] args) {
        try {
            throw new Exception();
        } catch (Exception e) {
            // ...
        }
    }
}

или так (ставим catch по предку и точно перехватываем)

public class App {
    public static void main(String[] args) {
        try {
            throw new Exception();
        } catch (Throwable e) {
            // ...
        }
    }
}

Но если перехватили только потомка, то не выйдет

public class App {
    public static void main(String[] args) {
        try {
            throw new Throwable();
        } catch (Exception e) {
            // ...
        }
    }
}

>> COMPILATION ERROR: unhandled exception: java.lang.Throwable

Не годится, естественно, и перехватывание «брата»

public class App {
    public static void main(String[] args) {
        try {
            throw new Exception();
        } catch (Error e) {
            // ...
        }
    }
}

>> COMPILATION ERROR: unhandled exception: java.lang.Exception

Если вы часть перехватили, то можете этим не пугать

import java.io.EOFException;
import java.io.FileNotFoundException;

public class App {
    // EOFException перехватили catch-ом, им не пугаем
    public static void main(String[] args) throws FileNotFoundException {
        try {
            if (System.currentTimeMillis() % 2 == 0) {
                throw new EOFException();
            } else {
                throw new FileNotFoundException();
            }
        } catch (EOFException e) {
            // ...
        }
    }
}

6. Поведение компилятора/JVM

Необходимо понимать, что
проверка на cheched исключения происходит в момент компиляции (compile-time checking)
перехват исключений (catch) происходит в момент выполнения (runtime checking)

public class App {
    // пугаем Exception
    public static void main(String[] args) throws Exception { 
        Throwable t = new Exception(); // и лететь будет Exception
        throw t; // но тут ошибка компиляции 
    }  
}

>> COMPILATION ERROR: unhandled exception: java.lang.Throwable

Полная аналогия с

public class App {
    public static void main(String[] args) throws Exception { 
        Object ref = "Hello!";  // ref указывает на строку 
        char c = ref.charAt(0); // но тут ошибка компиляции 
    }  
}

>> COMPILATION ERROR: Cannot resolve method 'charAt(int)'

Хотя ССЫЛКА ref УКАЗЫВАЕТ на объект типа java.lang.String (у которого имеется метод charAt(int)), но ТИП ССЫЛКИ — java.lang.Object (ссылка типа java.lang.Object на объект типа java.lang.String). Компилятор ориентируется на «левый тип» (тип ссылки, а не тип ссылаемого) и не пропускает такой код.

Хотя В ДАННОЙ СИТУАЦИИ компилятор мог бы разобрать, что t ссылается на Exception, а ref — на String, но этого уже невозможно сделать при раздельно компиляции. Представьте, что мы МОГЛИ БЫ скомпилировать ОТДЕЛЬНО такой класс, упаковать в jar и распространять

// НЕ КОМПИЛИРУЕТСЯ! ИССЛЕДУЕМ ГИПОТЕТИЧЕСКУЮ СИТУАЦИЮ!
public class App {
    public static void f0(Throwable t) throws Exception { 
        throw t;
    }  
    public static void f1(Object ref){ 
        char c = ref.charAt(0);
    }  
}

А кто-то берет этот класс, добавляет в classpath и вызывает App.f0(new Throwable()) или App.f1(new Integer(42)). В таком случае JVM столкнулась бы с ситуацией, когда от нее требует бросить проверяемое исключение, которое не отследил компилятор (в случае с f0) или вызвать метод, которого нет (в случае с f1)!

Java — язык со статической типизацией (т.е. отслеживание корректности использования типов (наличие используемых полей, наличие вызываемых методов, проверка на checked исключения, …) проводится компилятором), запрещает такое поведение. В некоторых языках (языки с динамической типизацией — отслеживание корректности использования типов проводится средой исполнения (оно разрешено, например в JavaScript).

Компилятор не пропустит этот код, хотя метод main ГАРАНТИРОВАННО НЕ ВЫБРОСИТ ИСКЛЮЧЕНИЯ

public class App {
    // пугаем Exception
    public static void main(String[] args) throws Exception { 
        try {
            Throwable t = new Exception(); // и лететь будет Exception
            throw t; // но тут ошибка компиляции 
        } catch (Exception e) {
            System.out.println("Перехвачено!");
        }
    }  
}

>> COMPILATION ERROR: unhandled exception: java.lang.Throwable
public class App {
    // ТЕПЕРЬ пугаем Throwable
    public static void main(String[] args) throws Throwable { 
        try {
            Throwable t = new Exception(); // а лететь будет Exception
            throw t;
        } catch (Exception e) { // и мы перехватим Exception
            System.out.println("Перехвачено!");
        }
    }  
}

>> Перехвачено!

7. Overriding и throws

При переопределении (overriding) список исключений потомка не обязан совпадать с таковым у предка.
Но он должен быть «не сильнее» списка предка:

import java.io.FileNotFoundException;
import java.io.IOException;

public class Parent {
    // предок пугает IOException и InterruptedException
    public void f() throws IOException, InterruptedException {}
}

class Child extends Parent {
    // а потомок пугает только потомком IOException
    @Override
    public void f() throws FileNotFoundException {}
}

Однако тут мы попытались «расширить тип» бросаемых исключений

import java.io.IOException;

public class Parent {
    public void f() throws IOException, InterruptedException {}
}

class ChildB extends Parent {
    @Override
    public void f() throws Exception {}
}

>> COMPILATION ERROR: overridden method does not throw 'java.lang.Exception'

Почему можно сужать тип, но не расширять?
Рассмотрим следующую ситуацию:

public class Parent {
    // предок пугает Exception
    public void f() throws Exception {}
}
// тут ошибка компиляции в Java, но, ДОПУСТИМ, ее нет
public class Child extends Parent {
    // потомок расширил Exception до Throwable
    public void f() throws Throwable {}
}
public class Demo {
    public static void test(Parent ref) {
        // тут все компилируется, Parent.f() пугает Exception и мы его ловим catch
        try {
            ref.f();
        } catch(Exception e) {}
    }
}
public class App {
    public static void main(String[] args) {
        // тут все компилируется, Demo.test хотел Parent и мы дали ему подтип - Child
        Demo.test(new Child());
    }
}

Внимательно посмотрите на этот пример — если бы потомок мог расширять тип бросаемого исключения предка, то те места которые «ждут» предка, а получают экземпляр «расширенного» потомка могли бы неконтролируемо выбрасывать проверяемые исключения

8. Передача свойства по наследству

Напомним иерархию исключений с расставленными флагами свойства checked/unchecked

                    Object
                      |
               Throwable(CHECKED)
               /            
     Error(UNCHECKED)    Exception(CHECKED)
                            |
                  RuntimeException(UNCHECKED)

Логика расположения свойства НЕ СВЯЗАНА С НАСЛЕДОВАНИЕМ. Эту логику мы рассмотрим позже (в следующих статьях).
Однако свойство checked/unchecked пользовательских классов исключений строится ИСКЛЮЧИТЕЛЬНО НА ОСНОВЕ НАСЛЕДОВАНИЯ.
Правило крайне простое:
1. Если исключение из списка Throwable, Error, Exception, RuntimeException — то твое свойство надо просто запомнить.
2. Если ты не из списка, то твое свойство равно свойству предка. Нарушить наследование тут нельзя.

Если мы породим потомков A, B, C, D, E, F, G, H, I, J, K, L которые следующим образом наследуются от «корневища» (Throwable, Error, Exception, RuntimeException), то значение их свойства checked/unchecked можно увидеть на схеме

                    Object
                      |
               Throwable(CHECKED)
               /    |       
 Error(UNCHECKED)   |   Exception(CHECKED)
    |       |       |      |            |
    A(UNC)  D(UNC)  |      F(C)        RuntimeException(UNCHECKED)
  /                |     /                |       |
B(UNC) C(UNC)       |   G(C)  H(C)          I(UNC)  J(UNC)  
                   E(C)                   /    
                                       K(UNC) L(UNC)

Контакты

Я занимаюсь онлайн обучением Java (курсы программирования) и публикую часть учебных материалов в рамках переработки курса Java Core. Видеозаписи лекций в аудитории Вы можете увидеть на youtube-канале, возможно видео канала лучше систематизировано в этой статье.
Мой метод обучения состоит в том, что я

  1. показываю различные варианты применения
  2. строю усложняющуюся последовательность примеров по каждому варианту
  3. объясняю логику двигавшую авторами (по мере возможности)
  4. даю большое количество тестов (50-100) всесторонне проверяющее понимание и демонстрирующих различные комбинации
  5. даю лабораторные для самостоятельной работы

Данная статье следует пунктам #1 (различные варианты) и #2(последовательность примеров по каждому варианту).

skype: GolovachCourses
email: GolovachCourses@gmail.com

Исключение (exception) — это ненормальная ситуация (термин «исключение» здесь следует понимать как «исключительная ситуация»), возникающая во время выполнения программного кода. Иными словами, исключение — это ошибка, возникающая во время выполнения программы (в runtime).

Исключение — это способ системы Java (в частности, JVM — виртуальной машины Java) сообщить вашей программе, что в коде произошла ошибка. К примеру, это может быть деление на ноль, попытка обратиться к массиву по несуществующему индексу, очень распространенная ошибка нулевого указателя (NullPointerException) — когда вы обращаетесь к ссылочной переменной, у которой значение равно null и так далее.

В любом случае, с формальной точки зрения, Java не может продолжать выполнение программы.

Обработка исключений (exception handling) — название объектно-ориентированной техники, которая пытается разрешить эти ошибки.

Программа в Java может сгенерировать различные исключения, например:

  • программа может пытаться прочитать файл из диска, но файл не существует;

  • программа может попытаться записать файл на диск, но диск заполнен или не отформатирован;

  • программа может попросить пользователя ввести данные, но пользователь ввел данные неверного типа;

  • программа может попытаться осуществить деление на ноль;

  • программа может попытаться обратиться к массиву по несуществующему индексу.

Используя подсистему обработки исключений Java, можно управлять реакцией программы на появление ошибок во время выполнения. Средства обработки исключений в том или ином виде имеются практически во всех современных языках программирования. В Java подобные инструменты отличаются большей гибкостью, понятнее и удобнее в применении по сравнению с большинством других языков программирования.

Преимущество обработки исключений заключается в том, что она предусматривает автоматическую реакцию на многие ошибки, избавляя от необходимости писать вручную соответствующий код.

В Java все исключения представлены отдельными классами. Все классы исключений являются потомками класса Throwable. Так, если в программе возникнет исключительная ситуация, будет сгенерирован объект класса, соответствующего определенному типу исключения. У класса Throwable имеются два непосредственных подкласса: Exception и Error.

Исключения типа Error относятся к ошибкам, возникающим в виртуальной машине Java, а не в прикладной программе. Контролировать такие исключения невозможно, поэтому реакция на них в приложении, как правило, не предусматривается. В связи с этим исключения данного типа не будут рассматриваться в книге.

Ошибки, связанные с работой программы, представлены отдельными подклассами, производными от класса Exception. В частности, к этой категории относятся ошибки деления на нуль, выхода за пределы массива и обращения к файлам. Подобные ошибки следует обрабатывать в самой программе. Важным подклассом, производным от Exception, является класс RuntimeException, который служит для представления различных видов ошибок, часто возникающих во время выполнения программ.

Каждой исключительной ситуации поставлен в соответствие некоторый класс. Если подходящего класса не существует, то он может быть создан разработчиком.


Так как в Java
ВСЁ ЯВЛЯЕТСЯ ОБЪЕКТОМ, то исключение тоже является объектом некоторого класса, который описывает исключительную ситуацию, возникающую в определенной части программного кода.

«Обработка исключений» работает следующим образом:

  • когда возникает исключительная ситуация, JVM генерирует (говорят, что JVM ВЫБРАСЫВАЕТ исключение, для описания этого процесса используется ключевое слово throw) объект исключения и передает его в метод, в котором произошло исключение;

  • вы можете перехватить исключение (используется ключевое слово catch), чтобы его каким-то образом обработать. Для этого, необходимо определить специальный блок кода, который называется обработчиком исключений, этот блок будет выполнен при возникновении исключения, код должен содержать реакцию на исключительную ситуацию;

  • таким образом, если возникнет ошибка, все необходимые действия по ее обработке выполнит обработчик исключений.

Если вы не предусмотрите обработчик исключений, то исключение будет перехвачено стандартным обработчиком Java. Стандартный обработчик прекратит выполнение программы и выведет сообщение об ошибке.

Рассмотрим пример исключения и реакцию стандартного обработчика Java.

public static void main(String[] args) {

System.out.println(5 / 0);

Мы видим, что стандартный обработчик вывел в консоль сообщение об ошибке. Давайте разберемся с содержимым этого сообщения:

«C:Program FilesJavajdk1.8.0_60binjava»

Exception in thread «main» java.lang.ArithmeticException: / by zero

at ua.opu.Main.main(Main.java:6)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Process finished with exit code 1

Exception in thread «main» java.lang.ArithmeticException: / by zero

сообщает нам тип исключения, а именно класс ArithmeticException (про классы исключений мы будем говорить позже), после чего сообщает, какая именно ошибка произошла. В нашем случае это деление на ноль.

at ua.opu.Main.main(Main.java:6)

в каком классе, методе и строке произошло исключение. Используя эту информацию, мы можем найти ту строчку кода, которая привела к исключительной ситуации, и предпринять какие-то действия. Строки

at ua.opu.Main.main(Main.java:6)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

называются «трассировкой стека» (stack tracing). О каком стеке идет речь? Речь идет о стеке вызовов (call stack). Соответственно, эти строки означают последовательность вызванных методов, начиная от метода, в котором произошло исключение, заканчивая самым первым вызванным методом.

Для вызова методов в программе используется инструкция «call». Когда вы вызываете метод в программе, важно сохранить адрес следующей инструкции, чтобы, когда вызванный метод отработал, программа продолжила работу со следующей инструкции. Этот адрес нужно где-то хранить в памяти. Также перед вызовом необходимо сохранить аргументы функции, которые тоже необходимо где-то хранить.

Вся эта информация хранится в специальной структуре – стеке вызовов. Каждая запись в стеке вызовов называется кадром или фреймом (stack frame).

Таким образом, зная, какая строка привела к возникновению исключения, вы можете изменить код либо предусмотреть обработчик событий.

Как уже было сказано выше, исключение это объект некоторого класса. В Java существует разветвленная иерархия классов исключений.

В Java, класс исключения служит для описания типа исключения. Например, класс NullPointerException описывает исключение нулевого указателя, а FileNotFoundException означает исключение, когда файл, с которым пытается работать приложение, не найден. Рассмотрим иерархию классов исключений:

На самом верхнем уровне расположен класс Throwable, который является базовым для всех исключений (как мы помним, JVM «выбрасывает» исключение», поэтому класс Throwable означает – то, что может «выбросить» JVM).

От класса Throwable наследуются классы Error и Exception. Среди подклассов Exception отдельно выделен класс RuntimeException, который играет важную роль в иерархии исключений.

В Java существует некоторая неопределенность насчет того – существует ли два или три вида исключений.

Если делить исключения на два вида, то это:

  1. 1.

    контролируемые исключения (checked exceptions) – подклассы класса Exception, кроме подкласса RuntimeException и его производных;

  2. 2.

    неконтролируемые исключения (unchecked exceptions) – класс Error с подклассами, а также класс RuntimeException и его производные;

В некоторых источниках класс Error и его подклассы выделяют в отдельный вид исключений — ошибки (errors).

Далее мы видим класс Error. Классы этой ветки составляют вид исключений, который можно обозначить как «ошибки» (errors). Ошибки представляют собой серьезные проблемы, которые не следует пытаться обработать в собственной программе, поскольку они связаны с проблемами уровня JVM.

На самом деле, вы конечно можете предпринять некоторые действия при возникновении ошибок, например, вывести сообщение для пользователя в удобном формате, выслать трассировку стека себе на почту, чтобы понять – что вообще произошло.

Но, по факту, вы ничего не можете предпринять в вашей программе, чтобы эту ошибку исправить, и ваша программа, как правило, при возникновении такой ошибки дальше работать не может.

В качестве примеров «ошибок» можно привести: переполнение стека вызова (класс StackOverflowError); нехватка памяти в куче (класс OutOfMemoryError), вследствие чего JVM не может выделить память под новый объект и сборщик мусора не помогает; ошибка виртуальной машины, вследствие которой она не может работать дальше (класс VirtualMachineError) и так далее.

Несмотря на то, что в нашей программе мы никак не можем помочь этой проблеме, и приложение не может работать дальше (ну как может работать приложение, если стек вызовов переполнен или JVM не может дальше выполнять код?!); знание природы этих ошибок поможет вам предпринять некоторые действия, чтобы избежать этих ошибок в дальнейшем. Например, ошибки типа StackOverflowError и OutOfMemoryError могут быть следствием вашего некорректного кода.

Например, попробуем спровоцировать ошибку StackOverflowError

public static void main(String[] args) {

public static void methodA() {

private static void methodB() {

Получим такое сообщение об ошибке

Exception in thread «main» java.lang.StackOverflowError

at com.company.Main.methodB(Main.java:14)

at com.company.Main.methodA(Main.java:10)

at com.company.Main.methodB(Main.java:14)

at com.company.Main.methodA(Main.java:10)

at com.company.Main.methodB(Main.java:14)

at com.company.Main.methodA(Main.java:10)

at com.company.Main.methodB(Main.java:14)

at com.company.Main.methodA(Main.java:10)

Ошибка OutOfMemoryError может быть вызвана тем, что ваш код, вследствие ошибки при программировании, создает очень большое количество массивных объектов, которые очень быстро заполняют кучу и свободного места не остается.

Exception in thread «main» java.lang.OutOfMemoryError: Java heap space

at java.base/java.util.Arrays.copyOf(Arrays.java:3511)

at java.base/java.util.Arrays.copyOf(Arrays.java:3480)

at java.base/java.util.ArrayList.grow(ArrayList.java:237)

at java.base/java.util.ArrayList.grow(ArrayList.java:244)

at java.base/java.util.ArrayList.add(ArrayList.java:454)

at java.base/java.util.ArrayList.add(ArrayList.java:467)

at com.company.Main.main(Main.java:13)

Process finished with exit code 1

Ошибка VirtualMachineError может означать, что следует переустановить библиотеки Java.

В любом случае, следует относиться к типу Error не как к неизбежному злу и «воле богов», а просто как к сигналу к тому, что в вашем приложении что-то не так, или что-то не так с программным или аппаратным обеспечением, которое вы используете.

Класс Exception описывает исключения, связанные непосредственно с работой программы. Такого рода исключения «решаемы» и их грамотная обработка позволит программе работать дальше в нормальном режиме.

В классе Exception описаны исключения двух видов: контролируемые исключения (checked exceptions) и неконтролируемые исключения (unchecked exceptions).

Неконтролируемые исключения содержатся в подклассе RuntimeException и его наследниках. Контролируемые исключения содержатся в остальных подклассах Exception.

В чем разница между контролируемыми и неконтролируемыми исключениями, мы узнаем позже, а теперь рассмотрим вопрос – а как же именно нам обрабатывать исключения?

Обработка исключений в методе может выполняться двумя способами:

  1. 1.

    с помощью связки try-catch;

  2. 2.

    с помощью ключевого слова throws в сигнатуре метода.

Рассмотрим оба метода поподробнее:

Способ 1. Связка try-catch

Этот способ кратко можно описать следующим образом.

Код, который теоретически может вызвать исключение, записывается в блоке try{}. Сразу за блоком try идет блок код catch{}, в котором содержится код, который будет выполнен в случае генерации исключения. В блоке finally{} содержится код, который будет выполнен в любом случае – произошло ли исключение или нет.

Теперь разберемся с этим способом более подробно. Рассмотрим следующий пример – программу, которая складывает два числа, введенные пользователем из консоли

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

System.out.println(«Введите первое число: «);

String firstNumber = scanner.nextLine();

System.out.println(«Введите второе число: «);

String secondNumber = scanner.nextLine();

a = Integer.parseInt(firstNumber);

b = Integer.parseInt(secondNumber);

System.out.println(«Результат: « + (a + b));

Первое, что нам нужно определить – и что является главным при работе с исключениями, КАКАЯ ИНСТРУКЦИЯ МОЖЕТ ПРИВЕСТИ К ВОЗНИКНОВЕНИЮ ИСКЛЮЧЕНИЯ?

То есть, мы должны понять – где потенциально у нас может возникнуть исключение? Понятно, что речь идет не об операции сложения и не об операции чтения данных из консоли. Потенциально опасными строчками кода здесь являются строчки

a = Integer.parseInt(firstNumber);

b = Integer.parseInt(secondNumber);

в которых происходит преобразование ввода пользователя в целое число (метод parseInt() преобразует цифры в строке в число).

Почему здесь может возникнуть исключение? Потому что пользователь может ввести не число, а просто какой-то текст и тогда непонятно – что записывать в переменную a или b. И да, действительно, если пользователь введет некорректное значение, возникнет исключение в методе Integer.parseInt().

Итак, что мы можем сделать. «Опасный код» нужно поместить в блок try{}

Обратите внимание на синтаксис блока try. В самом простом случае это просто ключевое слово try, после которого идут парные фигурные скобки. Внутри этих скобок и заключается «опасный» код, который может вызвать исключение. Сразу после блока try должен идти блок catch().

a = Integer.parseInt(firstNumber);

b = Integer.parseInt(secondNumber);

} catch (NumberFormatException e) {

// сохранить текст ошибки в лог

System.out.println(«Одно или оба значения некорректны!»);

System.out.println(«Результат: « + (a + b));

Обратите внимание на синтаксис блока catch. После ключевого слова, в скобках описывается аргумент с именем e типа NumberFormatException.

Когда произойдет исключение, то система Java прервет выполнение инструкций в блоке try и передаст управление блоку catch и запишет в этот аргумент объект исключения, который сгенерировала Java-машина.

То есть, как только в блоке try возникнет исключение, то дальше инструкции в блоке try выполняться не будут! А сразу же начнут выполняться действия в блоке catch.

Обработчик исключения находится в блоке catch, в котором мы можем отреагировать на возникновение исключения. Также, в этом блоке нам будет доступен объект исключения, от которого мы можем получить дополнительные сведения об исключении.

Блок catch сработает только в том случае, если указанный в скобках тип объекта исключения будет суперклассом или будет того же типа, что и объект исключения, который сгенерировала Java.

Например, если в нашем примере мы напишем код, который потенциально может выбросить исключение типа IOException, но не изменим блок catch

} catch (NumberFormatException e) {

// сохранить текст ошибки в лог

System.out.println(«Одно или оба значения некорректны!»);

тогда обработчик не будет вызван и исключение будет обработано стандартным обработчиком Java.

Способ 2. Использование ключевого слова throws

Второй способ позволяет передать обязанность обработки исключения тому методу, который вызывает данный метод (а тот, в свою очередь может передать эту обязанность выше и т.д.).

Изменим наш пример и выделим в отдельный метод код, который будет запрашивать у пользователя число и возвращать его как результат работы метода

public static void main(String[] args) {

int a = getNumberFromConsole(«Введите первое число»);

int b = getNumberFromConsole(«Введите второе число»);

System.out.println(«Результат: « + (a + b));

public static int getNumberFromConsole(String message) {

Scanner scanner = new Scanner(System.in);

System.out.print(message + «: «);

String s = scanner.nextLine();

return Integer.parseInt(s);

Мы понимаем, что в данном методе может произойти исключение, но мы не хотим или не можем его обработать. Причины могут быть разными, например:

  1. 1.

    обработка исключений может происходить централизованно однотипным способом (например, показ окошка с сообщением и с определенным текстом);

  2. 2.

    это не входит в нашу компетенцию как программиста – обработкой исключений занимается другой программист;

  3. 3.

    мы пишем только некоторую часть программы и непонятно – как будет обрабатывать исключение другой программист, который потом будет использовать наш код (например, мы пишем просто какую-то библиотеку, которая производит вычисления, и как будет выглядеть обработка – это не наше дело).

В любом случае, мы знаем, что в этом коде может быть исключение, но мы не хотим его обрабатывать, а хотим просто предупредить другой метод, который будет вызывать наш код, что выполнение кода может привести к исключению. В этом случае, используется ключевое слово throws, которое указывается в сигнатуре метода

public static int getNumberFromConsole(String message) throws NumberFormatException {

Scanner scanner = new Scanner(System.in);

System.out.print(message + «: «);

String s = scanner.nextLine();

return Integer.parseInt(s);

Обратите внимание на расположение сигнатуру метода. Мы привыкли, что при объявлении метода сразу после скобок входных аргументов мы открываем фигурную скобку и записываем тело метода. Здесь же, после входных аргументов, мы пишем ключевое слово throws и потом указываем тип исключения, которое может быть сгенерировано в нашем методе. Если метод может выбрасывать несколько типов исключений, они записываются через запятую

public static void foo() throws NumberFormatException, ArithmeticException, IOException {

Тогда, в методе main мы должны написать примерно следующее

public static void main(String[] args) {

a = getNumberFromConsole(«Введите первое число»);

b = getNumberFromConsole(«Введите второе число»);

} catch (NumberFormatException e) {

// сохранить текст ошибки в лог

System.out.println(«Одно или оба значения некорректны!»);

System.out.println(«Результат: « + (a + b));

Основное преимущество этого подхода – мы передаем обязанность по обработке исключений другому, вышестоящему методу.

Отличия между контролируемыми и неконтролируемыми исключениями

Если вы вызываете метод, который выбрасывает checked исключение, то вы ОБЯЗАНЫ предусмотреть обработку возможного исключения, то есть связку try-catch.

Яркий пример checked исключения – класс IOException и его подклассы.

Рассмотрим пример – попробуем прочитать файл и построчно вывести его содержимое на экран консоли:

public static void main(String[] args) {

Path p = Paths.get(«c:\temp\file.txt»);

BufferedReader reader = Files.newBufferedReader(p);

while ((line = reader.readLine()) != null) {

System.out.println(line);

Как мы видим, компилятор не хочет компилировать наш код. Чем же он недоволен? У нас в коде происходит вызов двух методов – статического метода Files.newBufferedReader() и обычного метода BufferedReader.readLine().

Если посмотреть на сигнатуры этих методов то можно увидеть, что оба этих метода выбрасывают исключения типа IOException. Этот тип исключения относится к checked-исключению и поэтому, если вы вызываете эти методы, компилятор ТРЕБУЕТ от вас предусмотреть блок catch, либо в самом вашем методе указать throws IOException и, таким образом, передать обязанность обрабатывать исключение другому методу, который будет вызывать ваш.

Таким образом, «оборачиваем» наш код в блок try и пишем блок catch.

public static void main(String[] args) {

Path p = Paths.get(«c:\temp\file.txt»);

BufferedReader reader = Files.newBufferedReader(p);

while ((line = reader.readLine()) != null) {

System.out.println(line);

} catch (IOException e) {

System.out.println(«Ошибка при чтении файла!»);

Еще один способ — указать в сигнатуре метода, что он выбрасывает исключение типа IOException и переложить обязанность обработать ошибку в вызывающем коде

public static void main(String[] args) {

Path p = Paths.get(«c:\temp\file.txt»);

} catch (IOException e) {

System.out.println(«Ошибка при чтении файла!»);

public static void printFile(Path p) throws IOException {

BufferedReader reader = Files.newBufferedReader(p);

while ((line = reader.readLine()) != null) {

System.out.println(line);

Eсли метод выбрасывает checked-исключение, то проверка на наличие catch-блока происходит на этапе компиляции. И вы обязаны предусмотреть обработку исключения для checked-исключения.

Что касается unchecked-исключения, то обязательной обработки исключения нет – вы можете оставить подобные ситуации без обработки.

Зачем необходимо наличие двух видов исключений?

В большинстве языков существует всего лишь один тип исключений – unchecked. Некоторые языки, например, C#, в свое время отказались от checked-исключений.

Во-первых, мы не можем сделать все исключения checked, т.к. очень многие операции могут генерировать исключения, и если каждый такой участок кода «оборачивать» в блок try-catch, то код получится слишком громоздким и нечитабельным.

С другой стороны, зачем нужно делать некоторые типы исключений checked? Почему просто не сделать все исключения unchecked и оставить решения об обработке исключений целиком на совести программиста?

В официальной документации написано, что unchecked-исключения – это те исключения, от которых программа «не может восстановиться», тогда как checked-исключения позволяют откатить некоторую операцию и повторить ее снова.

На самом деле, если вы посмотрите на различные типы unchecked-исключений, то вы увидите, что большинство их связаны с ошибками самого программиста. Выход за пределы массива, исключение нулевого указателя, деление на ноль – большинство из подобного рода исключений целиком лежат на совести программистов. Тогда мы можем сказать, что лучше программист пишет более хороший код, чем везде вставляет проверки на исключения.

Контролируемые исключения, как правило, представляют те ошибки, которые возникают не из-за программиста и предусмотреть которые программист не может. Например, это отсутствующие файлы, работа с сокетами, подключение к базе данных, сетевые соединения, некорректный пользовательский ввод.

Вы можете написать идеальный код, но потом вы отдадите приложение пользователю, а он введет название файла, которого нет или напишет неправильный IP для сокет-соединения. Таким образом, мы заранее должны быть готовыми к неверным действиям пользователя или к программным или аппаратным проблемам на его стороне и в обязательном порядке предусмотреть обработку возможных исключений.

Дополнительно об исключениях

Рассмотрим детально различные возможности механизма исключений, которые позволяют программисту максимально эффективно противодействовать исключениям:

Java позволяет вам для одного блока try предусмотреть несколько блоков catch, каждый из которых должен обрабатывать свой тип исключения

public static void foo() {

} catch (ArithmeticException e) {

// обработка арифметического исключения

} catch (IndexOutOfBoundsException e) {

// обработка выхода за пределы коллекции

} catch (IllegalArgumentException e) {

// обработка некорректного аргумента

Важно помнить, что Java обрабатывает исключения последовательно. Java просматривает блок catch сверху вниз и выполняет первый подходящий блок, который может обработать данное исключение.

Так как вы можете указать как точный класс, так и суперкласс, то если первым блоком будет блок для суперкласса – выполнится он. Например, исключение FileNotFoundException является подклассом IOException. И поэтому если вы первым поставите блок с IOException – он будет вызываться для всех подтипов исключений, в том числе и для FileNotFoundException и блок c FileNotFoundException никогда не выполнится.

public static void main(String[] args) {

Path p = Paths.get(«c:\temp\file.txt»);

} catch (IOException e) {

System.out.println(«Ошибка при чтении файла!»);

} catch (FileNotFoundException e) {

// данный блок никогда не будет вызван

public static void printFile(Path p) throws IOException {

BufferedReader reader = Files.newBufferedReader(p);

while ((line = reader.readLine()) != null) {

System.out.println(line);

Один блок для обработки нескольких типов исключений

Начиная с версии Java 7, вы можете использовать один блок catch для обработки исключений нескольких, не связанных друг с другом типов. Приведем пример

public static void foo() {

} catch (ArithmeticException | IllegalArgumentException | IndexOutOfBoundsException e) {

// три типа исключений обрабатываются одинаково

Как мы видим, один блок catch используется для обработки и типа IOException и NullPointerException и NumberFormaException.

Вы можете использовать вложенные блоки try, которые могут помещаться в других блоках try. После вложенного блока try обязательно идет блок catch

public static void foo() {

} catch (IllegalArgumentException e) {

// обработка вложенного блока try

} catch (ArithmeticException e) {

Выбрасывание исключения с помощью ключевого слова throw

С помощью ключевого слова throw вы можете преднамеренно «выбросить» определенный тип исключения.

public static void foo(int a) {

throw new IllegalArgumentException(«Аргумент не может быть отрицательным!»);

Кроме блока try и catch существует специальный блок finally. Его отличительная особенность – он гарантированно отработает, вне зависимости от того, будет выброшено исключение в блоке try или нет. Как правило, блок finally используется для того, чтобы выполнить некоторые «завершающие» операции, которые могли быть инициированы в блоке try.

public static void foo(int a) {

FileOutputStream fout = null;

File file = new File(«file.txt»);

fout = new FileOutputStream(file);

} catch (IOException e) {

// обработка исключения при записи в файл

} catch (IOException e) {

При любом развитии события в блоке try, код в блоке finally отработает в любом случае.

Блок finally отработает, даже если в try-catch присутствует оператор return.

Как правило, блок finally используется, когда мы в блоке try работаем с ресурсами (файлы, базы данных, сокеты и т.д.), когда по окончании блока try-catch мы освобождаем ресурсы. Например, допустим, в процессе работы программы возникло исключение, требующее ее преждевременного закрытия. Но в программе открыт файл или установлено сетевое соединение, а, следовательно, файл нужно закрыть, а соединение – разорвать. Для этого удобно использовать блок finally.

Блок try-with-resources является модификацией блока try. Данный блок позволяет автоматически закрывать ресурс после окончания работы блока try и является удобной альтернативой блоку finally.

public static void foo() {

Path p = Paths.get(«c:\temp\file.txt»);

try (BufferedReader reader = Files.newBufferedReader(p)) {

while ((line = reader.readLine()) != null)

System.out.println(line);

} catch (IOException e) {

Внутри скобок блока try объявляется один или несколько ресурсов, которые после отработки блока try-catch будут автоматически освобождены. Для этого объект ресурса должен реализовывать интерфейс java.lang.AutoCloseable.

Создание собственных подклассов исключений

Встроенные в Java исключения позволяют обрабатывать большинство распространенных ошибок. Тем не менее, вы можете создавать и обрабатывать собственные типы исключений. Для того, чтобы создать класс собственного исключения, достаточно определить как его произвольный от Exception или от RuntimeException (в зависимости от того, хотите ли вы использовать checked или unchecked – исключения).

Насчет создания рекомендуется придерживаться двух правил:

  1. 1.

    определитесь, исключения какого типа вы хотите использовать для собственных исключений (checked или unchecked) и старайтесь создавать исключения только этого типа;

  2. 2.

    старайтесь максимально использовать стандартные типы исключений и создавать свои типы только в том случае, если существующие типы исключений не отражают суть того исключения, которое вы хотите добавить.

Плохие практики при обработке исключений

Ниже представлены действия по обработке ошибок, которые характерны для плохого программиста. Ни в коем случае не рекомендуется их повторять!

  1. 1.

    Указание в блоке catch объекта исключения типа Exception. Существует очень большой соблазн при создании блока catch указать тип исключения Exception и, таким образом, перехватывать все исключения, которые относятся к этому классу (а это все исключения, кроме системных ошибок). Делать так крайне не рекомендуется, т.к. вместо того чтобы решать проблему с исключениями, мы фактически игнорируем ее и просто реализуем некоторую «заглушку», чтобы приложение продолжило работу дальше. Кроме того, каждый тип исключения должен быть обработан своим определенным образом.

  2. 2.

    Помещение в блок try всего тела метода. Следующий плохой прием используется, когда программист не хочет разбираться с кодом, который вызывает исключение и просто, опять же, реализует «заглушку». Этот прием очень «хорошо» сочетается с первым приемом. В блок try должен помещаться только тот код, который потенциально может вызвать исключение, а не всё подряд, т.к. лень обрабатывать исключения нормально.

  3. 3.

    Игнорирование исключения. Следующий плохой прием состоит в том, что мы просто игнорируем исключение и оставляем блок catch пустым. Программа должна реагировать на исключения и должна информировать пользователя и разработчика о том, что что-то пошло не так. Безусловно, исключение это не повод тут же закрывать приложение, а попытаться повторить то действие, которое привело к исключению (например, повторно указать название файла, попытаться открыть базу данных через время и т.д.). В любом случае, когда приложение в ответ на ошибку никак не реагирует – не выдает сообщение, но и не делает того, чего от нее ожидали – это самый плохой вариант.

Вопросы к собеседованию

Вопросы:

Что такое исключение?

Событие, которое встречается в ходе программы и прерывает стандартный ход её выполнения.

Исключение в Java – объект, экземпляр класса. Могут порождаться не только автоматически
при возникновении исключительной ситуации, но и создаваться самим разработчиком. Все классы
исключений наследуются от Throwable (имеют свойство – возможность быть брошенными через слово throw).

Throwable имплементирует Serializable

Иерархия исключений

Схема исключений

Исключения имеют общего предка — класс Throwable, потомками которого являются классы Exception и Error.

Error — это критическая ошибка во время исполнения программы, связанная с работой
виртуальной машины Java. Error это ошибки виртуальной машины.

В большинстве случаев Error не нужно обрабатывать, поскольку она
свидетельствует о каких-то серьезных недоработках в коде.

StackOverflowError — возникает, например, когда метод бесконечно вызывает сам себя;

OutOfMemoryError — возникает, когда недостаточно памяти для создания новых объектов;

NoClassDefFoundError – не смог найти класс.

Исключения (Exceptions) являются результатом проблем в программе, которые в принципе
решаемые и предсказуемые.

Например, произошло деление на ноль в целых числах. RuntimeExceptions исключения, которые могут быть
предотвращены программно.

Расскажите про обрабатываемые и необрабатываемые исключения?

В Java все исключения делятся на два типа:

  • checked (контролируемые/проверяемые исключения) должны обрабатываться блоком catch
    или описываться в сигнатуре метода (например, throws IOException). Наличие такого
    обработчика/модификатора сигнатуры проверяются на этапе компиляции;
  • unchecked (неконтролируемые/непроверяемые исключения), к которым относятся ошибки Error,
    обрабатывать которые не рекомендуется, и исключения времени выполнения, представленные классом
    RuntimeException и его наследниками;

RuntimeExceptions:

  • ArithmeticException — исключение, возникающее при делении на ноль
  • IndexOutOfBoundException — тип индекса вышел за допустимые пределы
  • IllegalArgumentException — использование неверного аргумента при вызове метода
  • NullPointerException — использование пустой ссылки
  • NumberFormatException — ошибка преобразования строки в число
  • ArrayIndexOutOfBoundsException — выход за пределы массива
  • FileNotFoundException – не нашел файл для открытия
  • AccessDeniedException
  • SocketException
  • BindException
  • ConnectException

Обработка исключений


Можно ли обработать необрабатываемые исключения?

Можно, но не надо.


Какой оператор позволяет принудительно выбросить исключение?

throw new Exception();


О чем говорит ключевое слово throws?

Модификатор throws прописывается в сигнатуре метода и указывает на то, что метод
потенциально может выбросить исключение с указанным типом


Как создать собственное («пользовательское») исключение?

Необходимо унаследоваться от базового класса требуемого типа исключений (например от Exception или
RuntimeException).


Что произойдет если исключение будет выброшено из блока catch после чего другое исключение будет выброшено из
блока finally?

Из finally подавит из catch будет обработано в finally блоке.

Одно в try, второе в finally, то исключение в finally проглотит исключение.

Если до блока finally исключение было обработано, то мы можем получить информацию об исключении в блоке try и
тем самым не потерять исключение, которое впоследствии может быть перезаписано в finally другим исключением.

Механизм Try-catch-finally

try
— данное ключевое слово используется для отметки начала блока кода, который
потенциально может привести к ошибке.

catch
— ключевое слово для отметки начала блока кода,
предназначенного для перехвата и обработки исключений в случае их возникновения.

finally
— ключевое слово для отметки начала блока кода, который является дополнительным. Этот блок
помещается после последнего блока catch.

Управление передаётся в блок finally в любом случае, было выброшено исключение или нет.

Общий вид конструкции для обработки исключительной ситуации выглядит следующим образом:

try {

//код, который потенциально может привести к исключительной ситуации

} catch(SomeException e ) { //в скобках указывается класс конкретной ожидаемой ошибки

//код обработки исключительной ситуации

} finally {

//необязательный блок, код которого выполняется в любом случае

}


Возможно ли использование блока try-finally (без catch)

Да


Может ли один блок catch отлавливать сразу несколько исключений?

Да


Всегда ли выполняется блок finally? Существуют ли ситуации, когда блок finally не будет выполнен?

Не будет выполнен когда:

  1. Когда System.exit(0) вызывается из блока try.
  2. Когда JVM исчерпывает память catch (OutOfMemoryError oome) { // do stuff }
  3. Когда ваш java-процесс принудительно убит из задачи или консоли
  4. Условие взаимоблокировки потоков в блоке try
  5. Когда ваш компьютер отключается из-за сбоя питания

Может ли метод main() выбросить исключение во вне и если да, то где будет происходить обработка данного
исключения?

Может и оно будет передано в виртуальную машину Java (JVM).


В каком порядке следует обрабатывать исключения в catch блоках?


Общее правило: обрабатывать исключения нужно от «младшего» к старшему.

Т.е. нельзя поставить в первый блок catch(Exception ex) {}, иначе все дальнейшие блоки catch() уже ничего не
смогут обработать, т.к. любое исключение будет соответствовать обработчику catch(Exception ex).

Механизм Try-With-Resources

Конструкцию try-with-resources ввели в Java 7. Она дает возможность объявлять
один или несколько ресурсов в блоке try, которые
будут закрыты автоматически без использования finally блока.

В качестве ресурса можно использовать любой объект, класс которого реализует интерфейс
java.lang.AutoCloseable или java.io.Closable.

Если try блок также выбрасывает исключение, оно побеждает, а исключение из close() метода подавляется.

Использование блока finally для закрытия ресурса

public static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {

BufferedReader br = new BufferedReader(new FileReader(path));

try {

return br.readLine();

} finally {

if (br != null) {

br.close();

}

}

}

Использование конструкции try-with-resources для закрытия ресурса

public static String readFirstLineFromFile(String path) throws IOException {

try (BufferedReader br = new BufferedReader(new FileReader(path))) {

return br.readLine();

}

}

Использование конструкции try-with-resources для закрытия нескольких ресурсов

public static String readFirstLineFromFile2(String path) throws IOException {

try (FileReader f = new FileReader(«a.txt»); BufferedReader br = new BufferedReader(f)) {

return br.readLine();

}

}


Что произойдет если исключение будет выброшено из блока catch после чего другое
исключение будет выброшено из метода close() при использовании try-with-resources?

Если исключение будет выброшено в основном коде (в try) и в методе close(), то приоритетнее будет
первое исключение, а второе исключение будет подавлено, но информация о нем сохранится (с
помощью метода Throwable.addSuppressed(Throwable exception), который вызывается неявно Java
компилятором):

Java_Deep_7.4-5020-83cb21.png

JavaSpec_Welcome_970x90-1801-439a19.png

В нашей жизни нередко возникают ситуации, которые мы не планировали. К примеру, пошли вы утром умываться и с досадой обнаружили, что отключили воду. Вышли на улицу, сели в машину, а она не заводится. Позвонили другу, а он недоступен. И так далее и тому подобное… В большинстве случаев человек без труда справится с подобными проблемами. А вот как с непредвиденными ситуациями справляется Java, мы сейчас и поговорим.

Что называют исключением. Исключения в мире программирования

В программировании исключением называют возникновение ошибки (ошибок) и различных непредвиденных ситуаций в процессе выполнения программы. Исключения могут появляться как в итоге неправильных действий юзера, так и из-за потери сетевого соединения с сервером, отсутствии нужного ресурса на диске и т. п. Также среди причин исключений — ошибки программирования либо неверное использование API.

При этом в отличие от «человеческого мира», программное приложение должно чётко понимать, как поступать в подобной ситуации. И вот как раз для этого в Java и существует механизм исключений (exception).

Используемые ключевые слова

При обработке исключений в Java применяются следующие ключевые слова:
try – служит для определения блока кода, в котором может произойти исключение;
catch – необходим для определения блока кода, где происходит обработка исключения;
finally – применяется для определения блока кода, являющегося необязательным, однако при его наличии он выполняется в любом случае вне зависимости от результата выполнения блока try.

Вышеперечисленные ключевые слова необходимы для создания в коде ряда специальных обрабатывающих конструкций: try{}finally{}, try{}catch, try{}catch{}finally.

Кроме того:
1. Для возбуждения исключения используем throw.
2. Для предупреждения в сигнатуре методов о том, что метод может выбросить исключение, применяем throws.

Давайте на примере посмотрим, как используются ключевые слова в Java-программе:

//метод считывает строку с клавиатуры

public String input() throws MyException {//предупреждаем с помощью throws,
// что метод может выбросить исключение MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
//в блок try заключаем код, в котором может произойти исключение, в данном
// случае компилятор нам подсказывает, что метод readLine() класса
// BufferedReader может выбросить исключение ввода/вывода
    try {
        s = reader.readLine();
// в блок  catch заключаем код по обработке исключения IOException
    } catch (IOException e) {
        System.out.println(e.getMessage());
// в блоке finally закрываем поток чтения
    } finally {
// при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому “оборачиваем” код в блок try
        try {
            reader.close();
// пишем обработку исключения при закрытии потока чтения
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw
        throw new MyException("String can not be empty!");
    }
    return s;
}

Зачем нам механизм исключений?

Для понимания опять приведём пример из обычного мира. Представьте, что на какой-нибудь автодороге имеется участок с аварийным мостом, на котором ограничена грузоподъёмность. И если по такому мосту проедет грузовик со слишком большой массой, мост разрушится, а в момент этого ЧП ситуация для шофёра станет, мягко говоря, исключительной. И вот, дабы такого не произошло, дорожные службы заранее устанавливают на дороге соответствующие предупреждающие знаки. И тогда водитель, посмотрев на знак, сравнит массу своего авто со значением разрешённой грузоподъёмности и примет соответствующее решение, например, поедет по другой дороге.

То есть мы видим, что из-за правильных действий дорожной службы шоферы крупногабаритных транспортных средств:
1) получили возможность заранее изменить свой путь;
2) были предупреждены об опасности;
3) были предупреждены о невозможности проезжать по мосту при определённых условиях.

Вот как наш жизненный пример соотносится с применением исключения на Java:

1-20219-9bd18c.jpg

Исходя из вышесказанного, мы можем назвать одну из причин применения исключений в Java. Заключается она в возможности предупреждения исключительной ситуации для её последующего разрешения и продолжения работы программы. То есть механизм исключений позволит защитить написанный код от неверного применения пользователем путём валидации входящих данных.

Что же, давайте ещё раз побудем дорожной службой. Чтобы установить знак, мы ведь должны знать места, где водителей ТС могут ждать различные неприятности. Это первое. Далее, нам ведь надо заготовить и установить знаки. Это второе. И, наконец, надо предусмотреть маршруты объезда, позволяющие избежать опасности.

В общем, механизм исключений в Java работает схожим образом. На стадии разработки программы мы выполняем «ограждение» опасных участков кода в отношении наших исключений, используя блок try{}. Чтобы предусмотреть запасные пути, применяем блок catch{}. Код, выполняемый в программе при любом исходе, пишем в блоке finally{}.

Иногда бывает, что мы не можем предусмотреть «запасной аэродром» либо специально желаем предоставить право его выбора юзеру. Но всё равно мы должны как минимум предупредить пользователя об опасности. Иначе он превратится в разъярённого шофёра, который ехал долго, не встретил ни одного предупреждающего знака и в итоге добрался до аварийного моста, проехать по которому не представляется возможным.

Что касается программирования на Java, то мы, когда пишем свои классы и методы, далеко не всегда можем предвидеть контекст их применения другими программистами в своих программах, а значит, не можем со стопроцентной вероятностью предвидеть правильный путь для разрешения исключительных ситуаций. Но предупредить коллег о возможной исключительной ситуации мы всё-таки должны, и это не что иное, как правило хорошего тона.

Выполнить это правило в Java нам как раз и помогает механизм исключений с помощью throws. Выбрасывая исключение, мы, по сути, объявляем общее поведение нашего метода и предоставляем пользователю метода право написания кода по обработке исключения.

Предупреждаем о неприятностях

Если мы не планируем обрабатывать исключение в собственном методе, но желаем предупредить пользователей метода о возможной исключительной ситуации, мы используем, как это уже было упомянуто, ключевое слово throws. В сигнатуре метода оно означает, что при некоторых обстоятельствах метод может выбросить исключение. Это предупреждение становится частью интерфейса метода и даёт право пользователю на создание своего варианта реализации обработчика исключения.

После упоминания ключевого слова throws мы указываем тип исключения. Как правило, речь идёт о наследниках класса Exception Java. Но так как Java — это объектно-ориентированный язык программирования, все исключения представляют собой объекты.

2-20219-ee1e82.jpg

Иерархия исключений в Java

Когда возникают ошибки при выполнении программы, исполняющая среда Java Virtual Machine обеспечивает создание объекта нужного типа, используя иерархию исключений Java — речь идёт о множестве возможных исключительных ситуаций, которые унаследованы от класса Throwable — общего предка. При этом исключительные ситуации, которые возникают в программе, делят на 2 группы:
1. Ситуации, при которых восстановление нормальной дальнейшей работы невозможно.
2. Ситуации с возможностью восстановления.

К первой группе можно отнести случаи, при которых возникают исключения, которые унаследованы из класса Error. Это ошибки, возникающие во время выполнения программы при сбое работы Java Virtual Machine, переполнении памяти либо сбое системы. Как правило, такие ошибки говорят о серьёзных проблемах, устранение которых программными средствами невозможно. Данный вид исключений в Java относят к неконтролируемым исключениям на стадии компиляции (unchecked). К этой же группе относятся и исключения-наследники класса Exception, генерируемые Java Virtual Machine в процессе выполнения программы — RuntimeException. Данные исключения тоже считаются unchecked на стадии компиляции, а значит, написание кода по их обработке необязательно.

Что касается второй группы, то к ней относят ситуации, которые можно предвидеть ещё на стадии написания приложения, поэтому для них код обработки должен быть написан. Это контролируемые исключения (checked). И в большинстве случаев Java-разработчики работают именно с этими исключениями, выполняя их обработку.

Создание исключения

В процессе исполнения программы исключение генерируется Java Virtual Machine либо вручную посредством оператора throw. В таком случае в памяти происходит создание объекта исключения, выполнение основного кода прерывается, а встроенный в JVM обработчик исключений пробует найти способ обработать это самое исключение.

Обработка исключения

Обработка исключений в Java подразумевает создание блоков кода и производится в программе посредством конструкций try{}finally{}, try{}catch, try{}catch{}finally.

3-20219-4ec690.jpg

В процессе возбуждения исключения в try обработчик исключения ищется в блоке catch, который следует за try. При этом если в catch присутствует обработчик данного вида исключения, происходит передача управления ему. Если же нет, JVM осуществляет поиск обработчика данного типа исключения, используя для этого цепочку вызова методов. И так происходит до тех пор, пока не находится подходящий catch. После того, как блок catch выполнится, управление переходит в необязательный блок finally. Если подходящий блок catch найден не будет, Java Virtual Machine остановит выполнение программы, выведя стек вызовов методов под названием stack trace. Причём перед этим выполнится код блока finally при наличии такового.

Рассмотрим практический пример обработки исключений:

public class Print {

     void print(String s) {
        if (s == null) {
            throw new NullPointerException("Exception: s is null!");
        }
        System.out.println("Inside method print: " + s);
    }

    public static void main(String[] args) {
        Print print = new Print();
        List list= Arrays.asList("first step", null, "second step");

        for (String s:list) {
            try {
                print.print(s);
            }
            catch (NullPointerException e) {
                System.out.println(e.getMessage());
                System.out.println("Exception was processed. Program continues");
            }
            finally {
                System.out.println("Inside bloсk finally");
            }
            System.out.println("Go program....");
            System.out.println("-----------------");
        }

    }
    }

А теперь глянем на результаты работы метода main:

Inside method print: first step
Inside bloсk finally
Go program....
-----------------
Exception: s is null!
Exception was processed. Program continues
Inside bloсk finally
Go program....
-----------------
Inside method print: second step
Inside bloсk finally
Go program....
-----------------

Блок finally чаще всего используют, чтобы закрыть открытые в try потоки либо освободить ресурсы. Но при написании программы уследить за закрытием всех ресурсов возможно не всегда. Чтобы облегчить жизнь разработчикам Java, была предложена конструкция try-with-resources, автоматически закрывающая ресурсы, открытые в try. Используя try-with-resources, мы можем переписать наш первый пример следующим образом:

public String input() throws MyException {
    String s = null;
    try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")){
        throw new MyException ("String can not be empty!");
    }
    return s;
}

А благодаря появившимся возможностям Java начиная с седьмой версии, мы можем ещё и объединять в одном блоке перехват разнотипных исключений, делая код компактнее и читабельнее:

public String input() {
    String s = null;
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
        s = reader.readLine();
        if (s.equals("")) {
            throw new MyException("String can not be empty!");
        }
    } catch (IOException | MyException e) {
        System.out.println(e.getMessage());
    }
    return s;
}

Итоги

Итак, применение исключений в Java повышает отказоустойчивость программы благодаря использованию запасных путей. Кроме того, появляется возможность отделить код обработки исключительных ситуаций от логики основного кода за счёт блоков catch и переложить обработку исключений на пользователя кода посредством throws.

Основные вопросы об исключениях в Java

1.Что такое проверяемые и непроверяемые исключения?
Если говорить коротко, то первые должны быть явно пойманы в теле метода либо объявлены в секции throws метода. Вторые вызываются проблемами, которые не могут быть решены. Например, это нулевой указатель или деление на ноль. Проверяемые исключения очень важны, ведь от других программистов, использующих ваш API, вы ожидаете, что они знают, как обращаться с исключениями. К примеру, наиболее часто встречаемое проверяемое исключение — IOException, непроверяемое — RuntimeException.
2.Почему переменные, определённые в try, нельзя использовать в catch либо finally?
Давайте посмотрим на нижеследующий код. Обратите внимание, что строку s, которая объявлена в блоке try, нельзя применять в блоке catch. То есть данный код не скомпилируется.

try {
    File file = new File("path");
    FileInputStream fis = new FileInputStream(file);
    String s = "inside";
} catch (FileNotFoundException e) {
    e.printStackTrace();
    System.out.println(s);
}

А всё потому, что неизвестно, где конкретно в try могло быть вызвано исключение. Вполне вероятно, что оно было вызвано до объявления объекта.
3.Почему Integer.parseInt(null) и Double.parseDouble(null) вызывают разные исключения?
Это проблема JDK. Так как они были разработаны разными людьми, то заморачиваться вам над этим не стоит:

Integer.parseInt(null);
// вызывает java.lang.NumberFormatException: null

Double.parseDouble(null);
// вызывает java.lang.NullPointerException

4.Каковы основные runtime exceptions в Java?
Вот лишь некоторые из них:

IllegalArgumentException
ArrayIndexOutOfBoundsException

Их можно задействовать в операторе if, если условие не выполняется:

if (obj == null) {
   throw new IllegalArgumentException("obj не может быть равно null");

5.Возможно ли поймать в одном блоке catch несколько исключений?
Вполне. Пока классы данных исключений можно отследить вверх по иерархии наследования классов до одного и того же суперкласса, возможно применение только этого суперкласса.
6.Способен ли конструктор вызывать исключения?
Способен, ведь конструктор — это лишь особый вид метода.

class FileReader{
    public FileInputStream fis = null;

    public FileReader() throws IOException{
        File dir = new File(".");//get current directory
        File fin = new File(dir.getCanonicalPath() + File.separator + "not-existing-file.txt");
        fis = new FileInputStream(fin);
    }
}

7.Возможен ли вызов исключений в final?
В принципе, можете сделать таким образом:

public static void main(String[] args) {
    File file1 = new File("path1");
    File file2 = new File("path2");
    try {

        FileInputStream fis = new FileInputStream(file1);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        try {
            FileInputStream fis = new FileInputStream(file2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Но если желаете сохранить читабельность, объявите вложенный блок try-catch в качестве нового метода и вставьте вызов данного метода в блок finally.

finally. 
public static void main(String[] args) {
    File file1 = new File("path1");
    File file2 = new File("path2");
    try {

        FileInputStream fis = new FileInputStream(file1);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        methodThrowException();
    }
}

JavaSpec_Welcome_970x90-1801-439a19.png

Экзаменационные вопросы по предмету «Программирование на языке Джава»

Если вы хотите помочь с заполнением вопросов или исправить ошибку, то создайте issue или pull request. Спасибо!

Тема 1. Особенности платформы Java. Синтаксис языка Java

1. К какому типу языков относится язык Джава

Java — строго типизированный объектно-ориентированный язык программирования общего назначения, разработанный компанией Sun Microsystems (в последующем приобретённой компанией Oracle). Разработка ведётся сообществом, организованным через Java Community Process; язык и основные реализующие его технологии распространяются по лицензии GPL. Права на торговую марку принадлежат корпорации Oracle.

Приложения Java обычно транслируются в специальный байт-код, поэтому они могут работать на любой компьютерной архитектуре, для которой существует реализация виртуальной Java-машины. Дата официального выпуска — 23 мая 1995 года. Занимает высокие места в рейтингах популярности языков программирования (2-е место в рейтингах IEEE Spectrum (2020) и TIOBE (2021)).

2. Особенности языка Джава

Некоторые из ключевых особенностей языка программирования Java:

  • Объектно-ориентированность: Java является объектно-ориентированным языком, что означает, что он позволяет разработчикам определять объекты и манипулировать ими в своем коде.

  • Независимость от платформы: Java-программы компилируются в байт-код, который можно запускать на любом устройстве, на котором установлена виртуальная машина Java (JVM). Это означает, что программы Java являются переносимыми, то есть их можно запускать на любом устройстве, поддерживающем Java, независимо от используемого оборудования и операционной системы.

  • Многопоточность: Java позволяет разработчикам создавать многопоточные программы, что означает, что программа может выполнять несколько задач одновременно. Это полезно для разработки программ, которые должны выполнять несколько задач одновременно, например веб-серверов, которым необходимо обрабатывать несколько запросов одновременно.

  • Надежность: Java обладает рядом функций, которые делают его надежным языком, включая механизм сборки мусора, который автоматически освобождает память, используемую объектами, которые больше не нужны, и строгую систему типов, помогающую предотвратить распространенные ошибки программирования.

  • Безопасность: Java имеет встроенную поддержку функций безопасности, таких как возможность цифровой подписи и проверки кода, а также возможность шифрования и расшифровки данных.

  • Высокая производительность: Java-программы можно оптимизировать для быстрой работы, и существует ряд инструментов и методов, помогающих разработчикам писать высокопроизводительный Java-код.

  • Широкое использование: Java является одним из самых популярных языков программирования в мире и используется для разработки широкого спектра приложений, включая веб-приложения, мобильные приложения, настольные приложения и серверные приложения.

3. Класс Scanner и его использование для чтения стандартного потока ввода

Класс Scanner — это класс Java, который используется для чтения входных данных из различных источников, таких как файлы, сетевые сокеты и стандартный поток ввода (stdin). Он является частью пакета java.util и может использоваться для чтения входных данных различных типов, таких как целые числа, двойные числа, строки и логические значения.

Чтобы использовать класс Scanner для чтения ввода из стандартного потока ввода, вы можете создать новый объект Scanner и передать стандартный поток ввода (System.in) в качестве аргумента конструктору. Например:

Scanner scanner = new Scanner(System.in);

После того, как вы создали объект Scanner, вы можно использовать его различные методы для чтения ввода из стандартного потока ввода. Например, чтобы прочитать целое число из стандартного потока ввода, вы можно использовать метод nextInt():

int i = scanner.nextInt();

Чтобы прочитать двойное число, вы можно использовать метод nextDouble():

double d = scanner.nextDouble();

Чтобы прочитать строку, вы можно использовать метод nextLine():

String s = scanner.nextLine();

Также можно передать объект File, URL-адрес или строку, содержащую входные данные, конструктору сканера для чтения входных данных из этих источников. Например, чтобы прочитать ввод из файла, можно сделать что-то вроде этого:

File file = new File("input.txt");
Scanner scanner = new Scanner(file);

Или, чтобы прочитать ввод с URL-адреса, можно сделать это:

URL url = new URL("http://www.example.com");
Scanner scanner = new Scanner(url.openStream());

Также можно использовать метод hasNext(), чтобы проверить, доступны ли дополнительные данные для чтения, и метод close(), чтобы закрыть Scanner и освободить все используемые им ресурсы.

Вот пример того, как можно использовать класс Scanner для чтения ряда целых чисел из стандартного потока ввода и вывода их суммы:

int sum = 0;
while (scanner.hasNextInt()) {
    int n = scanner.nextInt();
    sum += n;
}
System.out.println("Sum: " + sum);
scanner.close();

4. Класс Scanner, конструктор класса Scanner для чтения стандартного потока ввода

Дубликат вопроса 3.

5. Методы класса Scanner nextLine(), nextInt(), hasNextInt(), hasNextLine() и их использование для чтения ввода пользователя с клавиатуры

Можно использовать его различные методы для чтения ввода от пользователя. Вот некоторые распространенные методы класса Scanner и то, как их можно использовать для чтения ввода от пользователя:

  • nextLine(): считывает строку ввода (то есть строку, которая заканчивается символом новой строки) от пользователя.

  • nextInt(): считывает целое число от пользователя.

  • hasNextInt(): возвращает true, если есть целое число, доступное для чтения, и false в противном случае.

  • hasNextLine(): возвращает true, если есть строка ввода, доступная для чтения, и false в противном случае.

6. Примитивные типы данных, объявление и присваивание переменных

В Java примитивный тип данных — это базовый тип данных, встроенный в язык и представляющий одно значение. В Java существует восемь примитивных типов данных:

  • boolean: логическое значение. Может быть либо true, либо false.
  • char: символьное значение. это один символ, например «a» или «1».
  • byte: значение с плавающей запятой, представляющее собой 8-битное целое число со знаком (от -128 до 127).
  • short: целочисленное значение, представляющее собой 16-разрядное целое число со знаком (от -32 768 до 32 767).
  • int: целочисленное значение, представляющее собой 32-разрядное целое число со знаком (от -2 147 483 648 до 2 147 483 647).
  • long: целочисленное значение, представляющее собой 64-битное целое число со знаком (от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807).
  • float: значение с плавающей запятой, представляющее собой 32-разрядное число с плавающей запятой одинарной точности.
  • double: значение с плавающей запятой, представляющее собой 64-разрядное число с плавающей запятой двойной точности.

Чтобы объявить переменную в Java, вам нужно указать тип данных переменной и имя для переменной. Например:

Это объявляет две переменные, x и y, типа int и double соответственно.

Чтобы присвоить значение переменной, можно использовать оператор присваивания (=). Например:

int x = 5;
double y = 3.14;

Это присваивает значение 5 переменной x и значение 3,14 переменной y.

Важно отметить, что нельзя изменить тип данных переменной после ее объявления. Например, если вы объявите переменную типа int, вы не сможете впоследствии присвоить ей double значение.

7. Условные операторы, полное и неполное ветвление в Джава, синтаксис

В Java можно использовать условные операторы для выполнения ветвления, что означает выполнение различных блоков кода в зависимости от значения определенных условий.

В Java есть три основных типа условных операторов:

  • Оператор if. Оператор if позволяет выполнить блок кода, если выполняется определенное условие. Например:
if (x > 0) {
    System.out.println("x is positive");
}
  • Оператор if-else. Оператор if-else позволяет выполнять один блок кода, если определенное условие истинно, и другой блок кода, если условие ложно. Например:
if (x > 0) {
    System.out.println("x is positive");
} else {
    System.out.println("x is not positive");
}
  • Оператор switch: оператор switch позволяет выполнять другой блок кода на основе значения переменной или выражения. Например:
switch (x) {
    case 1:
        System.out.println("x is 1");
        break;
    case 2:
        System.out.println("x is 2");
        break;
    default:
        System.out.println("x is something else");
        break;
}

Также можно использовать условный оператор (также известный как тернарный оператор) для выполнения простого ветвления в одной строке кода. Синтаксис условного оператора следующий:

условие? значение_если_истина : значение_если_ложь

Например:

int y = (x > 0) ? 1 : -1;

8. Оператор множественного выбора в Джава, синтаксис

Можно использовать оператор switch для выполнения нескольких ветвлений на основе значения переменной или выражения. Синтаксис оператора switch следующий:

switch (expression) {
    case value1:
        // code to execute if expression equals value1
        break;
    case value2:
        // code to execute if expression equals value2
        break;
    // ...
    default:
        // code to execute if expression does not match any of the case values
        break;
}

Необходмо обратить внимание, что каждый case в операторе switch должен завершаться оператором break, что приводит к выходу потока управления из оператора switch. Если опустить оператор break, поток управления продолжится до следующего случая, что может привести к непредвиденным последствиям.

9. Класс System. Работа со стандартами потоками вывода

Класс System — это встроенный класс Java, предоставляющий доступ к различным функциям и переменным системного уровня. Одной из функций, предоставляемых классом System, является возможность работы со стандартным потоком вывода, представляющим собой поток, который используется для вывода данных на консоль или в окно терминала.

Для вывода данных в стандартный поток вывода в Java можно использовать статический метод println() объекта System.out. Этот метод принимает один аргумент, то есть данные, которые вы хотите вывести, и выводит данные на консоль, за которыми следует символ новой строки.

Например, чтобы вывести строку «Hello, world!» в консоль можно использовать следующий код:

System.out.println("Hello, world!");

Также можно использовать статический метод print() объекта System.out для вывода данных в стандартный поток вывода без символа новой строки. Например:

System.out.print("Hello, ");
System.out.print("world!");

В дополнение к методам println() и print() объект System.out предоставляет несколько других методов для вывода данных в стандартный поток вывода, например, printf() для форматированного вывода и write() для записи необработанных байтов.

Метод printf() — это метод класса PrintStream (который является суперклассом объекта System.out), который используется для вывода форматированной строки в стандартный поток вывода. Он принимает строку формата и список аргументов и заменяет заполнители в строке формата соответствующими аргументами.

Вот пример того, как можно использовать метод printf() для вывода форматированной строки на консоль:

Метод write(), с другой стороны, является методом класса OutputStream (который является суперклассом класса PrintStream), который используется для записи для записи одиночных байтов или массива байтов в стандартный поток вывода. Он принимает массив байтов в качестве аргумента и записывает байты в поток.

byte[] data = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21};
System.out.write(data);

Вот пример того, как вы можете использовать метод write() для вывода массива байтов на консоль:

int x = 10;
double y = 3.14;
System.out.printf("x is %d and y is %fn", x, y);

Этот код выведет строку «Hello, world!» к консоли. Массив байтов содержит коды ASCII для символов в строке, а метод write() записывает байты в стандартный поток вывода.

Метод write() не добавляет автоматически символ новой строки, поэтому вам нужно использовать отдельный вызов println(), если вы хотите включить новую строку в свой вывод.

10. Перегруженные методы out.println() класса System и их использование для вывода в консоль

Метод println() объекта System.out является перегруженным методом, что означает, что он имеет несколько версий, которые принимают различные типы аргументов. Различные версии метода println() позволяют вам выводить на консоль различные типы данных удобным и простым способом.

Вот список различных версий метода println() вместе с типами аргументов, которые они принимают, и соответствующим выводом, который они производят:

  • println(boolean x): принимает логическое значение и выводит либо «true», либо «false».
  • println(char x): принимает значение char и выводит символ,.
  • println(int x): принимает значение int и выводит целое число.
  • println(long x): принимает long значение и выводит целое число.
  • println(float x): принимает значение с плавающей запятой и выводит число с плавающей запятой.
  • println(double x): принимает значение с плавающей запятой и выводит число с плавающей запятой.
  • println(char[] x): принимает массив значений char и выводит символы в массиве. В качестве типа массива можно использовать и другие типы.
  • println(String x): принимает значение String и выводит строку.
  • println(Object x): принимает любой объект и выводит результат вызова метода объекта toString().

11. Константы в Джава: объявление константы

В Java вы можете объявить константу, используя ключевое слово final. Константа — это значение, которое нельзя изменить после его инициализации.

Чтобы объявить константу в Java, вы можете использовать следующий синтаксис:

Также можно использовать ключевые слова static и final вместе, чтобы объявить константу, которая является переменной класса (т. е. переменной, которая является общей для всех экземпляров класса). Например:

public class MyClass {
    static final double PI = 3.14159;
    // ...
}

Константы должны быть инициализированы значением при их объявлении. Нельзя объявить константу и оставить ее неинициализированной.

12. В результате выполнения этой строчки

???

13. Объявление и использование бестиповых переменных в Джава

В Java нет концепции нетипизированных переменных. Все переменные в Java должны иметь определенный тип, который определяет значения, которые может содержать переменная, и операции, которые можно с ней выполнять.

Однако в Java есть тип Object, который можно использовать для хранения ссылки на любой объект. Тип Object является корнем иерархии классов в Java, и все классы являются подклассами Object.

Пример объявления:

Можно присвоить любой объект этой переменной, например:

obj = "Hello, world!"; // assign a String object to obj
obj = 123; // assign an Integer object to obj

Необходимо обратить внимание, что когда присваивается примитивное значение (например, int или double) переменной Object, это значение автоматически помещается в объект соответствующего класса-оболочки (например, Integer или Double). Это известно как Автобоксинг.

Чтобы получить доступ к значению, хранящемуся в переменной типа Object, необходимо привести его к соответствующему типу, а затем использовать соответствующие методы для извлечения значения. Например:

String str = (String) obj; // cast obj to a String and assign it to str
int x = ((Integer) obj).intValue(); // cast obj to an Integer and extract the int value

Важно быть осторожным при работе с нетипизированными переменными, так как они могут привести к ошибкам во время выполнения, если попытаться использовать их способом, несовместимым с их фактическим типом.

Как правило, рекомендуется использовать типизированные переменные, когда это возможно, так как они могут помочь предотвратить ошибки и сделать ваш код более понятным. Однако бывают ситуации, когда нетипизированные переменные могут быть полезны, например, когда вам нужно записать значение типа, неизвестного во время компиляции.

14. Объявление переменных и инициализация типа класс

Чтобы инициализировать переменную типа класса (т. е. переменную, которая содержит ссылку на объект), можно использовать оператор new для создания экземпляра класса и присвоения ссылки переменной.

Например:

String str = new String("Hello, world!");

Также можно использовать оператор new для создания экземпляра класса с конструктором, принимающим аргументы. Например:

public class MyClass {
    private int x;
    private int y;
    public MyClass(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Чтобы создать экземпляр MyClass и инициализировать его значениями 10 и 20, используем следующий код:

MyClass obj = new MyClass(10, 20);

Важно отметить, что переменные типа класса являются ссылками на объекты, а не на объектами. Когда вы присваиваете переменную типа класса другой переменной, вы копируете только ссылку, а не сам объект.

Важно отметить, что необходимо инициализировать переменную типа класса, прежде чем использовать.

15. Арифметические операции, операции инкремента и декремента в Джава

В Java можно выполнять арифметические операции, используя стандартные арифметические операторы: + для сложения, - для вычитания, * для умножения, / для деления и % (остаток от деления).

Вы также можете использовать операторы ++ и -- для инкремента и декремента. Например:

int x = 10;
x++; // x will be 11
x--; // x will be 10 again

Операторы ++ и -- могут использоваться как префиксные операторы (например, ++x) или постфиксные операторы (например, x++). При использовании в качестве префиксного оператора инкремент или декремент применяется до того, как значение переменной будет использовано в выражении. При использовании в качестве постфиксного оператора инкремент или декремент применяется после того, как значение переменной используется в выражении.

int x = 10;
int y = ++x; // y will be 11, x will be 11
int z = x++; // z will be 11, x will be 12

Важно отметить, что операторы ++ и — имеют более высокий приоритет, чем арифметические операторы. Например:

int x = 10;
int y = 20;
int z = x++ * y; // z will be 200, x will be 11 

Вы можете использовать круглые скобки, чтобы указать порядок операций, если вам нужно изменить приоритет операций.

16. В результате выполнения фрагмента программы

???

17. Арифметические операции, приоритет выполнения операций

Список приоритетов арифметических операторов в Java, от высшего к низшему:

  • ++, — (префикс инкремента и декремента)
  • ! (логическое НЕ)
  • *, / % (умножение, деление и остаток от деления)
  • +, — (сложение и вычитание)
  • <=, <, > и >= (операторы сравнения)
  • ==, != (операторы равенства)
  • && (логическое И)
  • || (логическое ИЛИ)

18. Типы данных в языке Джава, классификация, примеры

В Java типы данных делят на две большие группы: примитивные и ссылочные. В состав примитивных типов (или просто примитивов) входят четыре подвида и восемь типов данных:

  • целые числа (byte, short, int, long);

  • числа с плавающей точкой (float, double);

  • логический (boolean);

  • символьный (char).

Ссылочные типы данных ещё называют ссылками. К ним относятся все классы, интерфейсы, массивы, а также тип данных String.

Хотя у примитивов и ссылок много общего, между ними есть существенные различия. И главное различие — в том, что именно в них хранится.

Примитивные типы Ссылочные типы
Хранят значение Хранят адрес объекта в памяти, на который ссылаются (отсюда и название).
Создаются присваиванием значения Создаются через конструкторы классов (присваивание только создаёт вторую ссылку на существующий объект)
Имеют строго заданный диапазон допустимых значений По умолчанию их значение — null
В аргументы методов попадают копии значения переменной (это передача по значению) В методы передаётся значение ссылки — операция выполняется над оригинальным объектом, на который ссылается переменная

19. Массивы в Джава, объявление и инициализация массивов, длина массива, получение доступа к элементу массива

В Java массив представляет собой набор фиксированного размера элементов одного и того же типа данных. Вы можете объявить массив, указав тип данных элементов и имя массива, за которыми следуют квадратные скобки []. Например:

Инициалия массива означает создание массива определенного размера и заполнение его значениями по умолчанию (0 для числовых типов, false для логических и null для ссылочных типов). Можно инициализировать массив с помощью оператора new и указать размер массива в квадратных скобках []. Например:

int[] numbers = new int[10];

Также можно инициализировать массив определенными значениями, используя инициализатор массива. Инициализатор массива представляет собой список значений, заключенных в фигурные скобки {}.

int[] numbers = {1, 2, 3};

Важно отметить, что можно получать доступ только к элементам массива, которые находятся в пределах границ массива. Если вы попытаетесь получить доступ к элементу с отрицательным индексом или большим или равным размеру массива, вы вызовете исключение ArrayIndexOutOfBoundsException.

Вы можете использовать длину массива, используя length.

Размер массива фиксирован и не может быть изменен после создания массива. Если необходимо хранить переменное количество элементов, можно использовать, например, arrayList или LinkedList.

20. Массивы в Джава, как объектные типы данных, контроль доступа за выход за границы массива

В Java массивы — это объектные типы данных, что означает, что массив является экземпляром класса Array. Это означает, что массивы обладают некоторыми характеристиками объектов, такими как способность иметь поля (переменные) и методы (функции).

Одним из полей класса Array является поле length, которое содержит размер массива. Оно является константным полем, что означает, что оно не может быть изменено после инициализации массива.

Важно отметить, что можно получать доступ только к элементам массива, которые находятся в пределах границ массива. Если вы попытаетесь получить доступ к элементу с отрицательным индексом или большим или равным размеру массива, вы вызовете исключение ArrayIndexOutOfBoundsException.

Чтобы предотвратить это исключение, можно использовать поле length, чтобы проверить, находится ли индекс в пределах границ массива перед доступом к элементу. Например:

int[] numbers = {1, 2, 3};
int index = 5;
if (index >= 0 && index < numbers.length) {
    int element = numbers[index];
    System.out.println(element);
} else {
    System.out.println("Index out of bounds");
}

21. Операции над массивами, просмотр элементов массива, поиск по образцу, сортировка массива, сумма элементов массива

Чтобы просмотреть элементы массива, вы можете использовать цикл для перебора элементов массива. Например:

int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
    int element = numbers[i];
    System.out.println(element);
}

Для поиска определенного элемента в массиве вы можете использовать цикл и условный оператор. Например:

int[] numbers = {1, 2, 3, 4, 5};
int target = 3;
for (int i = 0; i < numbers.length; i++) {
    if (numbers[i] == target) {
        System.out.println("Found at index " + i);
        break;
    }
}

Для сортировки массива можно использовать один из алгоритмов сортировки, предоставляемых стандартной библиотекой Java, например Arrays.sort(). Например:

int[] numbers = {4, 3, 2, 1, 5};
Arrays.sort(numbers);

Чтобы вычислить сумму элементов массива, вы можете использовать цикл для перебора элементов массива и добавления их к промежуточной сумме. Например:

int[] numbers = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
    sum += numbers[i];
}
System.out.println("Sum: " + sum);

22. В результате выполнения фрагмента программы

???

23. Операция конкатенации строк в Джава, ее обозначение и использование и ее использование

В Java оператор + используется для конкатенации (объединения) строк. Например:

String s1 = "Hello";
String s2 = "World";
String s3 = s1 + " " + s2; // s3 = "Hello World"

Также можно использовать оператор + для объединения строки с нестроковым значением. В этом случае нестроковое значение будет преобразовано в строку с помощью метода toString().

int i = 123;
String s = "The value is " + i; // s = "The value is 123"

Важно отметить, что конкатенация строк является относительно дорогостоящей операцией, поскольку она включает в себя создание нового строкового объекта и копирование в него содержимого старых строк. Если вам нужно объединить большое количество строк, как правило, более эффективно использовать StringBuilder или StringBuffer.

Например, чтобы объединить большое количество строк с помощью StringBuilder, вы можете использовать метод append():

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append("x");
}
String s = sb.toString();

StringBuilder и StringBuffer похожи на String, но они изменяемы, что означает, что вы можете изменять их содержимое, не создавая новый объект. Это делает их более эффективными для объединения строк, поскольку они позволяют избежать расходов на создание новых объектов для каждого объединения.

24. Циклы в Джава, цикл с предусловием, цикл с постусловием, пример записи и использование. Условие окончания цикла

Цикл while — это цикл с предусловием, то есть условие цикла проверяется до выполнения тела цикла. Цикл while имеет следующий синтаксис:

while (condition) {
// loop body
}

Цикл do while похож на цикл while, но условие проверяется после выполнения тела цикла. Цикл do while имеет следующий синтаксис:

do {
    // loop body
} while (condition);

25. Циклы в Джава, итерационный цикл for(), синтаксис, счетчик цикла, условие окончания цикла, модификация счетчика, пример использования

Цикл for — это цикл с счетчиком. Цикл for имеет следующий синтаксис:

for (initialization; condition; iteration) {
    // loop body
}

Счетчик цикла инициализируетася перед выполнением цикла.

Условие окончания цикла проверяеттся перед каждой итерацией цикла. Если условие истинно, цикл продолжается; в противном случае цикл завершается.

Модификатор счетчика выполняется после каждой итерации цикла. Обычно он используется для обновления переменной цикла.

Пример использования:

for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}

26. Способы объявления массивов в Джава, использование операции new для выделения памяти для элементов массива. Объявление с инициализацией, объявление массива определенного размера без инициализации

Дубликат 19 вопроса

Тема 2. Реализация ООП в Java

27. Объявление класса на Джава, пример объявления

В Java класс — это шаблон для создания объектов. Класс определяет данные и поведение (методы) типа объекта.

Чтобы объявить класс в Java, используется ключевое слово class, за которым следует имя класса. Тело класса заключено в фигурные скобки {}.

class MyClass {
    // class body
}

28. Использования this для доступа к компонентам класса

Иногда требуется, чтобы метод ссылался на вызвавший его объект. Ключевое слово this в Java используется в теле любого метода для ссылки на текущий объект.

class Point {
    private double x;
    private double y;
    void setLocation(double x, double y) {
        this.x = x; // refers to the "x" field of the current object
        this.y = y; // refers to the "y" field of the current object
    }
}

29. Создание или инстанцирование объектов типа класс

В Java вы можете создавать экземпляры (объекты) класса с помощью оператора new.

Например, рассмотрим следующий класс:

class Point {
    double x;
    double y;
}

Чтобы создать объект типа Point, можно использовать следующий код:

Можно инициализировать поля следующим образом:

Кроме того, можно инициализировать поля, заключив значения в фигурные скобки {}.

Point p = new Point { x = 3, y = 4 };

Когда создается объект с помощью оператора new, вызывается конструктор для данного объекта.

30. Что такое класс в Java?

Класс в Java — это шаблон для создания объекта, а объект — это экземпляр класса. Класс определяет структуру и поведение, которые будут совместно использоваться набором объектов. Класс содержит переменные и методы, которые называются элементами класса, членами класса.

31. Модификатор доступа или видимости в Джава, виды и использование

Все члены класса в языке Java — поля и методы — имеют модификаторы доступа. Модификаторы доступа позволяют задать допустимую область видимости для членов класса, то есть контекст, в котором можно употреблять данную переменную или метод.

В Java используются следующие модификаторы доступа:

  • public: публичный, общедоступный класс или член класса. Поля и методы, объявленные с модификатором public, видны другим классам из текущего пакета и из внешних пакетов.
  • private: закрытый класс или член класса, противоположность модификатору public. Закрытый класс или член класса доступен только из кода в том же классе.
  • protected: такой класс или член класса доступен из любого места в текущем классе или пакете или в производных классах, даже если они находятся в других пакетах
  • Модификатор по умолчанию. Отсутствие модификатора у поля или метода класса предполагает применение к нему модификатора по умолчанию. Такие поля или методы видны всем классам в текущем пакете.

32. Чем отличаются static-метод класса от обычного метода класса

В Java статический метод — это метод, связанный с классом, а не с экземпляром (объектом) класса. Статический метод можно вызывать для самого класса, а не для объекта класса.

Обычный (нестатический) метод — это метод, связанный с экземпляром (объектом) класса. Обычный метод может быть вызван для объекта класса и может получить доступ к полям и методам объекта.

Методы с модификатором static не могут использовать ключевое слово this

33. Для чего используется оператор new?

Дубликат 29 вопроса

34. Можно ли вызвать static-метод внутри обычного метода?

Да, статический метод можно вызвать из обычного (нестатического) метода в Java.

Чтобы вызвать статический метод из обычного метода, можно исполльзовать имя класса, за которым следует точка и имя метода. Например:

double distanceFromOrigin() {
    return Math.sqrt(x * x + y * y);
}

35. Как вызвать обычный метод класса внутри static-метода?

Чтобы вызвать обычный метод из статического метода, нужно создать объект класса и вызвать обычный метод для этого объекта. Например:

static double distanceFromOrigin(Point p) {
    double dx = p.x;
    double dy = p.y;
    return Math.sqrt(dx * dx + dy * dy);
}

36. Для чего используется в Джава ключевое слово this?

Ключевое слово this в Java используется для ссылки на текущий объект. Ключевое слово this можно использовать внутри метода класса для ссылки на текущий объект. Например:

public class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

37. Объявление и использование методов, объявленных с модификатором public static

В Java метод, объявленный с модификатором public static, является методом, доступным из любой точки программы и связанным с классом, а не с объектом класса. Методы могут быть вызваны без создания объекта класса. Например:

public class Point {
    public static double square(double x) {
        return x * x;
    }
}

public class Main {
    public static void main(String[] args) {
        double x = 3.0;
        double y = Point.square(x);
        System.out.println(y);
    }
}

38. Синтаксис объявления методов, тип возвращаемого значения, формальные параметры и аргументы

В Java методы — это блоки кода, которые выполняют определенные действия. Методы могут принимать аргументы и возвращать значение. Синтаксис объявления метода в Java следующий:

[модификаторы] тип_возвращаемого_значения имя_метода(формальные_параметры) {
    // тело метода
}

Здесь тип возвращаемого значения — это тип данных, который возвращает метод. Формальные параметры — это переменные, которые используются внутри метода. Аргументы — это значения, которые передаются в метод при его вызове.

39. Методы с пустым списком параметров

В Java методы могут быть объявлены с пустым списком параметров. В этом случае метод может быть вызван без передачи аргументов. Например:

Для объявления метода с пустым списком параметров используется следующий синтаксис:

[модификаторы] тип_возвращаемого_значения имя_метода() {
    // тело метода
}

Если перед вызовом метода передавать аргументы в метод, то компилятор выдаст ошибку.

40. Стандартные методы класса сеттеры и геттеры, синтаксис и их назначение?

Сеттеры и геттеры — это методы класса, которые используются для установки и получения значений полей класса.

Методы обычно используются для обеспечения контроля доступа к полям объекта, поскольку они позволяют указать, как можно получить доступ к полям и изменить их. Они также используются для проверки устанавливаемых или извлекаемых значений и для выполнения любых необходимых действий при изменении значений.

Сеттеры и геттеры обычно имеют следующий синтаксис:

[модификаторы] тип_возвращаемого_значения getИмя_поля() {
    // тело метода
}

[модификаторы] void setИмя_поля(тип_параметра имя_параметра) {
    // тело метода
}

41. Может ли быть поле данных класса объявлено как с модификатором static и final одновременно и что это означает?

Да, это возможно. Статическое поле данных — это поле данных, которое принадлежит классу, а не объекту. Статическое поле данных может быть объявлено как final, что означает, что его значение не может быть изменено после инициализации.

42. Методы класса конструкторы, синтаксис и назначение

Конструкторы — это методы класса, которые используются для инициализации объектов класса. Конструкторы имеют следующий синтаксис:

[модификаторы] Имя_класса([параметры]) {
    // тело метода
}

Конструкторы могут быть перегружены, то есть иметь одинаковое имя, но разные параметры.

Конструкторы могут быть объявлены как public, private или protected.

Конструктор необязательно должен быть объявлен в классе. Если конструктор не объявлен в классе, то компилятор автоматически создает конструктор по умолчанию, который не имеет параметров и не выполняет никаких действий.

43. Может ли класс иметь в своем составе несколько конструкторов?

Да, класс может иметь в своем составе несколько конструкторов. Конструкторы могут быть перегружены, то есть иметь одинаковое имя, но разные параметры.

Пример:

class MyClass {
    int x;
    MyClass() {
        x = 10;
    }
    MyClass(int i) {
        x = i;
    }
}

44. Может ли конструктор класса возвращать значение?

Нет, конструктор класса не может возвращать значение. Конструктор класса используется для инициализации объектов класса. Конструктор класса не может возвращать значение, так как возвращаемое значение должно быть типа void.

Тема 3. Реализация наследования в программах на Джаве

45. Наследование в Джава. Вид наследования и синтаксис Ключевое слово extends

Наследование в Джава — это механизм, который позволяет создавать новые классы на основе уже существующих. Новый класс называется подклассом (дочерним классом, производным классом), а существующий класс называется суперклассом (родительским классом, базовым классом).

Ключевое слово extends используется для наследования класса. Оно указывает, что класс, который объявляется после ключевого слова extends, является суперклассом для класса, который объявляется до ключевого слова extends.

Существует несколько видов наследования:

  • Одиночное наследование (single inheritance). При этом класс может наследовать только один суперкласс. Синтаксис:
class SubClass extends SuperClass {
    // тело класса
}
  • Множественное наследование. В Java множественное наследование не поддерживается. Однако стоит упомянуть, что класс может реализовывать несколько интерфейсов.

  • Иерархическое наследование. При этом класс может наследовать суперкласс, который в свою очередь может наследовать другой суперкласс и т.д. Синтаксис:

class SubClass extends SuperClass {
    // тело класса
}
class SubSubClass extends SubClass {
    // тело класса
}

46. Что означает перегрузка метода в Java (overload)?

Перегрузка метода — это возможность создавать несколько методов с одинаковым именем, но с разными параметрами. При этом каждый метод должен иметь уникальный набор параметров. При вызове метода компилятор выбирает метод, который соответствует переданным параметрам.

Пример:

public class calc {
    public int add(int a, int b) {
        return a + b;
    }
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    public double add(double a, double b) {
        return a + b;
    }
}

Перегрузка методов — полезная возможность, поскольку он позволяет повторно использовать одно и то же имя метода для разных целей.

Однако стоит упомянуть, что перегрузка методов не всегда является хорошей практикой. Например, если методы имеют одинаковые названия, но разные параметры, то это может привести к путанице. Поэтому стоит использовать перегрузку методов только в тех случаях, когда это действительно необходимо.

Также перегрузка не является полной заменой переопределения методов. Перегрузка методов используется для создания различных вариантов одного и того же метода, а переопределение методов используется для изменения поведения метода в подклассе.

47. Что означает переопределение метода в Java (override)?

Переопределение метода — это возможность создавать методы с одинаковым именем и параметрами, но с разным телом. При этом методы должны быть объявлены в одном классе, но в разных подклассах.

Пример:

public class calc {
    public int add(int a, int b) {
        return a + b;
    }
}
public class calc2 extends calc {
    @override
    public int add(int a, int b) {
        return a + b + 1;
    }
}

Строка @override не является обязательной, но ее использование позволяет избежать ошибок при переопределении методов.

Стоит отметить, что переопределение методов не является полной заменой перегрузки методов. Перегрузка методов используется для создания различных вариантов одного и того же метода, а переопределение методов используется для изменения поведения метода в подклассе.

48. В чем разница между перегрузкой и переопределением методов, поясните

В Java перегрузка методов и переопределение методов — это две разные концепции, связанные со способностью иметь несколько методов с одним и тем же именем.

Перегрузка методов относится к способности класса иметь несколько методов с одним и тем же именем, но с разными списками параметров. Перегрузка метода — это способ предоставления нескольких реализаций метода в зависимости от количества и типа аргументов, переданных методу.

Переопределение метода относится к способности подкласса переопределять или переопределять метод, который он унаследовал от своего суперкласса. Переопределение метода позволяет подклассу предоставлять собственную реализацию метода, унаследованного от суперкласса, а также изменять или расширять поведение метода.

Вот основные различия между перегрузкой и переопределением методов в Java:

  • Перегрузка основана на количестве и типе аргументов, переданных методу, а переопределение основано на сигнатуре метода. Сигнатура метода состоит из имени метода, количества и типа аргументов, а также порядка аргументов.
  • Перегрузка происходит внутри одного класса, а переопределение происходит между суперклассом и подклассом. Перегрузка методов позволяет классу иметь несколько методов с одинаковыми именами, но с разными списками параметров в одном классе. Переопределение метода позволяет подклассу переопределить или переопределить метод, который он унаследовал от своего суперкласса.
  • Перегрузка не связана с наследованием, а переопределение связано с наследованием. Перегрузка методов не требует наследования, так как происходит внутри одного класса. Переопределение метода включает в себя наследование, поскольку позволяет подклассу переопределять или переопределять метод, который он унаследовал от своего суперкласса.
  • Перегрузка разрешается во время компиляции, а переопределение разрешается во время выполнения. Перегрузка метода разрешается во время компиляции на основе количества и типа аргументов, переданных методу. Компилятор Java определяет, какую перегруженную версию метода следует вызывать, на основе аргументов, переданных методу.

49. Абстрактные классы в Джава и абстрактные методы класса

В Java абстрактный класс — это класс, который не может быть создан и который используется для обеспечения общей базы для подклассов. Абстрактный класс определяется с помощью ключевого слова abstract и может содержать как абстрактные, так и конкретные методы.

Абстрактные методы — это методы, объявленные в абстрактном классе, но не имеющие реализации. Абстрактные методы используются для определения сигнатуры метода, но не реализации, и предназначены для реализации подклассами.

Абстрактные методы определяются с помощью ключевого слова abstract и не содержат тела метода. Абстрактные методы должны быть переопределены в подклассах, иначе подкласс должен быть объявлен абстрактным. Также абстрактные методы не могут быть объявлены как final, static или private.

Стоит отметить, что абстрактные классы могут содержать как абстрактные, так и конкретные методы. Конкретные методы — это методы, которые имеют реализацию. Конкретные методы могут быть объявлены как final, static или private, но не могут быть объявлены как abstract.

abstract class Shape {
    abstract void draw();
    void msg() {
        System.out.println("This is a shape");
    }
}

Чтобы использовать абстрактный класс, необходимо создать подкласс, который наследует абстрактный класс и реализует абстрактные методы. В приведенном ниже примере класс Rectangle наследует абстрактный класс Shape и реализует абстрактный метод draw():

class Rectangle extends Shape {
    void draw() {
        System.out.println("drawing rectangle");
    }
}

Абстрактные методы могут быть переопределены в подклассах, но не могут быть переопределены в классах, которые не являются подклассами абстрактного класса.

50. Виды наследования в Джава, использование интерфейсов для реализации наследования

В Джава существует следующие виды наследования:

  • Одиночное наследование: при одиночном наследовании подкласс расширяет один суперкласс и наследует все свойства и методы суперкласса. Синтаксис одиночного наследования в Java следующий:
class Subclass-name extends Superclass-name {
   //methods and fields
}
  • Множественное наследование: при множественном наследовании подкласс расширяет несколько суперклассов и наследует свойства и методы всех суперклассов. Множественное наследование напрямую не поддерживается в Java, так как это может привести к неоднозначности и сложности.

  • Интерфейсное наследование: при интерфейсном наследовании подкласс реализует один или несколько интерфейсов. Синтаксис интерфейсного наследования в Java следующий:

  • иерархическое наследование: при иерархическом наследовании подкласс расширяет один суперкласс и наследует все свойства и методы суперкласса. Синтаксис иерархического наследования в Java следующий:

class Subclass-name extends Superclass-name {
   //methods and fields
}

class SubSubclass-name extends Subclass-name {
   //methods and fields
}
  • Интерфейсное наследование: при интерфейсном наследовании подкласс реализует один или несколько интерфейсов. Синтаксис интерфейсного наследования в Java следующий:
class Subclass-name implements Interface-name, Interface-name, ... {
   //methods and fields
}

51. Что наследуется при реализации наследования в Джава (какие компоненты класса), а что нет?

При реализации наследования в Java подкласс наследует все поля и методы суперкласса, кроме приватных полей и методов. Подкласс также может расширять суперкласс, добавляя свои собственные поля и методы.

Подкласс не наследует закрытые поля и методы своего суперкласса, поскольку закрытые поля и методы недоступны за пределами класса, в котором они определены. Приватные поля и методы предназначены для использования только внутри класса, в котором они определены, и не предназначены для совместного использования с другими классами.

Подкласс также может определять свои собственные поля и методы, которые не наследуются от суперкласса. Эти поля будут недоступны в суперклассе.

Пример:

class A {
   int i; // this is public
   private int j; // this is private
   A(int i, int j) {
      this.i = i;
      this.j = j;
   }
}

class B extends A {
   int total;
   void sum() {
      this.total = i + j; // ERROR, j is not accessible here
   }
}

Как видно из примера, подкласс не может использовать закрытые поля и методы своего суперкласса. Подкласс может использовать только открытые поля и методы своего суперкласса.

Конструкторы не наследуются. Подкласс не наследует конструкторы своего суперкласса. Подкласс должен явно вызывать конструктор своего суперкласса, используя ключевое слово super, чтобы инициализировать суперкласс.

Пример:

class A {
   int i;
   A(int i) {
      this.i = i;
   }
}
Class B extends A {
   int j;
   B(int i, int j) {
      super(i);
      this.j = j;
   }
}

52. К каким методам и полям базового класса производный класс имеет доступ (даже если базовый класс находится в другом пакете), а каким нет? Область видимости полей и данных из производного класса

В Java существует 4 уровня доступа к полям и методам класса:

  • public — доступно всем
  • protected — доступно внутри пакета и в подклассах
  • default — доступно внутри пакета
  • private — доступно только внутри класса

Тема 4. Полиморфизм в Джава. Работа со строками. Интерфейсы

53. Объявление и инициализация переменных типа String

В Java существует 2 способа инициализации переменных типа String:

  • прямая инициализация

Синтаксис:

Имя_переменной = "Значение";
  • инициализация с помощью конструктора

Синтаксис:

Имя_переменной = new String("Значение");

Литералы хранятся в пуле строк, а переменные типа String — в куче. Поэтому, при присваивании литерала типа String переменной типа String, создается новый объект типа String.

Рассмотрим пример:

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

В данном примере, переменная s1 и литерал «hello» хранятся в пуле строк, а переменная s2 и литерал «hello» хранятся в куче. Поэтому, при сравнении переменных s1 и s2, результатом будет true, а при сравнении переменных s1 и s3, результатом будет false.

Для сравнения строк используется метод equals(), который возвращает true, если строки равны, и false, если строки не равны.

Синтаксис:

Имя_переменной_1.equals(Имя_переменной_2);

54. Операция конкатенации строк и ее использование

В Java вы можете использовать оператор «+» для конкатенации (объединения) двух или более строковых значений. Это называется операцией конкатенации строк.

Синтаксис:

Имя_переменной_1 + Имя_переменной_2;

Это создает новую строку с именем «результат», которая представляет собой конкатенацию строк «строка1» и «строка2».

Вы также можете использовать оператор «+» для объединения строковых значений с другими типами данных. Например, вы можете объединить строку с числом, результатом будет строка. Целое число будет преобразовано в строку, а затем объединено с другой строкой.

Пример:

String result = "строка1" + 2; // результатом будет строка "строка12"

Также можно использовать метод concat() для конкатенации строк. Этот метод возвращает новую строку, которая является результатом конкатенации двух строк. Данный метод не изменяет исходные строки. Он также определен в классе String.

Синтаксис:

Имя_переменной_1.concat(Имя_переменной_2);

Важно отметить, что операция конкатенации строк неэффективна, так как она создает новый строковый объект для каждой конкатенации. Если вам нужно объединить много строк, вместо этого рекомендуется использовать класс StringBuffer или StringBuilder.

55. Что означает утверждение, что объект класса String является неизменяемым

В Java объект класса «String» является неизменяемым, что означает, что его нельзя изменить после создания.

Неизменяемый объект — это объект, состояние которого нельзя изменить после его создания. Это означает, что после создания строкового объекта вы не можете изменить его значение или содержимое.

Например, рассмотрим следующий код:

String str = "строка";
str.toUpperCase();

В этом коде строка str инициализируется значением «Hello». Затем для строки str вызывается метод toUpperCase(), который преобразует строку в верхний регистр. Однако этот метод не изменяет строку str, поскольку она неизменяема.

Вместо этого метод toUpperCase() создает новый строковый объект со значением в верхнем регистре и возвращает его. Если вы хотите сохранить результат метода toUpperCase(), вам нужно присвоить его новой строковой переменной:

String str = "строка";
String strUpper = str.toUpperCase();

Этот код создает новую строку с именем strUpper, которая является результатом применения метода toUpperCase() к строке str. Строка str остается неизменной.

Неизменяемый объект полезен во многих случаях, так как он гарантирует, что состояние объекта не может быть изменено по ошибке или другой частью кода. Это может помочь избежать ошибок и несоответствий в вашем коде.

Однако важно отметить, что неизменяемый объект — не всегда лучший выбор, поскольку в некоторых случаях он может быть неэффективным. Например, если вам нужно выполнить множество операций над строкой, например конкатенацию, более эффективно использовать изменяемый объект, такой как StringBuilder или StringBuffer, который можно изменить без создания нового объекта для каждой операции.

56. При создании объектов строк с помощью класса StringBuffer, например StringBuffer strBuffer = new StringBuffer(str) можно ли использовать операцию конкатенации строк или необходимо использовать методы класса StringBuffer

В Java при создании строкового объекта с помощью класса StringBuffer вы можете использовать операцию конкатенации строк, а также методы класса StringBuffer для изменения строки.

Класс StringBuffer — это изменяемая (модифицируемая) версия класса String, которая позволяет изменять строку без создания нового объекта для каждой операции.

Чтобы создать строковый объект с помощью класса StringBuffer, используйте следующий синтаксис:

StringBuffer имя_объекта = new StringBuffer(строка);

Чтобы объединить две строки с помощью класса StringBuffer, используйте метод append().

StringBuffer strBuffer = new StringBuffer("Hello");
strBuffer.append(" World");
System.out.println(strBuffer); // Hello World

57. Объявление и инициализация массива строк. Организация просмотра элементов массива

Для объявления массива строк используется следующий синтаксис:

String[] имя_массива = new String[количество_элементов];

Также можно объявить массив строк и сразу инициализировать его значениями:

String[] имя_массива = {"значение1", "значение2", "значение3"};

Для просмотра элементов массива используется цикл for-each (либо цикл for):

for (String str : имя_массива) {
    System.out.println(str);
}

Также можно использовать класс Arrays для просмотра элементов массива:

System.out.println(Arrays.toString(имя_массива));

В данном случае метод toString() возвращает строку, которая содержит все элементы массива, разделенные запятой.

58. Понятие и объявление интерфейсов в Джава

В Джава интерфейс — это абстрактный тип данных, который содержит только объявления методов, но не содержит реализацию этих методов. Интерфейсы используются для описания поведения класса, но не для реализации этого поведения.

Для объявления интерфейса используется следующий синтаксис:

interface Имя_интерфейса {
    // объявление методов
}

Для реализации интерфейса в классе используется следующий синтаксис:

class Имя_класса implements Имя_интерфейса {
    // реализация методов
}

Интерфейсы могут содержать константы, но не могут содержать переменные.

Рассмотрим пример объявления интерфейса и класса, который реализует этот интерфейс:

interface Shape {
    double PI = 3.14;
    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Рисуем круг");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape = new Circle();
        shape.draw();
    }
}

В данном примере интерфейс Shape содержит константу PI и метод draw(). Класс Circle реализует интерфейс Shape и реализует метод draw(). Также при создании объекта класса Circle мы можем использовать ссылку на интерфейс Shape, так как класс Circle реализует интерфейс Shape.

Интерфейс полезен во многих случаях, так как он позволяет определить набор поведений или функций, которые должен реализовать класс, не указывая, как эти поведения реализуются. Это позволяет вам создавать гибкий и многоразовый дизайн для вашего кода и применять один и тот же интерфейс к разным классам с разными реализациями.

В новых версиях Java (с 8 по 11) интерфейсы могут содержать статические методы, которые реализуются в классе, который реализует интерфейс. Для объявления статического метода в интерфейсе используется следующий синтаксис:

interface Имя_интерфейса {
    static void имя_метода() {
        // тело метода
    }
}

60. Что входит в состав интерфейса. (какие компоненты может содержать интерфейс)?

Интерфейс может содержать следующие компоненты:

  • Константы. Поля, объявленные в интерфейсе, должны быть инициализированы и являются константами. Эти константы не могут быть изменены в классе, который реализует интерфейс.

  • Абстрактные методы (методы без реализации). Методы, объявленные в интерфейсе, должны быть абстрактными. Эти методы должны быть реализованы в классе, который реализует интерфейс.

  • Методы по умолчанию (default methods). Методы, объявленные в интерфейсе, могут быть реализованы в интерфейсе. Эти методы могут быть переопределены в классе, который реализует интерфейс.

  • Статические методы (static methods). Методы, объявленные в интерфейсе, могут быть реализованы в интерфейсе. Эти методы не могут быть переопределены в классе, который реализует интерфейс.

  • Вложенные типы (nested types). Интерфейс может содержать вложенные типы, такие как классы, интерфейсы, перечисления и аннотации.

61. Может ли интерфейс наследоваться от другого интерфейса?

Да, интерфейс может наследоваться от другого интерфейса. Интерфейс может наследовать только интерфейсы, но не классы.

62. Интерфейс Camparable, назначение, его методы и использование в Джава

В Java интерфейс Comparable — это стандартный интерфейс, который определяет метод, называемый compareTo(), который позволяет сравнивать два объекта. Интерфейс Comparable является частью пакета «java.lang» и реализуется многими классами в библиотеке Java, такими как String, Integer или Double.

Интерфейс Comparable имеет следующий синтаксис:

public interface Comparable<T> {
    public int compareTo(T o);
}

Этот интерфейс объявляет один абстрактный метод с именем compareTo(), который принимает один аргумент типа T и возвращает значение int. Параметр типа T представляет тип сравниваемых объектов.

Чтобы реализовать Comparable интерфейс в классе, необходимо реализовать метод compareTo().

Если метод compareTo() возвращает положительное число, то первый объект больше второго объекта. Если метод compareTo() возвращает отрицательное число, то первый объект меньше второго объекта. Если метод compareTo() возвращает ноль, то объекты равны.

Пример:

public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private int rollNo;
    private String className;
    // getters and setters
    @Override
    public int compareTo(Student student) {
        return (this.rollNo - student.rollNo);
    }
}

63. Какое значение возвращает вызов метода object1.compareTo(object2), который сравнивает 2 объекта obj1 и obj2 в зависимости от объектов?

Если объект obj1 больше объекта obj2, то метод compareTo() возвращает положительное число. Если объект obj1 меньше объекта obj2, то метод compareTo() возвращает отрицательное число. Если объект obj1 равен объекту obj2, то метод compareTo() возвращает ноль.

64. Интерфейсные ссылки и их использование в Джава

В Java ссылка на интерфейс — это ссылочная переменная, которая может содержать ссылку на объект класса, реализующего конкретный интерфейс. Ссылка на интерфейс позволяет получить доступ к методам интерфейса через объект, не зная деталей реализации класса.

Чтобы объявить ссылку на интерфейс в Java, вы можете использовать имя интерфейса в качестве типа ссылочной переменной, за которым следует имя переменной.

Например, рассмотрим следующее объявление ссылки на интерфейс:

Объявляется ссылочная переменная с именем «shape», которая может содержать ссылку на объект класса, реализующего интерфейс «Shape».

Чтобы создать объект класса, реализующего интерфейс, вы можете использовать оператор new, за которым следует имя класса и аргументы конструктора.

Например, рассмотрим следующий код:

Shape shape = new Circle();

Этот код создает новый объект класса Circle, который реализует интерфейс Shape, и присваивает его ссылке интерфейса Shape.

Ссылка на интерфейс полезна во многих случаях, поскольку позволяет получить доступ к методам интерфейса, не зная деталей реализации класса. Это позволяет вам создавать гибкий и многоразовый дизайн для вашего кода и применять один и тот же интерфейс к разным классам с разными реализациями.

Тема 5. Основные принципы и типы исключительных ситуаций

65. Понятие исключительной ситуации и ее обработка

В Java исключительная ситуация представляет собой событие, которое происходит во время выполнения программы и прерывает ее нормальное выполнение. Исключительная ситуация может быть вызвана ошибкой в программе, неправильным вводом данных или другими причинами.

В Java все исключения делятся на два основных типа: Error и Exception. Error используется для ошибок, которые не могут быть обработаны программой. Exception используется для ошибок, которые могут быть обработаны программой.

Чтобы обработать исключительную ситуацию, вы можете использовать оператор try-catch. Оператор try-catch позволяет вам определить блок кода, который может вызвать исключительную ситуацию, и блок кода, который будет выполнен, если исключительная ситуация произойдет.

Оператор try-catch имеет следующий синтаксис:

try {
// code that may throw an exception
} catch (ExceptionType1 ex1) {
// code to handle ExceptionType1
} catch (ExceptionType2 ex2) {
// code to handle ExceptionType2
} catch (ExceptionType3 ex3) {
// code to handle ExceptionType3
} finally {
// code to be executed regardless of whether an exception occurs
}

Блок try содержит код, который может вызвать исключительную ситуацию. Блок catch содержит код, который будет выполнен, если исключительная ситуация произойдет. Блок catch должен указывать тип исключительной ситуации, которую он обрабатывает. Если исключительная ситуация произойдет, то будет выполнен только один блок catch, который соответствует типу исключительной ситуации. finally блок содержит код, который будет выполнен независимо от того, произойдет ли исключительная ситуация или нет. Блок finally не является обязательным.

Важно отметить, что вы должны обрабатывать только те исключения, которые могут произойти в блоке try, а не все исключения.

Чтобы обработать исключительную ситуацию, вы можете использовать оператор throw. Оператор throw позволяет вам создать исключительную ситуацию вручную. Оператор throw имеет следующий синтаксис:

throw new ExceptionType("message");

Чтобы обработать исключительную ситуацию, вы можете использовать оператор throws. Оператор throws позволяет вам указать, что метод может вызвать исключительную ситуацию. Оператор throws имеет следующий синтаксис:

public void method() throws ExceptionType1, ExceptionType2, ExceptionType3 {
// code that may throw an exception
}

Оператор throws указывает, что метод может вызвать исключительную ситуацию типа ExceptionType1, ExceptionType2 или ExceptionType3. Если метод вызовет исключительную ситуацию, то она будет передана вызывающему методу, который должен обработать ее.

Чтобы создать собственное исключение, вы можете создать класс, который наследуется от класса Exception. Класс Exception является базовым классом для всех исключений в Java. Класс Exception имеет следующий синтаксис:

public class ExceptionType extends Exception {
    // code
}

66. В каком случае программа должна использовать оператор throw?

В Java оператор throw необходимо использовать, чтобы выбросить исключение «вручную», чтобы сигнализировать об исключительной ситуации. Например, если метод должен вернуть значение, но в какой-то момент он не может вернуть значение, то он должен выбросить исключение, чтобы сигнализировать об этом.

Пример:

public int divide(int a, int b) {
    if (b == 0) {
        throw new ArithmeticException("Cannot divide by zero");
    }
    return a / b;
}

67. В Java все исключения делятся на два основных типа. Что это за типы и какие виды ошибок ни обрабатывают?

В Java исключения делятся на два основных типа: контролируемые и неконтролируемые:

  • Контролируемые исключения (checked exceptions) — это исключения, которые должны быть обработаны в коде программы. Они проверяются компилятором или объявлены программистом. Контролируемые исключения наследуются от класса Exception, который наследуется от класса Throwable. Контролируемые исключения обрабатываются с помощью оператора try-catch или с помощью оператора throws в сигнатуре метода.

Синтаксис оператора try-catch:

try {
    // code
} catch (ExceptionType e) {
    // code
}

Cинтаксис оператора throws:

public void method() throws ExceptionType {
    throw new ExceptionType();
}
  • Неконтролируемые исключения (unchecked exceptions) — это исключения, которые не проверяются на этапе компиляции и не требуют объявления программистом.

68. Код ниже вызовет ошибку: Exception <…> java. lang.ArrayIndexOutOfBoundsException: 4: Что она означает?

Сообщение об ошибке Exception <...> java.lang.ArrayIndexOutOfBoundsException: 4 означает, что в программе возникло ArrayIndexOutOfBoundsException.

ArrayIndexOutOfBoundsException — это неконтролируемое исключение, которое возникает, когда индекс массива находится вне его границ.

Сообщение об ошибке Exception <...> java.lang.ArrayIndexOutOfBoundsException: 4 указывает на то, что исключение было вызвано при попытке доступа к элементу с индексом 4 массива. Это означает, что в массиве менее 5 элементов или что вы пытаетесь получить доступ к элементу с индексом, который находится за пределами массива.

69. Контролируемые исключения (checked)

В Java контролируемые исключения — это исключения, которые проверяются компилятором и должны обрабатываться или объявляться программистом. Контролируемые исключения представляют собой ошибки, которые можно предвидеть и обработать в программе, например неверный ввод или отсутствующий ресурс.

Контролируемые исключения определяются как подклассы класса Exception или одного из его подклассов и проверяются компилятором Java. Это означает, что если метод генерирует контролируемое исключение, то caller должен либо обработать исключение, либо объявить его в сигнатуре throws метода.

Пример контролируемого исключения:

public void readFile(String fileName) throws IOException {
    if (!fileName.endsWith(".txt")) {
        throw new IOException("Неверное расширение файла");
    }
}
public void readFile(String fileName) {
    try {
        if (!fileName.endsWith(".txt")) {
            throw new IOException("Неверное расширение файла");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

70. Неконтролируемые исключения (unchecked) и ошибки, которые они обрабатывают

В Java неконтролируемые исключения — это исключения, которые не проверяются компилятором и не требуют обработки или объявления программистом. Неконтролируемые исключения представляют собой ошибки, которые не ожидаются и не могут быть предвидены, например ошибки программирования или ошибки времени выполнения.

Неконтролируемые исключения определяются как подклассы класса RuntimeException или одного из его подклассов и не проверяются компилятором Java. Это означает, что если метод выдает неконтролируемое исключение, то caller не должен обрабатывать исключение или объявлять его в сигнатуре throws метода.

Список распространенных неконтролируемых исключений:

  • NullPointerException представляет собой ошибку нулевого указателя, например нулевую ссылку или нулевой элемент массива.
  • ArrayIndexOutOfBoundsException представляет собой ошибку выхода индекса массива за пределы, например доступ к массиву с недопустимым индексом.
  • ArithmeticException представляет собой арифметическую ошибку, такую как деление на ноль или переполнение целого числа.

71. Как реализуется принципы ООП в Java при создании исключений?

При создании исключений в Java используются следующие принципы ООП:

  • Наследование. При создании исключений в Java используется наследование, чтобы создать иерархию исключений. Например, класс IOException является подклассом класса Exception, который в свою очередь является подклассом класса Throwable. Это позволяет создавать исключения, которые могут быть обработаны в блоке catch для класса Exception или Throwable.
  • Инкапсуляция. Исключения в Java инкапсулируют информацию об ошибке, которая может быть получена с помощью методов getMessage() и printStackTrace().
  • Абстракция. Исключения в Java абстрагируют ошибки, которые могут возникнуть во время выполнения программы.
  • Полиморфизм. Исключения в Java могут быть обработаны в блоке catch для класса Exception или Throwable, что позволяет обрабатывать исключения различных типов.

Чтобы создать пользовательское исключение в Java, необходимо создать подкласс класса Exception или RuntimeException. Например, чтобы создать исключение, которое может быть обработано в блоке catch для класса Exception, необходимо создать подкласс класса Exception. Например, чтобы создать исключение, которое не может быть обработано в блоке catch для класса Exception, необходимо создать подкласс класса RuntimeException.

Пример создания пользовательского исключения в Java:

public class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
}

72. Какой оператор позволяет принудительно выбросить исключение?

Чтобы принудительно выбросить исключение в Java, необходимо использовать оператор throw. Например, чтобы выбросить исключение MyException, необходимо написать следующий код:

throw new MyException("MyException");

73. Порядок выполнения операторов при обработке блока блока try…catch

При обработке блока try...catch в Java операторы выполняются в следующем порядке:

  1. Выполняется блок try.
  2. Выполняется блок catch.
  3. Выполняется блок finally.

В блоке try содержится код, который может вызвать исключение. В блоке catch содержится код, который выполняется, если в блоке try возникло исключение. В блоке finally содержится код, который выполняется в любом случае, вне зависимости от того, возникло ли исключение в блоке try или нет.

Использование блока finally в Java не является обязательным. Если блок finally не используется, то после выполнения блока try или catch управление передаётся на следующую строку кода после блока try...catch.

Тема 6. Дженерики и использование контейнерных классов в Джава

74. Абстрактный тип данных Stack (cтек) в Java

Стек — это абстрактный тип данных, который представляет собой список элементов, организованных по принципу LIFO (last in, first out — последним пришёл, первым вышел). Стек можно реализовать с помощью массива или списка. В Java для реализации стека используется класс Stack. Для того, чтобы использовать класс Stack, необходимо импортировать пакет java.util:

import java.util.Stack;

public class StackDemo {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        stack.push("A"); // pushing element into the stack
        stack.push("B");
        stack.push("C");
        stack.push("D");
        stack.push("E");
        System.out.println(stack); // [A, B, C, D, E]
        System.out.println(stack.pop()); // E
        System.out.println(stack); // [A, B, C, D]
        System.out.println(stack.peek()); // D
        System.out.println(stack); // [A, B, C, D]
        System.out.println(stack.search("B")); // 2
        System.out.println(stack.search("Z")); // -1
    }
}

С помощью метода push() можно добавить элемент в стек. С помощью метода pop() можно извлечь элемент из стека. С помощью метода peek() можно получить элемент из вершины стека, не удаляя его из стека. С помощью метода search() можно получить позицию элемента в стеке. Если элемент не найден, то возвращается значение -1.

Стек наследует от класса Vector, поэтому в классе Stack доступны все методы класса Vector.

75. Универсальные типы или обобщенные типы данных, для чего создаются?

В Java универсальные типы или обобщенные типы данных создаются для того, чтобы создавать классы, интерфейсы или методы, которые могут работать с различными типами данных. Они используются для более гибкого и многократно используемого кода, а также для обеспечения безопасности типов.

Универсальные типы или обобщенные типы данных в Java определяются с помощью параметра типа, который указывается в объявлении класса, интерфейса или метода. Параметр типа указывается в угловых скобках <> после имени класса, интерфейса или метода. Параметр типа может быть любым допустимым идентификатором, но принято использовать одну букву из английского алфавита. Например, T, E, K, V и т.д.

Обозначения стандартных параметров типа:

  • T — type. Обозначает тип данных.
  • E — element. Обозначает элемент.
  • K — key. Обозначает ключ.
  • V — value. Обозначает значение.
  • N — number. Обозначает число.
  • S, U, V — second, third, fourth типы.

Синтаксис объявления универсального типа или обобщенного типа данных в Java:

public class ClassName<T, U> {
    private T t;
    private U u;
    public ClassName(T t, U u) {
        this.t = t;
        this.u = u;
    }
    public T getT() {
        return t;
    }
    public U getU() {
        return u;
    }
}

76. Объявление обобщённого класса коллекции с параметризованным методом для обработки массива элементов коллекции на основе цикла foreach (определение общего метода для отображения элементов массива)

Пример объявления обобщённого класса коллекции с параметризованным методом для обработки массива элементов коллекции на основе цикла foreach в Java:

import java.util.ArrayList;
import java.util.List;

public class GenericClass<T> {
    private List<T> list = new ArrayList<>();

    public void add(T t) {
        list.add(t);
    }

    public void print() {
        for (T t : list) {
            System.out.println(t);
        }
    }
}

Пример использования обобщённого класса коллекции с параметризованным методом для обработки массива элементов коллекции на основе цикла foreach в Java:

public class Main {
    public static void main(String[] args) {
        GenericClass<String> genericClass = new GenericClass<>();
        genericClass.add("Hello");
        genericClass.add("World");
        genericClass.print();
    }
}

77. Что представляет из себя класс ArrayList и в каком случае используется

Класс ArrayList — это класс из Java collections framework, который реализует интерфейс List. Это реализация интерфейса List на основе массива, что означает, что он хранит элементы в массиве и обеспечивает динамическое изменение размера, позволяющее увеличивать или уменьшать список по мере необходимости.

Класс ArrayList используется для создания динамического массива объектов, который может увеличиваться или уменьшаться по мере необходимости. Он предоставляет методы для добавления, удаления и доступа к элементам списка, а также методы для поиска, сортировки и повторения элементов списка.

Пример объявления класса ArrayList в Java:

import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("Hello"); // add element
        arrayList.add("World"); 
        System.out.println(arrayList); // [Hello, World]
        System.out.println(arrayList.get(0)); // Hello
        arrayList.remove(0); // remove element
        System.out.println(arrayList); // [World]
    }
}

ArrayList полезен, когда вы заранее не знаете, сколько элементов будет в списке. Если вы знаете, сколько элементов будет в списке, то лучше использовать массив, так как он быстрее и эффективнее.

Удаление элемента из ArrayList происходит за константное время, т.е. время не зависит от размера списка. Добавление элемента в ArrayList происходит за константное время, если размер списка не превышает емкость. Если размер списка превышает емкость, то время добавления элемента будет линейно зависеть от размера списка.

78. Класс Pattern и его использование

Класс Pattern — это класс стандартной библиотеки Java. Он представляет собой компилированный шаблон регулярного выражения. Этот класс используется для создания объекта Matcher, который выполняет сопоставление с шаблоном регулярного выражения.

Пример использования класса Pattern в Java:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("Java");
        Matcher matcher = pattern.matcher("Java is a programming language");
        while (matcher.find()) {
            System.out.println(matcher.start() + " " + matcher.group()); // 0 Java
        }
    }
}

В данном примере используется метод find(), который возвращает true, если в строке найдено совпадение с шаблоном регулярного выражения. Метод start() возвращает индекс начала совпадения, а метод group() возвращает совпадение.

Регулярные выражения используются при работе с текстом. Они позволяют искать и заменять текст в строке.

79. Класс Math и его использование

Класс Math — это класс стандартной библиотеки Java. Он предоставляет методы для выполнения основных математических операций, таких как возведение в степень, вычисление квадратного корня, тригонометрические функции и т.д.

Пример использования класса Math в Java:

import java.lang.Math;
public class Main {
    public static void main(String[] args) {
        System.out.println(Math.abs(-10)); // 10
        System.out.println(Math.pow(2, 3)); // 8.0
        System.out.println(Math.sqrt(9)); // 3.0
        System.out.println(Math.sin(Math.PI / 2)); // 1.0
    }
}

Класс Math содержит множество статических методов, которые можно использовать для выполнения различных математических операций. Например, метод abs() возвращает абсолютное значение числа, метод pow() возвращает значение числа, возведенного в степень, метод sqrt() возвращает квадратный корень числа, а метод sin() возвращает синус угла в радианах.

Конструктор класса Math объявлен как приватный, поэтому его нельзя использовать для создания объектов. Все методы класса Math также объявлены как статические, поэтому они могут быть вызваны без создания объекта класса Math.

80. Как вызываются методы класса Math и что при этом происходит?

Из-за того, что конструктор класса Math объявлен как приватный,создать объект класса Math нельзя. Однако все методы и поля класса Math объявлены как статические, поэтому они могут быть вызваны без создания объекта класса Math.

Тема 7. Java Core. Дженерики (продолжение) и использование контейнерных классов Java Framework Collection

81. Структура коллекций в Java Collection Framework. Иерархия интефейсов

Java Framework Collection — это набор классов, которые предоставляют различные алгоритмы для работы с коллекциями. Классы Java Framework Collection расположены в пакете java.util.

Структура коллекций в Java Framework Collection представлена на рисунке ниже.

JFC

Интерфейс Collection является базовым интерфейсом для всех коллекций. Он определяет основные методы для работы с коллекциями, такие как методы add(), remove(), size(), isEmpty(), contains(), toArray(), iterator() и т.д.

Приведем примеры самых частоиспользуемых реализаций.

Интерфейс List расширяет интерфейс Collection и определяет методы для работы с элементами коллекции по индексу. Интерфейс List реализуют классы ArrayList, LinkedList, Vector и Stack. Данный интерфейс используется для хранения элементов в виде списка. Stack — это класс, который наследуется от класса Vector и реализует интерфейс List. Stack используется для хранения элементов в виде стека.

Интерфейс Set расширяет интерфейс Collection и определяет методы для работы с множествами. Интерфейс Set реализуют классы HashSet, LinkedHashSet и TreeSet. Данный интерфейс используется для хранения элементов в виде множества. Интерфейс SortedSet расширяет интерфейс Set и определяет методы для работы с отсортированными множествами. Интерфейс SortedSet реализуют интерфейс NavigableSet. Интерфейс NavigableSet расширяет интерфейс SortedSet и определяет методы для работы с навигацией по множеству. Интерфейс NavigableSet реализуют классы TreeSet и ConcurrentSkipListSet. Данный интерфейс используется для хранения элементов в виде отсортированного множества.

Интерфейс Queue расширяет интерфейс Collection и определяет методы для работы с очередями. Интерфейс Queue реализуют классы LinkedList, PriorityQueue и ArrayDeque. Данный интерфейс используется для хранения элементов в виде очереди.

Интерфейс Deque расширяет интерфейс Queue и определяет методы для работы с двусторонними очередями. Интерфейс Deque реализуют классы LinkedList и ArrayDeque. Данный интерфейс используется для хранения элементов в виде двусторонней очереди.

Интерфейс Map определяет методы для работы с отображениями. Интерфейс Map реализуют классы HashMap, LinkedHashMap и TreeMap. Данный интерфейс используется для хранения элементов в виде отображения. Следует отметить, что интерфейс Map не расширяет интерфейс Collection. Интерфейс SortedMap расширяет интерфейс Map и определяет методы для работы с отсортированными отображениями. Интерфейс SortedMap реализует интерфейс NavigableMap. Интерфейс NavigableMap реализуют классы TreeMap и ConcurrentSkipListMap. Данный интерфейс используется для хранения элементов в виде отсортированного отображения.

82. Коллекция HashMap, создание и методы работы с ней

Коллекция HashMap — это класс в пакете java.util, который реализует интерфейс Map. Коллекция HashMap хранит элементы в виде пар ключ-значение. Ключи должны быть уникальными, а значения могут повторяться. Коллекция HashMap не гарантирует порядок хранения элементов. Коллекция HashMap не является синхронизированной, поэтому не является потокобезопасной. Коллекция HashMap позволяет хранить null в качестве ключа и значения.

Пример создания коллекции HashMap:

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
    }
}

Основные методы работы с HashMap:

  • put(K key, V value) — добавляет элемент в коллекцию. Возвращает null, если элемент добавлен, иначе возвращает предыдущее значение элемента с таким ключом.
  • get(Object key) — возвращает значение элемента с указанным ключом.
  • remove(Object key) — удаляет элемент с указанным ключом. Возвращает null, если элемент не найден.
  • containsKey(Object key) — проверяет, содержит ли коллекция элемент с указанным ключом.
  • containsValue(Object value) — проверяет, содержит ли коллекция элемент с указанным значением.
  • size() — возвращает количество элементов в коллекции.
  • isEmpty() — проверяет, пуста ли коллекция.
  • clear() — удаляет все элементы из коллекции.

83. Чем является класс LinkedList<Е>

Класс LinkedList<Е> является классом коллекции, который реализует интерфейс List<Е> и представляет собой связный список. Связный список — это структура данных, которая состоит из узлов, каждый из которых содержит ссылку на следующий узел. Первый узел называется головой, а последний — хвостом. Каждый узел содержит ссылку на предыдущий узел. Связный список позволяет быстро добавлять и удалять элементы в любом месте списка.

Класс LinkedList<Е> является универсальным, поэтому в качестве параметра типа можно указать любой ссылочный тип.

Пример использования класса LinkedList<Е>:

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
    }
}

Основные методы класса LinkedList<Е>:

  • add(E element) — добавляет элемент в конец списка.
  • addFirst(E element) — добавляет элемент в начало списка.
  • addLast(E element) — добавляет элемент в конец списка.
  • removeFirst() — удаляет первый элемент из списка.
  • removeLast() — удаляет последний элемент из списка.
  • remove(int index) — удаляет элемент по индексу.
  • get(int index) — возвращает элемент по индексу.
  • set(int index, E element) — заменяет элемент по индексу.
  • size() — возвращает размер списка.

84. Одним из ключевых методов интерфейса Collection является метод Iterator<Е> iterator(). Что возвращает это метод?

Метод iterator() возвращает объект типа Iterator<Е>, который позволяет перебирать элементы коллекции. Для перебора элементов коллекции используется следующий цикл:

import java.util.ArrayList;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Kotlin");
        list.add("C++");
        list.add("C#");
        list.add("Python");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

85. Что возвращает метод next()

Метод next() возвращает следующий элемент коллекции. Если элементов больше нет, то метод next() выбрасывает исключение NoSuchElementException. Поэтому перед вызовом метода next() следует проверить, есть ли еще элементы в коллекции, вызвав метод hasNext().

86. Что возвращает метод hasNext()

Метод hasNext() возвращает true, если в коллекции есть еще элементы, и false в противном случае.

Данный метод может быть использован, например, для проверки, есть ли еще элементы в коллекции, перед вызовом метода next().

87. Обобщенный класс HashSet класс коллекция, наследует свой функционал от класса AbstractSet, а также реализует интерфейс Set. Что он себя представляет?

Класс HashSet представляет собой коллекцию, которая хранит элементы в виде хэш-таблицы. Элементы в коллекции не упорядочены. Класс HashSet не позволяет хранить дубликаты элементов. Класс HashSet не является потокобезопасным, поэтому при работе с ним необходимо использовать синхронизацию.

Коасс HashSet реализует интерфейс Set, который является наследником интерфейса Collection. Поэтому класс HashSet наследует все методы, которые определены в интерфейсе Collection.

Пример использования класса HashSet:

import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
    }
}

Основные методы класса HashSet:

  • add(E e) — добавляет элемент в коллекцию
  • clear() — удаляет все элементы из коллекции
  • contains(Object o) — возвращает true, если коллекция содержит элемент o
  • isEmpty() — возвращает true, если коллекция пуста
  • remove(Object o) — удаляет элемент o из коллекции
  • size() — возвращает количество элементов в коллекции

88. Обобщенный класс HashMap класс коллекция, которая реализует интерфейс Map для хранения пар ключ-значение. Что он себя представляет?

Класс HashMap представляет собой коллекцию, которая хранит элементы в виде хэш-таблицы.

Класс HashMap реализует интерфейс Map, который является наследником интерфейса Collection. Поэтому класс HashMap наследует все методы, которые определены в интерфейсе Collection.

Пример использования класса HashMap:

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
    }
}

Основные методы работы с HashMap:

  • put(K key, V value) — добавляет элемент в коллекцию. Возвращает null, если элемент добавлен, иначе возвращает предыдущее значение элемента с таким ключом.
  • get(Object key) — возвращает значение элемента с указанным ключом.
  • remove(Object key) — удаляет элемент с указанным ключом. Возвращает null, если элемент не найден.
  • containsKey(Object key) — проверяет, содержит ли коллекция элемент с указанным ключом.
  • containsValue(Object value) — проверяет, содержит ли коллекция элемент с указанным значением.
  • size() — возвращает количество элементов в коллекции.
  • isEmpty() — проверяет, пуста ли коллекция.
  • clear() — удаляет все элементы из коллекции.

Тема 8. Стандартные потоки ввода-вывода. Сериализация

89. Стандартные поток ввода-вывода, предоставляемые Java

Java предоставляет ряд стандартных классов ввода-вывода (I/O) в пакете java.io стандартной библиотеки Java, которые позволяют читать и записывать данные из различных источников и назначений.

Данные классы:

  • InputStream и OutputStream — это абстрактные классы, определяющие основные методы чтения и записи байтов данных. Они являются суперклассами всех других классов потоков ввода и вывода в стандартной библиотеке Java.
  • Reader и Writer — это абстрактные классы, которые определяют основные методы чтения и записи символов данных. Они являются надклассами всех других классов символьных потоков ввода и вывода в стандартной библиотеке Java.
  • FileInputStream и FileOutputStream — это классы, которые позволяют читать и записывать данные в файлы.
  • ByteArrayInputStream и ByteArrayOutputStream — это классы, которые позволяют читать и записывать данные в массив байтов.
  • BufferedInputStream и BufferedOutputStream — это классы, которые позволяют буферизировать данные, прежде чем они будут записаны в файл или прочитаны из файла.
  • StringReader и StringWriter — это классы, которые позволяют читать и записывать данные в строку.
  • DataInputStream и DataOutputStream — это классы, которые позволяют читать и записывать данные в бинарном формате.

Пример использования потоков:

import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (FileInputStream inputStream = new FileInputStream("file.txt")) { // Open the file
        int data;
        while ((data = inputStream.read()) != -1) {
            System.out.print((char) data);
        }
        } catch (IOException e) {
            e.printStackTrace();
        }
        }
}

90. Понятие сериализации, интерфейс Serializable

Сериализация — это процесс преобразования объекта в последовательность байтов, которая может быть записана в файл или передана по сети.

В Java сериализация объектов реализуется с помощью интерфейса Serializable. Этот интерфейс не содержит никаких методов, он просто помечает классы, которые могут быть сериализованы.

91. Какие объекты можно сериализовать?

В Java можно сериализовать только объекты, которые реализуют интерфейс Serializable. Если класс не реализует этот интерфейс, то при попытке сериализовать объект этого класса будет выброшено исключение NotSerializableException.

Все поля класса должны быть сериализуемыми. Если поле не сериализуемое, то при сериализации объекта будет выброшено исключение NotSerializableException.

92. Какие методы определяет интерфейс Serializable?

Интерфейс Serializable не содержит никаких методов, он просто помечает классы, которые могут быть сериализованы.

93. Что означает понятие десериализация?

Десериализация — это процесс преобразования последовательности байтов в объект. Это обратный процесс сериализации.

В Java десериализация объектов реализуется с классом ObjectInputStream.

Пример десериализации объекта:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Main {
    public static void main(String[] args) {
        try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("file.txt"))) {
            Person person = (Person) objectInputStream.readObject();
            System.out.println(person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Для десериализации объекта необходимо, чтобы класс, объект которого был сериализован, был доступен во время десериализации. Если класс не будет найден, то будет выброшено исключение ClassNotFoundException.

94. Класс File, определенный в пакете java.io, не работает напрямую с потоками. В чем состоит его задача?

Класс File определяет объекты, которые представляют собой файлы и каталоги. Класс File не работает напрямую с потоками, он используется для создания объектов, которые могут быть использованы для создания потоков ввода-вывода.

Основные методы класса File:

  • createNewFile() — создает новый файл, если файл с таким именем уже существует, то метод вернет false;
  • delete() — удаляет файл;
  • exists() — проверяет существует ли файл;
  • isDirectory() — проверяет является ли файл каталогом;
  • isFile() — проверяет является ли файл обычным файлом;
  • isHidden() — проверяет является ли файл скрытым;
  • length() — возвращает размер файла в байтах;
  • list() — возвращает массив строк, содержащий имена файлов и каталогов, которые находятся в каталоге;
  • mkdir() — создает каталог;
  • renameTo(File dest) — переименовывает файл.
  • toPath() — возвращает объект типа Path, который представляет собой путь к файлу.

95. При работе с объектом класса FileOutputStream происходит вызов метода FileOutputStream.write(), что в результате этого происходит

При вызове метода FileOutputStream.write() происходит запись данных в файл. Если файл не существует, то он будет создан. Если файл существует, то его содержимое будет перезаписано.

Исключение в Java представляет проблему, которая возникает в ходе выполнения программы. В случае возникновения в Java исключения (exception), или исключительного события, имеет место прекращение нормального течения программы, и программа/приложение завершаются в аварийном режиме, что не является рекомендованным, и, как следствие, подобные случаи требуют в Java обработку исключений.

Причины возникновения исключения

Существует множество причин, которые могут повлечь за собой возникновение исключения. Далее рассмотрен ряд подобных сценариев, в контексте которых может произойти исключение:

  • Пользователь ввел недопустимые данные.
  • Файл, который необходимо открыть, не найден.
  • Соединение с сетью потеряно в процессе передачи данных либо JVM исчерпала имеющийся объем памяти.

Некоторые из данных исключений вызваны пользовательской ошибкой, другие – программной ошибкой, в некоторых случаях, причиной тому может послужить сбой в материальных ресурсах.

Исходя из приведенных сведений, мы можем обозначить три типа исключений. Знание данных типов позволит вам в дальнейшем разрешать проблемные ситуации, связанные с исключениями. Ниже рассмотрим список исключений в Java с примерами.

  • Контролируемые исключения – контролируемое исключение представляет собой вид исключения, которое происходит на стадии компиляции, их также именуют исключениями периода компиляции. Обозначенные исключения не следует игнорировать в ходе компиляции, они требуют должного обращения (разрешения) со стороны программиста.

К примеру, если вы используете класс FileReader в вашей программе для считывания данных из файла, в случае, если указанный в конструкторе файл не существует, происходит FileNotFoundException, и компилятор подсказывает программисту обработку данного исключения.

Пример 1

import java.io.File;
import java.io.FileReader;

public class Test {

   public static void main(String args[]) {		
      File f = new File("D://java/file.txt");
      FileReader fr = new FileReader(f); 
   }
}

При попытке компиляции обозначенной выше программы будут выведены следующие исключения:

C:>javac Test.java
Test.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(f);
                      ^
1 error

Примечание. В виду того, что методы read() и close() класса FileReader вызывают IOException, компилятор может уведомить вас об обработке IOException, совместно с FileNotFoundException.

  • Неконтролируемые исключения – неконтролируемое исключение представляет собой исключение, которое происходит во время выполнения. Они также носят название исключения на этапе выполнения. Данная категория может включать погрешности программирования, такие как логические ошибки либо неверный способ использования API. Исключения на этапе выполнения игнорируются в ходе компиляции.

К примеру, если вами в вашей программе был объявлен массив из 5 элементов, попытка вызова 6-го элемента массива повлечет за собой возникновение ArrayIndexOutOfBoundsExceptionexception.

Пример 2

public class Test {
   
   public static void main(String args[]) {
      int array[] = {1, 2, 3};
      System.out.println(array[4]);
   }
}

При компиляции и выполнении обозначенной выше программы будет получено следующее исключение:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Test.main(Test.java:8)

  • Ошибки – не являются исключениями, однако представляют проблемы, которые возникают независимо от пользователя либо программиста. Ошибки в вашем коде обычно игнорируются в виду того, что в редких случаях их обработка окажется результативной. К примеру, ошибка возникнет при переполнении стека. На этапе компиляции они также игнорируются.

Иерархия исключений

Все классы исключений в Java представляют подтипы класса java.lang.Exception. Класс исключений является подклассом класса Throwable. Помимо класса исключений существует также подкласс ошибок, образовавшихся из класса Throwable.

Ошибки представляют аварийное состояние вследствие значительных сбоев, которые не обрабатываются программами Java. Генерирование ошибок предназначено для отображения ошибок, выявленных средой выполнения. Примеры: JVM исчерпал имеющийся объем памяти. Обычно, программы не могут восстановить неполадки, вызванные ошибками.

Класс исключений делится на два основных подкласса: класс IOException и класс RuntimeException.

Иерархия исключений, исключения в Java, Java, RuntimeException Class

По ссылке представлен перечень наиболее распространенных контролируемых (checked) и неконтролируемых (unchecked) встроенных исключений в Java.

Методы исключений

Далее представлен список важных методов, доступных в классе Throwable.

Метод и описание
1 public String getMessage()
Возврат подробного сообщения о произошедшем исключении. Инициализация данного сообщения производится в конструкторе Throwable.
2 public Throwable getCause()
Возврат причины исключения, представленной объектом Throwable.
3 public String toString()
Возврат имени класса, соединенного с результатом getMessage().
4 public void printStackTrace()
Выведение результата toString() совместно с трассировкой стека в System.err, поток вывода ошибок.
5 public StackTraceElement [] getStackTrace()
Возврат массива, содержащего каждый элемент в трассировке стека. Элемент с номером 0 представляет вершину стека вызовов, последний элемент массива отображает метод на дне стека вызовов.
6 public Throwable fillInStackTrace()
Заполняет трассировку стека данного объекта Throwable текущей трассировкой стека, дополняя какую-либо предшествующую информацию в трассировке стека.

Обработка исключений – try и catch

Метод производит обработку исключения при использовании ключевых слов try и catch.

Описание

Блок try/catch размещается в начале и конце кода, который может сгенерировать исключение. Код в составе блока try/catch является защищенным кодом, синтаксис использования try/catch выглядит следующим образом:

try {
   // Защищенный код
}catch(НазваниеИсключения e1) {
   // Блок catch
}

Код, предрасположенный к исключениям, размещается в блоке try. В случае возникновения исключения, обработка данного исключения будет производиться соответствующим блоком catch. За каждым блоком try должен немедленно следовать блок catch либо блок finally.

Оператор catch включает объявление типа исключения, которое предстоит обработать. При возникновении исключения в защищенном коде, блок catch (либо блоки), следующий за try, будет проверен. В случае, если тип произошедшего исключения представлен в блоке catch, исключение передается в блок catch аналогично тому, как аргумент передается в параметр метода.

Пример

Ниже представлен массив с заявленными двумя элементами. Попытка кода получить доступ к третьему элементу массива повлечет за собой генерацию исключения.

import java.io.*;

public class Test {

   public static void main(String args[]) {
      try {
         int array[] = new int[2];
         System.out.println("Доступ к третьему элементу:" + array[3]);
      }catch(ArrayIndexOutOfBoundsException e) {
         System.out.println("Исключение:" + e);
      }
      System.out.println("Вне блока");
   }
}

Вследствие этого будет получен следующий результат:

Исключение:java.lang.ArrayIndexOutOfBoundsException: 3
Вне блока

Многократные блоки catch

За блоком try могут следовать несколько блоков catch. Синтаксис многократных блоков catch выглядит следующим образом:

try {
   // Защищенный код
}catch(ИсключениеТип1 e1) {
   // Блок catch
}catch(ИсключениеТип2 e2) {
   // Блок catch
}catch(ИсключениеТип3 e3) {
   // Блок catch
}

Представленные выше операторы демонстрируют три блока catch, однако, после однократного try количество данных используемых блоков может быть произвольным. В случае возникновения исключения в защищенном коде, исключение выводится в первый блок catch в списке. Если тип данных генерируемого исключения совпадает с ИсключениеТип1, он перехватывается в указанной области. В обратном случае, исключение переходит ко второму оператору catch. Это продолжается до тех пор, пока не будет произведен перехват исключения, либо оно не пройдет через все операторы, в случае чего выполнение текущего метода будет прекращено, и исключение будет перенесено к предшествующему методу в стеке вызовов.

Пример

Далее представлен сегмент кода, демонстрирующий использование многократных операторов try/catch.

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
}catch(IOException e1) {
   e1.printStackTrace();
   return -1;
}catch(FileNotFoundException e2) // Недействительно! {
   e2.printStackTrace();
   return -1;
}

Перехват многотипных исключений

В среде Java 7, Вы можете произвести обработку более чем одного исключения при использовании одного блока catch, данное свойство упрощает код. Ниже представлена модель реализации:

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

Ключевые слова throws/throw

В случае если метод не может осуществить обработку контролируемого исключения, производится соответствующее уведомление при использовании ключевого слова throws в Java. Ключевое слово throws появляется в конце сигнатуры метода.

При использовании ключевого слова throw вы можете произвести обработку вновь выявленного исключения либо исключения, которое было только что перехвачено.

Следует внимательно различать ключевые слова throw и throws в Java, так как throws используется для отложенной обработки контролируемого исключения, а throw, в свою очередь, используется для вызова заданного исключения.

Представленный ниже метод отображает, что им генерируется RemoteException:

Пример 1

import java.rmi.RemoteException;
public class Test {

   public void deposit(double amount) throws RemoteException {
      // Реализация метода
      throw new RemoteException();
   }
   // Остаток определения класса
}

Метод также может объявить о том, что им генерируется более чем одно исключение, в случае чего исключения представляются в виде перечня, отделенные друг от друга запятыми. К примеру, следующий метод оповещает о том, что им генерируются RemoteException и InsufficientFundsException:

Пример 2

import java.rmi.RemoteException;
public class Test {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Реализация метода
   }
   // Остаток определения класса
}

Блок finally

В Java finally следует за блоком try либо блоком catch. Блок finally в коде выполняется всегда независимо от наличия исключения.

Использование блока finally позволяет запустить какой-либо оператор, предназначенный для очистки, не зависимо от того, что происходит в защищенном коде.

Блок finally в Java появляется по окончании блоков catch, его синтаксис выглядит следующим образом:

Синтаксис

try {
   // Защищенный код
}catch(ИсключениеТип1 e1) {
   // Блок catch
}catch(ИсключениеТип2 e2) {
   // Блок catch
}catch(ИсключениеТип3 e3) {
   // Блок catch
}finally {
   // Блок finally всегда выполняется.
}

Пример

public class Test {

   public static void main(String args[]) {
      int array[] = new int[2];
      try {
         System.out.println("Доступ к третьему элементу:" + array[3]);
      }catch(ArrayIndexOutOfBoundsException e) {
         System.out.println("Исключение:" + e);
      }finally {
         array[0] = 6;
         System.out.println("Значение первого элемента: " + array[0]);
         System.out.println("Оператор finally выполнен.");
      }
   }
}

Вследствие этого будет получен следующий результат:

Исключение:java.lang.ArrayIndexOutOfBoundsException: 3
Значение первого элемента: 6
Оператор finally выполнен.

Следует помнить, что:

  • Выражение catch не может существовать без оператора try.
  • При наличии блока try/catch, выражение finally не является обязательным.
  • Блок try не может существовать при отсутствии выражения catch либо выражения finally.
  • Существование какого-либо кода в промежутке между блоками try, catch, finally является невозможным.

Конструкция try-with-resources

В норме, при использовании различных видов ресурсов, таких как потоки, соединения и др., нам предстоит закрыть их непосредственно при использовании блока finally. В программе, представленной ниже, нами производится считывание данных из файла при использовании FileReader, после чего он закрывается блоком finally.

Пример 1

import java.io.FileReader;
import java.io.File;
import java.io.IOException;

public class Test {

   public static void main(String args[]) {
      FileReader fr = null;		
      try {
         File f = new File("file.txt");
         fr = new FileReader(f); 
         char [] array = new char[10];
         fr.read(array);   // чтение содержимого массива
         for(char c : array)
         System.out.print(c);   // вывод символов на экран, один за одним
      }catch(IOException e1) {
         e1.printStackTrace();
      }finally {
         try {
            fr.close();
         }catch(IOException e2) {		
            e2.printStackTrace();
         }
      }
   }
}

Конструкция try-with-resources, также именуемая как автоматическое управление ресурсами, представляет новый механизм обработки исключений, который был представлен в 7-ой версии Java, осуществляя автоматическое закрытие всех ресурсов, используемых в рамках блока try catch.

Чтобы воспользоваться данным оператором, вам всего лишь нужно разместить заданные ресурсы в круглых скобках, после чего созданный ресурс будет автоматически закрыт по окончании блока. Ниже представлен синтаксис конструкции try-with-resources.

Синтаксис

try(FileReader fr = new FileReader("Путь к файлу")) {
   // использование ресурса
   }catch() {
      // тело catch 
   }
}

Программа ниже производит считывание данных в файле, используя конструкцию try-with-resources.

Пример 2

import java.io.FileReader;
import java.io.IOException;

public class Test {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://Soft/NetBeans 8.2/Projects/test/test/file.txt")) {
         char [] array = new char[10];
         fr.read(array);   // чтение содержимого массива
         for(char c : array)
         System.out.print(c);   // вывод символов на экран, один за одним
      }catch(IOException e) {
         e.printStackTrace();
      }
   }
}

При работе с конструкцией try-with-resources следует принимать во внимание следующие нюансы:

  • С целью использования конструкции try-with-resources следует реализовать интерфейс AutoCloseable, после чего соответствующий метод close() будет вызван автоматически во время выполнения.
  • В конструкции try-with-resources возможно указание одного и более классов.
  • При указании нескольких классов в блоке try конструкции try-with-resources, закрытие данных классов будет производиться в обратном порядке.
  • За исключением внесения ресурсов в скобки, все элементы являются равными аналогично нормальному блоку try/catch в составе блока try.
  • Ресурсы, внесенные в try, конкретизируются до запуска блока try.
  • Ресурсы непосредственно в составе блока try указываются как окончательные.

Создание своих собственных исключений

Вы можете создать свои собственные исключения в среде Java. При записи собственных классов исключений следует принимать во внимание следующие аспекты:

  • Все исключения должны быть дочерними элементами Throwable.
  • Если вы планируете произвести запись контролируемого исключения с автоматическим использованием за счет правила обработки или объявления, вам следует расширить класс Exception.
  • Если вы хотите произвести запись исключения на этапе выполнения, вам следует расширить класс RuntimeException.

Вы можете определить собственный класс исключений, как показано ниже:

class MyException extends Exception {
}

Вам лишь необходимо расширить предопределенный класс Exception с целью создания собственного исключения. Данная категория относится к контролируемым исключениям. Следующий класс InsufficientFundsException исключительных ситуаций, определяемых пользователем, расширяет класс Exception, делая его контролируемым исключением. Класс исключений, подобно всем остальным классам, содержит используемые области и методы.

Пример

// Название файла InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

С целью демонстрации наших исключений, определяемых пользователем, следующий класс Checking содержит метод withdraw(), генерирующий InsufficientFundsException.

// Название файла Checking.java
import java.io.*;

public class Checking {
   private int number;
   private double balance;
   
   public Checking(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

Следующая программа Bank демонстрирует вызов методов deposit() и withdraw() класса Checking.

// Название файла Bank.java
public class Bank {

   public static void main(String [] args) {
      Checking c = new Checking(101);
      System.out.println("Депозит $300...");
      c.deposit(300.00);
      
      try {
         System.out.println("nСнятие $100...");
         c.withdraw(100.00);
         System.out.println("nСнятие $400...");
         c.withdraw(400.00);
      }catch(InsufficientFundsException e) {
         System.out.println("Извините, но у Вас $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

Скомпилируйте все три выше обозначенные файла и произведите запуск Bank. Вследствие этого будет получен следующий результат:

Депозит $300...

Снятие $100...

Снятие $400...
Извините, но у Вас $200.0
InsufficientFundsException
         at Checking.withdraw(Checking.java:25)
         at Bank.main(Bank.java:13)

Общие исключения

В Java можно выделить две категории исключений и ошибок.

  • Исключения JVM – данная группа представлена исключениями/ошибками, которые вызываются непосредственно и логически со стороны JVM. Примеры: NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException.
  • Программные исключения – данные исключения вызываются непосредственно приложением либо программистами API. Примеры: IllegalArgumentException, IllegalStateException.

Список вопросов и ответов по теме «Исключения в Java».

К списку вопросов по всем темам

Вопросы

1. Дайте определение понятию “исключение”
2. Какова иерархия исключений.
3. Можно/нужно ли обрабатывать ошибки jvm?
4. Какие существуют способы обработки исключений?
5. О чем говорит ключевое слово throws?
6. В чем особенность блока finally? Всегда ли он исполняется?
7. Может ли не быть ни одного блока catch при отлавливании исключений?
8. Могли бы вы придумать ситуацию, когда блок finally не будет выполнен?
9. Может ли один блок catch отлавливать несколько исключений (с одной и разных веток наследований)?
10. Что вы знаете об обрабатываемых и не обрабатываемых (checked/unchecked) исключениях?
11. В чем особенность RuntimeException?
12. Как написать собственное (“пользовательское”) исключение? Какими мотивами вы будете руководствоваться при выборе типа исключения: checked/unchecked?
13. Какой оператор позволяет принудительно выбросить исключение?
14. Есть ли дополнительные условия к методу, который потенциально может выбросить исключение?
15. Может ли метод main выбросить исключение во вне и если да, то где будет происходить обработка данного исключения?
16. Если оператор return содержится и в блоке catch и в finally, какой из них “главнее”?
17. Что вы знаете о OutOfMemoryError?
18. Что вы знаете о SQLException? К какому типу checked или unchecked оно относится, почему?
19. Что такое Error? В каком случае используется Error. Приведите пример Error’а.
20. Какая конструкция используется в Java для обработки исключений?
21. Предположим, есть блок try-finally. В блоке try возникло исключение и выполнение переместилось в блок finally. В блоке finally тоже возникло исключение. Какое из двух исключений “выпадет” из блока try-finally? Что случится со вторым исключением?
22. Предположим, есть метод, который может выбросить IOException и FileNotFoundException в какой последовательности должны идти блоки catch? Сколько блоков catch будет выполнено?

Ответы

1. Дайте определение понятию “исключение”

Исключение — это проблема(ошибка) возникающая во время выполнения программы. Исключения могут возникать во многих случаях, например:

  1. Пользователь ввел некорректные данные.
  2. Файл, к которому обращается программа, не найден.
  3. Сетевое соединение с сервером было утеряно во время передачи данных. И т.д.

Все исключения в Java являются объектами. Поэтому они могут порождаться не только автоматически при возникновении исключительной ситуации, но и создаваться самим разработчиком.

2. Какова иерархия исключений.

Иерархия Исключений Java

Исключения делятся на несколько классов, но все они имеют общего предка — класс Throwable. Его потомками являются подклассы Exception и Error.

Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы и предсказуемы. Например, произошло деление на ноль в целых числах.

Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует пытаться обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память, доступная виртуальной машине. Программа дополнительную память всё равно не сможет обеспечить для JVM.

В Java все исключения делятся на два типа: контролируемые исключения (checked) и неконтролируемые исключения (unchecked), к которым относятся ошибки (Errors) и исключения времени выполнения (RuntimeExceptions, потомок класса Exception).

Контролируемые исключения представляют собой ошибки, которые можно и нужно обрабатывать в программе, к этому типу относятся все потомки класса Exception (но не RuntimeException).

3. Можно/нужно ли обрабатывать ошибки jvm?

Обрабатывать можно, но делать этого не стоит. Разработчику не предоставлены инструменты для обработки ошибок системы и виртуальной машины.

4. Какие существуют способы обработки исключений?

В Java есть пять ключевых слов для работы с исключениями:

  1. try — данное ключевое слово используется для отметки начала блока кода, который потенциально может привести к ошибке.
  2. catch — ключевое слово для отметки начала блока кода, предназначенного для перехвата и обработки исключений.
  3. finally — ключевое слово для отметки начала блока кода, которой является дополнительным. Этот блок помещается после последнего блока ‘catch’. Управление обычно передаётся в блок ‘finally’ в любом случае.
  4. throw — служит для генерации исключений.
  5. throws — ключевое слово, которое прописывается в сигнатуре метода, и обозначающее что метод потенциально может выбросить исключение с указанным типом.

Общий вид конструкции для «поимки» исключительной ситуации выглядит следующим образом:

try{

//здесь код, который потенциально может привести к ошибке

}

catch(SomeException e ){ //в скобках указывается класс конкретной ожидаемой ошибки  

//здесь описываются действия, направленные на обработку исключений

}

finally{

//выполняется в любом случае ( блок finally  не обязателен)

}

Подробнее http://www.quizful.net/post/java-exceptions

5. О чем говорит ключевое слово throws?

throws — ключевое слово, которое прописывается в сигнатуре метода, и обозначающее что метод потенциально может выбросить исключение с указанным типом.

6. В чем особенность блока finally? Всегда ли он исполняется?

Когда исключение передано, выполнение метода направляется по нелинейному пути. Это может стать источником проблем. Например, при входе метод открывает файл и закрывает при выходе. Чтобы закрытие файла не было пропущено из-за обработки исключения, был предложен механизм finally.

Ключевое слово finally создаёт блок кода, который будет выполнен после завершения блока try/catch, но перед кодом, следующим за ним. Блок будет выполнен, независимо от того, передано исключение или нет. Оператор finally не обязателен, однако каждый оператор try требует наличия либо catch, либо finally. Код в блоке finally будет выполнен всегда.

7. Может ли не быть ни одного блока catch при отлавливании исключений?

Такая запись допустима, если имеется связка try{} finally {}. Но смысла в такой записи не так много, всё же лучше иметь блок catch в котором будет обрабатываться необходимое исключение.

String x = «z»;

try {

   x=«234»;

} finally {

    x = «Finally»;

}

8. Могли бы вы придумать ситуацию, когда блок finally не будет выполнен?

Блок finally выполняется не всегда, например в такой ситуации:

try {

    System.exit(0);

} catch(Exception e) {

    e.printStackTrace();

} finally { }

Здесь finally недостижим, так как происходит системный выход из программы. Общими словами: когда jvm умирает, ей не до finally (отсюда можете придумать другие примеры как убить jvm и ответить на вопрос в заголовке).

9. Может ли один блок catch отлавливать несколько исключений (с одной и разных веток наследований)?

В Java 7 стала доступна новая конструкция, с помощью которой можно перехватывать несколько исключений одним блоком catch:

try {  

...

} catch( IOException | SQLException ex ) {  

  logger.log(ex);

  throw ex;

}

10. Что вы знаете об обрабатываемых и не обрабатываемых (checked/unchecked) исключениях?

Все исключительные ситуации делятся на «проверяемые» (checked) и «непроверяемые» (unchecked) (смотрите картинку в начале статьи). Это свойство присуще «корневищу» (Throwable, Error, Exception, RuntimeException) и передается по наследству. Никак не видимо в исходном коде класса исключения.
В дальнейших примерах просто учтите, что— Throwable и Exception и все их наследники (за исключением наследников Error-а и RuntimeException-а) — checked
— Error и RuntimeException и все их наследники — unchecked
checked exception = проверяемое исключение, проверяемое компилятором.

Тема достаточно обширная для того, чтобы уместить ее в одном ответе. К примеру, можно разобрать примеры Головача: http://habrahabr.ru/company/golovachcourses/blog/225585/

И еще с quizful.net

1. Checked исключения, это те, которые должны обрабатываться блоком catch или описываться в сигнатуре метода. Unchecked могут не обрабатываться и не быть описанными.
2. Unchecked исключения в Java — наследованные от RuntimeException, checked — от Exception (не включая unchecked).

Checked исключения отличаются от Unchecked исключения в Java, тем что:
1)Наличиеобработка Checked исключения проверяются на этапе компиляции. Наличиеобработка Unchecked исключения происходит на этапе выполнения.

11. В чем особенность RuntimeException?

public class RuntimeException extends Exception — базовый класс для ошибок во время выполнения. Относится к необрабатываемым исключениям (uncatchedunchecked). Как сказано в описании класса — это суперкласс, исключения которого могут быть выброшены во время нормальной работы JVM.

12. Как написать собственное (“пользовательское”) исключение? Какими мотивами вы будете руководствоваться при выборе типа исключения: checked/unchecked?

Необходимо унаследоваться от базового класса требуемого типа исключений (например от Exception или RuntimeException).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public class ExcClass extends Exception {

    private String someString;

    public ExcClass (String string) {

        this.someString = string;

        System.out.println(«Exception ExcClass»);

    }

    public void myOwnExceptionMsg() {

        System.err.println(«This is exception message for string: « + someString);

    }

}

public class TestExc {

    public static void main(String[] args) {

        try {

            String s = «SomeString»;

            throw new ExcClass(s);

        } catch (ExcClass ex) {

            ex.myOwnExceptionMsg();

        }

    }

}

//Вывод

Exception ExcClass

This is exception message for string: SomeString

Руководствоваться нужно определением типа исключения. В зависимости от того, что вы хотите обрабатывать или видеть нужно и наследоваться от нужного класса.

13. Какой оператор позволяет принудительно выбросить исключение?

throw new Exception();

14. Есть ли дополнительные условия к методу, который потенциально может выбросить исключение?

Если это проверяемое исключение, то оно должно быть задекларировано в сигнатуре метода.

public void someMethod() throws Exception {

    }

15. Может ли метод main выбросить исключение во вне и если да, то где будет происходить обработка данного исключения?

Может и оно будет передано в виртуальную машину Java (JVM).

16. Если оператор return содержится и в блоке catch и в finally, какой из них “главнее”?

Вернется из блока finally.

    public static void main(String[] args) {

       String what =  method();

        System.out.println(what);

    }

    public static String method() {

        try {

            return «SomeString»;

        } catch(Exception ex) {

            return «Catch message»;

        } finally {

            return «Finally message»;

        }

    }

//Вывод

Finally message

17. Что вы знаете о OutOfMemoryError?

OutOfMemoryError выбрасывается, когда виртуальная машина Java не может выделить (разместить) объект из-за нехватки памяти, а сборщик мусора не может высвободить ещё.

Область памяти, занимаемая java процессом, состоит из нескольких частей. Тип OutOfMemoryError зависит от того, в какой из них не хватило места.
Области памяти

1. java.lang.OutOfMemoryError: Java heap space
Не хватает места в куче, а именно, в области памяти в которую помещаются объекты, создаваемые программно в вашем приложении. Размер задается параметрами -Xms и -Xmx. Если вы пытаетесь создать объект, а места в куче не осталось, то получаете эту ошибку. Обычно проблема кроется в утечке памяти, коих бывает великое множество, и интернет просто пестрит статьями на эту тему.

2. java.lang.OutOfMemoryError: PermGen space
Данная ошибка возникает при нехватке места в Permanent области, размер которой задается параметрами -XX:PermSize и -XX:MaxPermSize.

3. java.lang.OutOfMemoryError: GC overhead limit exceeded
Данная ошибка может возникнуть как при переполнении первой, так и второй областей. Связана она с тем, что памяти осталось мало и GC постоянно работает, пытаясь высвободить немного места. Данную ошибку можно отключить с помощью параметра -XX:-UseGCOverheadLimit, но, конечно же, её надо не отключать, а либо решать проблему утечки памяти, либо выделять больше объема, либо менять настройки GC.

4. java.lang.OutOfMemoryError: unable to create new native thread

Выбрасывается, когда нет возможности создать еще потоки.

Подробнее в статье http://habrahabr.ru/post/117274/

18. Что вы знаете о SQLException? К какому типу checked или unchecked оно относится, почему?

SQLException предоставляет информацию об ошибках доступа к базе данных или других ошибках связанных с работой с базами данных.

SQLException относится к checked исключениям, а значит проверяется на этапе компиляции.

Споры об этом типе исключения идут о том, что разработчику приходится постоянно обрабатывать это исключение в коде, хотя большая часть ошибок возникает во время выполнения программы, т.е., по мнению многих, лучше бы отнести его к unchecked runtime исключениям.

try {

    // make some SQL call(s)

} catch {SQLException e) {

    // log the exception

    return; // and give up

}

Аргумент Joshua Bloch из Effective Java Second Edition такой: сделав SQLException проверяемым — это попытка заставить разработчиков обработать исключение и обернуть его в новом уровне абстракции.

19. Что такое Error? В каком случае используется Error. Приведите пример Error’а.

Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует пытаться обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память, доступная виртуальной машине.

За примером посмотрите картинку иерархии исключений в начале статьи. Как пример — OutOfMemoryError.

20. Какая конструкция используется в Java для обработки исключений?

Можно использовать try-catch-finally и c 7й Java try-with-resources. Первый способ:

try{

//здесь код, который потенциально может привести к ошибке

}

catch(SomeException e ){ //в скобках указывается класс конкретной ожидаемой ошибки  

//здесь описываются действия, направленные на обработку исключений

}

finally{

//выполняется в любом случае ( блок finally  не обязателен)

}

Try с ресурсами:

try(открываем файл и т.п. здесь){

//…

}

//после блока файл закроется автоматически.

Пример:

Старый способ

BufferedReader br = new BufferedReader(new FileReader(path));

   try {

            return br.readLine();

        } finally {

            if (br != null) {

                br.close();

            }

        }

JDK 7

try (BufferedReader br =

                   new BufferedReader(new FileReader(path)) ) {

        return br.readLine();

    }

Так же смотрите ответ к «Какие существуют способы обработки исключений?»

21. Предположим, есть блок try-finally. В блоке try возникло исключение и выполнение переместилось в блок finally. В блоке finally тоже возникло исключение. Какое из двух исключений “выпадет” из блока try-finally? Что случится со вторым исключением?

Ответ аналогичный случаю с двумя return — будет обработано в finally блоке. Если было выброшено два исключения — одно в try, второе в finally, то исключение в finally «проглотит» исключение выше (см. пример). Если до блока finally исключение было обработано, то мы можем получить информацию об исключении в блоке try и тем самым не потерять исключение, которое впоследствии может быть перезаписано в finally другим исключением.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public class TestExc {

    public static void main(String[] args) {

        Exception ex = twoExceptionsMethod();

        System.out.println(ex.getClass());

        String s = twoExceptionsMethod2();

        System.out.println(s);

    }

    public static Exception twoExceptionsMethod() {

        try {

            return new IndexOutOfBoundsException();

        } finally {

            return new NullPointerException();

        }

    }

    public static String twoExceptionsMethod2() {

        try {

            throw new NullPointerException();

        }catch (NullPointerException ex) {

            System.out.println(ex.getMessage()+ » catchBlock»);;

        }

        finally {

            Exception ex2 = new Exception();

            return ex2.getMessage() + «finallyBlock»;

        }

    }

}

//Вывод

class java.lang.NullPointerException

null catchBlock

null finallyBlock

22. Предположим, есть метод, который может выбросить IOException и FileNotFoundException в какой последовательности должны идти блоки catch? Сколько блоков catch будет выполнено?

Общее правило — обрабатывать исключения нужно от «младшего» к старшему. Т.е. нельзя поставить в первый блок catch(Exception e) {}, иначе все дальнейшие блоки catch() уже ничего не смогут обработать, т.к. любое исключение будет попадать под ExceptionName extends Exception.

Таким образом сначала нужно обработать public class FileNotFoundException extends IOException, а затем уже IOException.

    public static void ioExcAndFileNotFoundEx() {

        try {

            //TODO: some code

            String x = «abc»;

            if (x.equals(«abc»)) {

                throw new IOException();

            } else {

                throw new FileNotFoundException();

            }

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.getMessage();

        }

    }

К списку вопросов по всем темам

Share Button

89

178462 Total Views 67 Views Today


Views:
148 104

Понравилась статья? Поделить с друзьями:
  • Некая ошибка в программе это
  • Нейтрализатор низкий эффект ошибка 420
  • Нейронные сети ошибка обратного распространения ошибки
  • Нейронная сеть с обратным распространением ошибки java
  • Нейронная сеть прямого распространения с обратным распространением ошибки