Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Раздел 9. Исключения, логирование, отладка

Глава 1. Иерархия исключений (Exceptions)

Исключения в лямбдах, Stream API, конструкторах и статических блоках

Java предоставляет единый механизм исключений на базе класса Throwable, но поведение этого механизма существенно различается в зависимости от контекста выполнения. Лямбда-выражения и Stream API, появившиеся в Java 8, наложили архитектурные ограничения на использование checked-исключений, требуя от разработчиков поиска обходных стратегий. Конструкторы и статические блоки инициализации представляют собой особые точки жизненного цикла класса, где исключения ведут себя нестандартно и требуют специфического подхода к управлению ресурсами и обработке ошибок.


Часть первая. Исключения в лямбдах и Stream API

Функциональные интерфейсы Java 8 — Function<T,R>, Consumer<T>, Supplier<T>, Predicate<T> и другие — объявляют свои абстрактные методы без throws. Это означает, что любой метод, выбрасывающий checked-исключение, не может быть использован напрямую как лямбда-выражение или method reference в контексте Stream API.

// Метод с checked-исключением
public String fetchUrl(String url) throws IOException {
return httpClient.fetch(url);
}

// Ошибка компиляции: unreported exception IOException
List<String> urls = List.of("http://api1.com", "http://api2.com");
List<String> results = urls.stream()
.map(this::fetchUrl) // Не компилируется
.collect(Collectors.toList());


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


Стратегия первая: обёртка в try-catch внутри лямбды

Наиболее прямолинейный подход — обернуть вызов метода с checked-исключением в try-catch блок внутри лямбды и транслировать checked в unchecked:
List<String> results = urls.stream()
.map(url -> {
try {
return fetchUrl(url);
} catch (IOException e) {
throw new RuntimeException("Failed to fetch: " + url, e);
}
})
.collect(Collectors.toList());


Этот подход работает, но имеет серьезные недостатки. Во-первых, он загромождает код шаблонной обработкой исключений, разрушая лаконичность функционального стиля. Во-вторых, выброс RuntimeException из лямбды в Stream API прерывает весь конвейер — невозможно обработать ошибку для одного элемента и продолжить обработку остальных. В-третьих, исключение теряет семантику checked, и вызывающий код может не осознавать возможность сбоя.

Для сценариев, где требуется игнорировать ошибки и продолжить обработку, можно возвращать значение по умолчанию:
// Парсинг чисел из списка строк с игнорированием ошибок
List<String> rawValues = List.of("42", "invalid", "17", "3.14", "100");

List<Integer> parsedNumbers = rawValues.stream()
.map(s -> {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
// Возвращаем null для невалидных значений
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());

// Результат: [42, 17, 100]


Здесь NumberFormatException — unchecked-исключение, но паттерн применим и к checked. Фильтрация Objects::nonNull удаляет null-значения, соответствующие ошибкам парсинга. Однако этот подход имеет побочный эффект: информация об ошибках теряется без логирования.

Улучшенная версия с логированием:
List<Integer> parsedNumbers = rawValues.stream()
.map(s -> {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
logger.warn("Failed to parse integer from '{}'", s);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());



#Java #для_новичков #beginner #exception
👍2
Стратегия вторая: кастомный функциональный интерфейс с throws

Для сохранения семантики checked-исключений и повторного использования обработки можно создать собственные функциональные интерфейсы, объявляющие throws:
@FunctionalInterface
public interface ThrowingFunction<T, R, E extends Exception> {
R apply(T t) throws E;
}

@FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {
void accept(T t) throws E;
}

@FunctionalInterface
public interface ThrowingSupplier<T, E extends Exception> {
T get() throws E;
}
Эти интерфейсы позволяют объявлять методы, принимающие функциональные объекты с checked-исключениями:
java
Copy
public <T, R, E extends Exception> List<R> mapWithException(
List<T> list,
ThrowingFunction<T, R, E> function) throws E {

List<R> result = new ArrayList<>();
for (T item : list) {
result.add(function.apply(item));
}
return result;
}

// Использование
List<String> urls = List.of("http://api1.com", "http://api2.com");
try {
List<String> contents = mapWithException(urls, this::fetchUrl);
} catch (IOException e) {
// Обработка ошибки
}


Однако эти интерфейсы несовместимы со стандартным Stream API. Для интеграции требуется обёртка, транслирующая кастомный интерфейс в стандартный:
public static <T, E extends Exception> Consumer<T> throwingConsumerWrapper(
ThrowingConsumer<T, E> throwingConsumer) {

return item -> {
try {
throwingConsumer.accept(item);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
}

// Использование
urls.forEach(throwingConsumerWrapper(url -> {
writeToFile(url); // Метод, объявляющий throws IOException
}));


Этот подход сохраняет чистоту вызова, но по-прежнему теряет checked-семантику на границе обёртки.


#Java #для_новичков #beginner #exception
👍2
Стратегия третья: sneaky throws

Sneaky throws — техника, позволяющая выбросить checked-исключение без объявления его в сигнатуре метода, используя особенности generics и стирания типов в Java:
@SuppressWarnings("unchecked")
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
throw (E) e;
}

// Использование в лямбде
List<String> results = urls.stream()
.map(url -> {
try {
return fetchUrl(url);
} catch (IOException e) {
sneakyThrow(e); // Компилятор не требует throws
return null; // Недостижимый код, необходим для компиляции
}
})
.collect(Collectors.toList());


Механизм работает благодаря стиранию типов: компилятор не может проверить, что E не является RuntimeException, поэтому разрешает выброс без throws. Однако это крайне опасная практика. Вызывающий код не знает о возможности checked-исключения и не обрабатывает его. JVM вынуждена обрабатывать исключение как unchecked, что нарушает контракт типов и может привести к непредсказуемому поведению при межпроцессном взаимодействии или сериализации.

Sneaky throws оправдан только в крайне специфических сценариях: библиотечный код, полностью контролирующий контекст выполнения, или временный рефакторинг legacy-систем. В production-коде этот паттерн следует избегать.


Стратегия четвертая: Either и Try из функциональных библиотек


Наиболее элегантный подход для функциональной обработки ошибок — использование типов-результатов из библиотек вроде Vavr или самостоятельная реализация. Вместо выброса исключений метод возвращает объект, явно моделирующий два состояния: успех или ошибку.
// Упрощенная реализация Either (левая сторона — ошибка, правая — успех)
public class Either<L, R> {
private final L left;
private final R right;
private final boolean isLeft;

private Either(L left, R right, boolean isLeft) {
this.left = left;
this.right = right;
this.isLeft = isLeft;
}

public static <L, R> Either<L, R> left(L value) {
return new Either<>(value, null, true);
}

public static <L, R> Either<L, R> right(R value) {
return new Either<>(null, value, false);
}

public boolean isLeft() { return isLeft; }
public boolean isRight() { return !isLeft; }
public L getLeft() { return left; }
public R getRight() { return right; }
}


Применение для парсинга с сохранением ошибок:
public Either<String, Integer> parseInteger(String input) {
try {
return Either.right(Integer.parseInt(input));
} catch (NumberFormatException e) {
return Either.left("Invalid number format: " + input);
}
}

// Обработка в Stream API
List<String> rawValues = List.of("42", "invalid", "17", "not_a_number", "100");

List<Either<String, Integer>> results = rawValues.stream()
.map(this::parseInteger)
.collect(Collectors.toList());

List<Integer> successes = results.stream()
.filter(Either::isRight)
.map(Either::getRight)
.collect(Collectors.toList());

List<String> failures = results.stream()
.filter(Either::isLeft)
.map(Either::getLeft)
.collect(Collectors.toList());


Этот подход полностью устраняет исключения из потока управления, превращая их в значения. Клиентский код вынужден обрабатывать оба случая, что устраняет риск "забытого catch". Библиотека Vavr предоставляет готовую реализацию io.vavr.control.Either и io.vavr.control.Try с богатым API для композиции и обработки результатов.


#Java #для_новичков #beginner #exception
👍2
Практический пример: парсинг чисел с игнорированием ошибок

Объединим подходы в комплексном примере. Задача: преобразовать список строк в список целых чисел, игнорируя невалидные значения и логируя ошибки.
public class NumberParser {
private static final Logger logger = LoggerFactory.getLogger(NumberParser.class);

// Стратегия: обёртка с возвратом Optional
public List<Integer> parseAllIgnoringErrors(List<String> inputs) {
return inputs.stream()
.map(this::safeParse)
.flatMap(Optional::stream) // Java 9+: фильтрация пустых Optional
.collect(Collectors.toList());
}

private Optional<Integer> safeParse(String input) {
try {
return Optional.of(Integer.parseInt(input.trim()));
} catch (NumberFormatException e) {
logger.warn("Skipping invalid number: '{}'", input);
return Optional.empty();
}
}

// Стратегия: разделение на успешные и неуспешные
public ParseResult parseAllWithDetails(List<String> inputs) {
Map<Boolean, List<Either<String, Integer>>> partitioned = inputs.stream()
.map(this::parseWithError)
.collect(Collectors.partitioningBy(Either::isRight));

List<Integer> numbers = partitioned.get(true).stream()
.map(Either::getRight)
.collect(Collectors.toList());

List<String> errors = partitioned.get(false).stream()
.map(Either::getLeft)
.collect(Collectors.toList());

return new ParseResult(numbers, errors);
}

private Either<String, Integer> parseWithError(String input) {
try {
return Either.right(Integer.parseInt(input.trim()));
} catch (NumberFormatException e) {
return Either.left(input);
}
}

public record ParseResult(List<Integer> numbers, List<String> failedInputs) {}
}



Первый метод parseAllIgnoringErrors использует Optional для фильтрации ошибок — подход, нативно поддерживаемый Java. Второй метод parseAllWithDetails сохраняет информацию о неудачах для последующего анализа или отчетности.


Часть вторая. Исключения в конструкторах и статических блоках

Конструктор в Java — это специальный метод, вызываемый при создании объекта оператором new. Если конструктор выбрасывает исключение, объект не создается. Это фундаментальное свойство имеет важное следствие: ресурсы, выделенные внутри конструктора до момента исключения, не требуют закрытия через try-finally или try-with-resources, потому что объект не существует и не будет существовать.

public class FileProcessor {
private final FileInputStream inputStream;
private final BufferedReader reader;

public FileProcessor(String path) throws FileNotFoundException {
// Если new FileInputStream выбросит исключение,
// объект FileProcessor не будет создан
this.inputStream = new FileInputStream(path);

// Эта строка выполнится только если FileInputStream создан успешно
this.reader = new BufferedReader(new InputStreamReader(inputStream));
}
}



#Java #для_новичков #beginner #exception
👍2
В этом примере, если new FileInputStream(path) выбросит FileNotFoundException, конструктор прервется, объект FileProcessor не будет инстанцирован, и reader не будет создан. Никаких утечек ресурсов не происходит, так как FileInputStream сам не был создан.

Однако если конструктор выделяет несколько ресурсов последовательно, ситуация усложняется:
public class MultiResourceProcessor {
private final Connection connection;
private final Statement statement;
private final ResultSet resultSet;

public MultiResourceProcessor(String query) throws SQLException {
this.connection = DriverManager.getConnection(DB_URL);
this.statement = connection.createStatement();
this.resultSet = statement.executeQuery(query);
}
}


Если statement.executeQuery(query) выбросит SQLException, объект MultiResourceProcessor не будет создан, но connection и statement уже были созданы и останутся незакрытыми. Это классический сценарий утечки ресурсов в конструкторе.

Решение — использование локальных переменных и try для гарантии закрытия частично созданных ресурсов:
public MultiResourceProcessor(String query) throws SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

try {
conn = DriverManager.getConnection(DB_URL);
stmt = conn.createStatement();
rs = stmt.executeQuery(query);

// Все ресурсы созданы успешно — присваиваем полям
this.connection = conn;
this.statement = stmt;
this.resultSet = rs;
} catch (SQLException e) {
// Закрытие частично созданных ресурсов
if (rs != null) try { rs.close(); } catch (SQLException ignored) {}
if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
throw e;
}
}



Этот паттерн гарантирует, что любые ресурсы, созданные до возникновения исключения, будут закрыты. Однако он чрезвычайно многословен. Современный подход — отказ от сложной инициализации в конструкторе и использование фабричных методов:
public class MultiResourceProcessor {
private final Connection connection;
private final Statement statement;
private final ResultSet resultSet;

private MultiResourceProcessor(Connection connection, Statement statement, ResultSet resultSet) {
this.connection = connection;
this.statement = statement;
this.resultSet = resultSet;
}

public static MultiResourceProcessor create(String query) throws SQLException {
try (Connection conn = DriverManager.getConnection(DB_URL);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {

// ResultSet не закроется при выходе из try, так как мы его возвращаем
// Это требует специальной обработки — см. ниже
return new MultiResourceProcessor(conn, stmt, rs);
}
}
}


Важное ограничение: try-with-resources закроет ресурсы при выходе из блока, поэтому возврат ResultSet из фабричного метода требует отказа от автоматического закрытия или использования специальных оберток. В большинстве случаев лучше отложить создание ресурсов до момента фактического использования, а не выполнять их в конструкторе.


#Java #для_новичков #beginner #exception
👍2
Исключения в статических блоках

Статический блок инициализации выполняется при загрузке класса JVM, до создания любых экземпляров. Если в процессе выполнения статического блока или инициализации статической переменной возникает исключение, JVM автоматически оборачивает его в ExceptionInInitializerError.
public class ConfigHolder {
private static final Map<String, String> config;

static {
config = loadConfig(); // Может выбросить RuntimeException
}

private static Map<String, String> loadConfig() {
throw new RuntimeException("Configuration file corrupted");
}
}


При первом обращении к классу ConfigHolder будет выброшено:
java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Configuration file corrupted


ExceptionInInitializerError наследует LinkageError, что сигнализирует о фатальной проблеме при загрузке класса. Ключевое следствие: класс, выбросивший ExceptionInInitializerError, помечается как неинициализированный и не может быть использован в дальнейшем . Все последующие попытки обращения к этому классу приведут к NoClassDefFoundError с причиной "initialization error".
// Первое обращение — ExceptionInInitializerError
try {
ConfigHolder holder = new ConfigHolder();
} catch (ExceptionInInitializerError e) {
// Обработка
}

// Второе обращение — NoClassDefFoundError, даже если причина устранена
try {
ConfigHolder holder = new ConfigHolder(); // Не работает!
} catch (NoClassDefFoundError e) {
// Класс навсегда непригоден
}


Это поведение делает ExceptionInInitializerError особенно опасным: одна ошибка при загрузке класса делает его непригодным на всю жизнь JVM. Перезагрузка класса возможна только через создание нового ClassLoader.
Checked-исключения напрямую запрещены в статических блоках. Компилятор отклонит код, пытающийся выбросить checked из static initializer:
public class InvalidStatic {
static {
throw new IOException("Not allowed"); // Ошибка компиляции
}
}



Для обработки checked-исключений в статических блоках применяется паттерн оборачивания в ExceptionInInitializerError:
public class SafeStaticInit {
private static final Properties props;

static {
try {
props = loadProperties();
} catch (IOException e) {
// Оборачиваем checked в ExceptionInInitializerError
throw new ExceptionInInitializerError(e);
}
}

private static Properties loadProperties() throws IOException {
Properties p = new Properties();
try (InputStream is = SafeStaticInit.class.getResourceAsStream("/config.properties")) {
if (is == null) {
throw new IOException("Config file not found");
}
p.load(is);
}
return p;
}
}


В этом примере IOException из loadProperties() перехватывается и оборачивается в ExceptionInInitializerError. Важно: если мы явно выбрасываем ExceptionInInitializerError, JVM не оборачивает его повторно — сохраняется чистый стектрейс . Если же мы обернем checked в RuntimeException, JVM дополнительно обернет его в ExceptionInInitializerError, создавая избыточную вложенность:
// Антипаттерн: избыточная вложенность
static {
try {
props = loadProperties();
} catch (IOException e) {
throw new RuntimeException(e); // JVM обернет в ExceptionInInitializerError
}
}
// Результат: ExceptionInInitializerError -> RuntimeException -> IOException



#Java #для_новичков #beginner #exception
👍4
Что выведет код?

import java.util.stream.Stream;

@FunctionalInterface
interface ThrowingFunctionTask280426<T, R, E extends Exception> {
R apply(T t) throws E;
}

public class Task280426 {
public static void main(String[] args) {
try {
Stream.of("1", "2", "3")
.map(wrap(s -> throwsChecked(s)))
.forEach(System.out::print);
} catch (Exception e) {
System.out.print("Error");
}
}

static int throwsChecked(String s) throws Exception {
if (s.equals("2")) throw new Exception("boom");
return Integer.parseInt(s);
}

static <T, R> java.util.function.Function<T, R> wrap(ThrowingFunctionTask280426<T, R, Exception> f) {
return t -> {
try {
return f.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
17%
123
17%
12Error
50%
1Error
17%
1 Exception
👍2
Как передаются аргументы в Java: по значению или по ссылке? 🤓

Ответ:

В Java аргументы всегда передаются по значению (by value).

Для примитивов передается само значение. Для объектов передается значение ссылки на объект (копия ссылки).

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

Поэтому часто говорят "объекты передаются по значению ссылки".


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
История технологии сегодня — 29 апреля

ℹ️ Кто родился в этот день

Жюль Анри́ Пуанкаре́ (фр. Jules Henri Poincaré; 29 апреля 1854, Нанси, Франция — 17 июля 1912, Париж, Франция) — французский математик, механик, физик, астроном и философ.

Среди его самых крупных достижений:
Создание топологии.
Создание качественной теории дифференциальных уравнений.
Разработка теории автоморфных функций.
Разработка новых, чрезвычайно эффективных методов небесной механики.
Создание математических основ теории относительности, а также обобщение принципа относительности на все физические явления.
Наглядная модель геометрии Лобачевского (впервые встречается у Эудженио Бельтрами).



🌐 Знаковые события

1913 — эмигрировавший в США шведский инженер-электрик Гидеон Сундбек получил патент на изобретение, известное сейчас как застёжка-молния.


#Biography #Birth_Date #Events #29апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
[Совет по Java #031]

Тема: ArrayList.subList() не копирует данные, а создает view на исходный список.

Проблема: Метод subList(int fromIndex, int toIndex) возвращает не новый независимый список, а проекцию (view) исходного ArrayList.

Все операции над view отображаются на исходный список, и наоборот. Однако при структурной модификации исходного списка (добавление или удаление элементов) после создания view, последний становится некорректным. При последующей операции над sublist выбрасывается ConcurrentModificationException.

Это частая ошибка: разработчик создает sublist, затем изменяет оригинал и пытается использовать view, получая исключение. Кроме того, изменения sublist (например, clear()) неожиданно модифицируют оригинальный список, что может привести к трудноотлаживаемым багам.

Решение: Если нужен независимый список, скопируйте sublist в новую коллекцию: new ArrayList<>(list.subList(1, 5)). Если работаете с view, избегайте структурных модификаций исходного списка. Для read-only операций sublist эффективен и удобен.

public class SubListViewExample {

public static void main(String[] args) {
List<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8));

// Создаем view на подсписок
List<Integer> sub = original.subList(2, 5);
System.out.println("Sublist (view): " + sub); // [3, 4, 5]

//Опасность 1: изменение view меняет оригинал
sub.set(0, 99);
sub.add(100);
System.out.println("Original after modifying view: " + original);
// [1, 2, 99, 4, 5, 100, 6, 7, 8] — оригинал изменился!

//Опасность 2: модификация оригинала ломает view
try {
original.add(999); // структурная модификация оригинала
sub.get(0); // ConcurrentModificationException!
} catch (ConcurrentModificationException e) {
System.out.println("View сломан: " + e);
}

// Восстановим списки для демонстрации
original = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8));
sub = original.subList(2, 5);

//Решение 1: копирование для независимой работы
List<Integer> copy = new ArrayList<>(original.subList(2, 5));
copy.set(0, 999);
copy.add(1000);
System.out.println("Original unchanged: " + original); // [1, 2, 3, 4, 5, 6, 7, 8]
System.out.println("Independent copy: " + copy); // [999, 4, 5, 1000]

//Решение 2: работа только с view без трогания оригинала
List<Integer> list = new ArrayList<>(Arrays.asList(10, 20, 30, 40, 50));
List<Integer> view = list.subList(1, 4);
// Можно безопасно читать view
System.out.println("View: " + view); // [20, 30, 40]
// Можно менять элементы через set (не структурная модификация)
view.set(1, 999);
System.out.println("Original after set: " + list); // [10, 20, 999, 40, 50]
// НО нельзя добавлять/удалять из оригинала
// list.remove(0); // сломает view!

//Альтернатива: Java 8+ Stream для создания копии
List<Integer> streamCopy = original.stream().skip(2).limit(3).collect(Collectors.toList());
System.out.println("Stream copy: " + streamCopy);
}
}


Объяснение: subList() возвращает объект внутреннего класса SubList, который хранит ссылку на исходный ArrayList и смещения. Все операции делегируются исходному списку с проверкой границ. При создании SubList запоминается счетчик модификаций (modCount) оригинала. При каждой операции view сравнивает текущий modCount оригинала с запомненным. Если они различаются (оригинал был структурно изменен), выбрасывается ConcurrentModificationException.

Структурной модификацией считается изменение размера списка (add, remove, clear). Замена элементов через set() к ней не относится. Эта защита предотвращает неконсистентное состояние, когда view указывает на неправильные диапазоны.

#Java #советы
👍5
12. RabbitMQ: Полный разбор от Confirm до DLQ — всё, что нужно для надёжной очереди задач

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

Мы переходим от in-memory асинхронности (@Async) к настоящему брокеру сообщений — RabbitMQ.

Вы узнаете, как построить production-конвейер задач, который не теряет сообщения при падении сервиса, умеет повторять попытки, изолирует проблемные сообщения и даёт полную видимость через метрики.

Исходный код проекта на GitHub очень ждет Ваших звезд ☺️ (Вам че блин, жалко?)

Ссылка на Youtube
Ссылка на Рутьюб

Смотрите, ставьте лайки, подписывайтесь на каналы!✌️

❗️❗️❗️ Огромная просьба - если Вам понравилась моя работа, распространите эту серию по всем доступным вам местам: телеграм, discord и прочим каналам. ❗️❗️❗️

Буду крайне благодарен
🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5
Что выведет код?

import java.util.*;

public class Task290426 {
public static void main(String[] args) {
List<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> sub = original.subList(1, 4);

sub.add(99);
original.add(100);

System.out.println(sub.size());
System.out.println(original.size());
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
21%
4 6
21%
4 7
11%
3 6
47%
Исключение
👍2
Что такое NPE (NullPointerException) и как его избежать? 🤓

Ответ:

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

Типичные ситуации: вызов метода у null, обращение к полю, доступ к элементу массива.

Способы избежать:
1) проверка на null перед использованием (if (obj != null)).
2) использование Optional.
3) использование Objects.requireNonNull().
4) аннотации
@Nullable и @NonNull (IDE и статические анализаторы).
5) избегание возврата null (возвращайте пустую коллекцию или Optional).


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
История технологии сегодня — 30 апреля

ℹ️ Кто родился в этот день

Клод Э́лвуд Ше́ннон (англ. Claude Elwood Shannon; 30 апреля 1916, Петоски, Мичиган, США — 24 февраля 2001, Медфорд, Массачусетс, США) американский инженер, криптоаналитик и математик. Считается «отцом информационного века».

Является основателем теории информации, нашедшей применение в современных высокотехнологических системах связи. Предоставил фундаментальные понятия, идеи и их математические формулировки, которые в настоящее время формируют основу для современных коммуникационных технологий. В 1948 году предложил использовать слово «бит» для обозначения наименьшей единицы информации (в статье «Математическая теория связи»). Кроме того, понятие энтропии было важной особенностью теории Шеннона. Он продемонстрировал, что введённая им энтропия эквивалентна мере неопределённости информации в передаваемом сообщении. Статьи Шеннона «Математическая теория связи» и «Теория связи в секретных системах» считаются основополагающими для теории информации и криптографии. Клод Шеннон был одним из первых, кто подошёл к криптографии с научной точки зрения, он первым сформулировал её теоретические основы и ввёл в рассмотрение многие основные понятия. Шеннон внёс ключевой вклад в теорию вероятностных схем, теорию игр, теорию автоматов и теорию систем управления — области наук, входящие в понятие «кибернетика».


Иога́нн Карл Фри́дрих Га́усс (нем. Johann Carl Friedrich Gauß; 30 апреля 1777, Брауншвейг — 23 февраля 1855, Гёттинген) — немецкий математик, механик, физик, астроном и геодезист. Считается одним из величайших математиков всех времён, «королём математиков». С именем Гаусса связаны фундаментальные исследования почти во всех основных областях математики: в алгебре, теории чисел, дифференциальной и неевклидовой геометрии, математическом анализе, теории функций комплексного переменного, теории вероятностей, а также в аналитической и небесной механике, астрономии, физике и геодезии. «В каждой области глубина проникновения в материал, смелость мысли и значительность результата были поражающими.


🌐 Знаковые события

1897 — физик Джозеф Томсон на лекции в Королевском институте объявил об открытии электрона.

1993 — в Женеве объявлено, что технология Всемирной паутины (World Wide Web), разработанная сотрудником Европейской лаборатории физики элементарных частиц (CERN) англичанином Тимом Бернерсом-Ли, будет для всех бесплатной.


#Biography #Birth_Date #Events #30апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Хохма дня))) Вот что отвечает нейронка о изучении 1С 😂😂😂
🤓2
Раздел 9. Исключения, логирование, отладка

Глава 1. Иерархия исключений (Exceptions)

Objects.requireNonNull() – стандартная фабрика NullPointerException

Null-ссылка, введенная Тони Хоаром в 1965 году, впоследствии названная им "миллиардной ошибкой" (billion-dollar mistake), остается одним из наиболее частых источников runtime-ошибок в Java. NullPointerException занимает второе место по частоте среди всех дефектов программного обеспечения, что подчеркивает масштаб проблемы.

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

До Java 7 проверка параметров на null выполнялась через явные условия:
public void processOrder(Order order) {
if (order == null) {
throw new NullPointerException("Order cannot be null");
}
// Основная логика
}


Этот подход многословен и не предоставляет стандартизированного способа валидации. Java 7 ввела класс java.util.Objects с методом requireNonNull(), который превратил проверку null в однострочную операцию с гибкими возможностями кастомизации сообщений.


Методы requireNonNull: три перегрузки

Класс Objects предоставляет три перегруженные версии requireNonNull, каждая из которых подходит для разных сценариев:
// 1. Базовая версия без сообщения
public static <T> T requireNonNull(T obj)

// 2. Версия со статическим сообщением
public static <T> T requireNonNull(T obj, String message)

// 3. Версия с ленивым сообщением через Supplier
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)


Все три версии возвращают переданный объект, если он не null, что позволяет использовать их inline при инициализации полей или передаче параметров. Если объект null, выбрасывается NullPointerException с соответствующим сообщением.


Базовая версия: fail-fast без лишних слов
public class OrderService {
private final OrderRepository repository;

public OrderService(OrderRepository repository) {
this.repository = Objects.requireNonNull(repository);
}
}


Здесь requireNonNull(repository) возвращает repository, если он не null, или выбрасывает NullPointerException с дефолтным сообщением. Этот паттерн идеален для конструкторов, где требуется гарантия ненулевых зависимостей, а специфичное сообщение не критично.


Версия со статическим сообщением: контекст для отладки
public void updateInventory(Inventory inventory, String warehouseId) {
Objects.requireNonNull(inventory, "Inventory object cannot be null");
Objects.requireNonNull(warehouseId, "Warehouse ID must be specified");

inventory.adjustStock(warehouseId);
}


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


Версия с Supplier: ленивое вычисление сообщений

Третья перегрузка принимает Supplier<String> и вычисляет сообщение только при фактической необходимости — когда объект равен null:
public void processTransaction(Transaction tx) {
Objects.requireNonNull(tx,
() -> String.format("Transaction cannot be null at %s", Instant.now()));

// Сложное форматирование выполняется только если tx == null
}


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

Сравнение производительности:
// Избыточно: сообщение формируется всегда
Objects.requireNonNull(user, "User " + userId + " not found in database " + dbName);

// Эффективно: сообщение формируется только при null
Objects.requireNonNull(user,
() -> "User " + userId + " not found in database " + dbName);


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


#Java #для_новичков #beginner #exception #requireNonNull
👍3
Когда использовать requireNonNull

Валидация параметров методов и конструкторов

Основное применение requireNonNull — проверка предусловий (preconditions) на границе метода. Это реализация принципа fail-fast: ошибка обнаруживается немедленно, предотвращая каскадные сбои и упрощая отладку. Если метод валидирует параметры upfront, он быстро завершается с четким исключением, указывающим источник проблемы.
public class PaymentProcessor {
private final PaymentGateway gateway;
private final TransactionLogger logger;

public PaymentProcessor(PaymentGateway gateway, TransactionLogger logger) {
this.gateway = Objects.requireNonNull(gateway, "PaymentGateway is required");
this.logger = Objects.requireNonNull(logger, "TransactionLogger is required");
}

public PaymentResult process(PaymentRequest request) {
Objects.requireNonNull(request, "PaymentRequest cannot be null");

gateway.authorize(request);
logger.log(request);
return new PaymentResult();
}
}


Гарантия ненулевых возвращаемых значений


requireNonNull может использоваться для защиты от непреднамеренного возврата null из методов, особенно при делегировании к внутренним компонентам:
public Customer getCustomer(String customerId) {
Customer customer = customerRepository.findById(customerId);
return Objects.requireNonNull(customer,
() -> "Customer not found for ID: " + customerId);
}


Здесь requireNonNull служит последней линией защиты: если репозиторий вернул null (что не должно происходить по контракту, но возможно из-за бага), метод выбросит NullPointerException с информативным сообщением вместо передачи null вызывающему коду.

Защита внутреннего состояния при делегировании

При реализации методов, делегирующих вызовы внутренним объектам, requireNonNull гарантирует, что поле инициализировано:
public class CachedDataProvider {
private DataProvider delegate;

public void initialize(DataProvider provider) {
this.delegate = Objects.requireNonNull(provider);
}

public Data fetchData() {
return Objects.requireNonNull(delegate, "Provider not initialized").fetch();
}
}


Вызов fetchData() до initialize() выбросит NullPointerException с понятным сообщением, вместо стандартного NPE на разыменовании null.


Документирование через Javadoc

Использование requireNonNull должно сопровождаться документированием в Javadoc. Тег @throws указывает, что метод выбрасывает NullPointerException при нарушении параметрических ограничений:
/**
* Обрабатывает платеж через указанный шлюз.
*
* @param request запрос на платеж, не может быть null
* @param gateway платежный шлюз, не может быть null
* @return результат обработки платежа
* @throws NullPointerException если request или gateway равны null
*/
public PaymentResult process(PaymentRequest request, PaymentGateway gateway) {
Objects.requireNonNull(request, "PaymentRequest is required");
Objects.requireNonNull(gateway, "PaymentGateway is required");

return gateway.process(request);
}


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


Связь с аннотациями @NonNull

Разделение ответственности: runtime vs compile-time

Objects.requireNonNull() обеспечивает runtime-проверку: исключение возникает во время выполнения, если null передан в метод. Аннотации @NonNull (и их аналоги) предоставляют compile-time информацию о nullability, позволяя IDE и статическим анализаторам предупреждать о потенциальных проблемах до запуска программы.

Эти механизмы комплементарны, а не взаимоисключающие. Аннотации @NonNull документируют контракт и помогают инструментам, но не обеспечивают runtime-защиту. requireNonNull гарантирует защиту во время выполнения, но не предоставляет информации для статического анализа. Идеальный подход — комбинировать оба механизма.

#Java #для_новичков #beginner #exception #requireNonNull
👍3
Экосистема аннотаций nullability

В Java-экосистеме существует множество аннотаций
@NonNull из разных источников, каждая со своей семантикой и областью применения:
JSpecify (org.jspecify.annotations.NonNull) — современный стандарт, разработанный консорциумом Google, JetBrains, Spring и других. Применяется к использованию типа (type use), что позволяет различать nullability элементов массивов и generic-типов.
Spring Framework (org.springframework.lang.NonNull) — устаревшие аннотации из Spring 5, deprecated в Spring 7 в пользу JSpecify. Применялись к полям, параметрам и возвращаемым значениям.
JetBrains (org.jetbrains.annotations.NotNull) — аннотации IntelliJ IDEA, широко поддерживаемые IDE и Kotlin-компилятором.
JSR-305 (javax.annotation.Nonnull) — спецификация, больше не поддерживаемая активно, но широко распространенная в legacy-коде.
Jakarta Bean Validation (jakarta.validation.constraints.NotNull) — используется для runtime-валидации в фреймворках вроде Hibernate Validator.

JSpecify: современный стандарт

JSpecify, выпущенный в версии 1.0.0, представляет собой наиболее перспективный стандарт для null safety в Java. Он определяет три состояния nullability: unspecified (не указано), nullable (@Nullable) и non-null (@NonNull). Ключевая особенность — аннотация @NullMarked, применяемая на уровне пакета, которая устанавливает non-null как значение по умолчанию для всех типов в пакете, устраняя необходимость в явном @NonNull для каждого параметра.
// package-info.java
@NullMarked
package com.example.service;

import org.jspecify.annotations.NullMarked;


После этого все параметры, возвращаемые значения и поля в пакете считаются non-null по умолчанию. Только явно аннотированные @Nullable типы могут содержать null.
package com.example.service;

import org.jspecify.annotations.Nullable;

public class UserService {
// Не требует @NonNull — non-null по умолчанию благодаря @NullMarked
public User findById(String id) {
// ...
}

// Явно указано, что может вернуть null
public @Nullable User findByEmail(String email) {
// ...
}

// Параметр явно nullable
public void updateNickname(String id, @Nullable String nickname) {
// ...
}
}



Интеграция requireNonNull с аннотациями

Комбинированный подход использует @NonNull (или неявный non-null через @NullMarked) для документирования контракта и статического анализа, и requireNonNull для runtime-защиты:
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
public class OrderService {
private final PaymentGateway gateway;

public OrderService(PaymentGateway gateway) {
// Runtime-защита: выбросит NPE с сообщением
this.gateway = Objects.requireNonNull(gateway, "PaymentGateway is required");
}

// Метод принимает non-null по умолчанию (благодаря @NullMarked)
public Order processOrder(String orderId) {
Objects.requireNonNull(orderId, "Order ID is required");
return gateway.process(orderId);
}

// Явно nullable параметр
public Order processOrderWithNotes(String orderId, @Nullable String notes) {
Objects.requireNonNull(orderId, "Order ID is required");
// notes может быть null — допустимо по контракту
return gateway.process(orderId, notes);
}
}


В этом примере:

IDE и статические анализаторы (NullAway, Checker Framework) предупреждают о попытке передать null в non-null параметры на этапе разработки
requireNonNull гарантирует защиту во время выполнения, если статический анализ был проигнорирован или null пришел из неаннотированного кода
Кастомные сообщения в requireNonNull обеспечивают контекст при runtime-ошибках


#Java #для_новичков #beginner #exception #requireNonNull
👍3