Примеры решений в Java
1. Чтение и запись текстового файла (NIO.2)
import java.nio.file.*;
import java.util.List;
// Чтение всех строк из файла в List<String>
public static List<String> readLines(Path path) throws Exception {
return Files.readAllLines(path, StandardCharsets.UTF_8);
}
// Запись списка строк в файл (перезапись)
public static void writeLines(Path path, List<String> lines) throws Exception {
Files.write(path, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}
// Добавление строки в конец файла
public static void appendLine(Path path, String line) throws Exception {
Files.write(path, (line + System.lineSeparator()).getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
2. Парсинг JSON (с использованием org.json — простой и лёгкий вариант)
import org.json.JSONObject;
import org.json.JSONArray;
// Создание и сериализация JSON-объекта
public static String buildUserJson(String name, int age, List<String> tags) {
JSONObject user = new JSONObject();
user.put("name", name);
user.put("age", age);
user.put("tags", new JSONArray(tags));
return user.toString(2); // отступ 2 — читаемый вывод
}
// Парсинг JSON-строки → безопасно с проверкой типов
public static void parseUserJson(String json) {
JSONObject obj = new JSONObject(json);
String name = obj.optString("name", "unknown");
int age = obj.optInt("age", -1);
JSONArray tagsArr = obj.optJSONArray("tags");
List<String> tags = tagsArr != null ?
tagsArr.toList().stream().map(Object::toString).toList() :
List.of();
System.out.printf("User: %s, %d y.o., tags: %s%n", name, age, tags);
}
⚠️ Для продакшена рекомендуются Jackson (
ObjectMapper) или Gson (более производительные и типобезопасные).org.jsonудобен для прототипирования.
3. HTTP-запрос GET/POST (встроенное java.net.http.HttpClient, JDK 11+)
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
// Синхронный GET
public static String httpGet(String url) throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(15))
.header("User-Agent", "Java-App/1.0")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("HTTP " + response.statusCode() + ": " + response.body());
}
return response.body();
}
// Асинхронный POST с JSON-телом
public static CompletableFuture<String> httpPostJson(String url, String jsonBody) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(resp -> {
if (resp.statusCode() != 200 && resp.statusCode() != 201) {
throw new RuntimeException("HTTP " + resp.statusCode());
}
return resp.body();
});
}
4. Работа с датами и временем (java.time)
import java.time.*;
import java.time.format.*;
import java.time.temporal.ChronoUnit;
// Текущая дата/время в UTC и локальной зоне
public static void demoDateTime() {
ZonedDateTime nowUtc = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime nowLocal = ZonedDateTime.now();
System.out.println("UTC: " + nowUtc.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
System.out.println("Local: " + nowLocal.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss z")));
}
// Парсинг и форматирование ISO-8601
public static LocalDateTime parseIsoDateTime(String isoStr) {
return LocalDateTime.parse(isoStr, DateTimeFormatter.ISO_DATE_TIME);
}
// Добавление/вычитание временных единиц
public static LocalDate addDays(LocalDate date, long days) {
return date.plusDays(days);
}
// Разница в днях/часах
public static long daysBetween(LocalDate start, LocalDate end) {
return ChronoUnit.DAYS.between(start, end);
}
// Конвертация Instant ↔ LocalDateTime
public static LocalDateTime toLocalDateTime(Instant instant, ZoneId zone) {
return LocalDateTime.ofInstant(instant, zone);
}
public static Instant toInstant(LocalDateTime ldt, ZoneId zone) {
return ldt.atZone(zone).toInstant();
}
5. Коллекции: безопасные и эффективные идиомы
import java.util.*;
import java.util.stream.Collectors;
// Создание неизменяемых коллекций (JDK 9+)
public static void immutableCollections() {
List<String> list = List.of("a", "b", "c"); // неизменяемый
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("x", 10, "y", 20);
}
// Группировка по полю (Stream API)
record Person(String name, int age, String city) {}
public static Map<String, List<Person>> groupByCity(List<Person> people) {
return people.stream()
.collect(Collectors.groupingBy(Person::city));
}
// Подсчёт вхождений (частотный словарь)
public static Map<String, Long> wordFrequencies(List<String> words) {
return words.stream()
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
}
// Безопасное получение из Map с дефолтом
public static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
V value = map.get(key);
return value != null ? value : defaultValue;
// или просто map.getOrDefault(key, defaultValue) — встроенная реализация
}
6. Многопоточность: CompletableFuture, ExecutorService, synchronized, ReentrantLock
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
// Выполнение задачи в пуле потоков с возвратом результата
public static CompletableFuture<String> asyncTask(ExecutorService executor) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
return "Result from thread: " + Thread.currentThread().getName();
}, executor);
}
// Обработка исключений в CompletableFuture
public static CompletableFuture<String> safeAsyncTask() {
return CompletableFuture.supplyAsync(() -> {
if (Math.random() < 0.3) throw new RuntimeException("Oops");
return "OK";
}).exceptionally(ex -> "Fallback: " + ex.getMessage());
}
// Thread-safe инкремент с ReentrantLock (альтернатива synchronized)
public static class Counter {
private long value = 0;
private final ReentrantLock lock = new ReentrantLock();
public long increment() {
lock.lock();
try {
return ++value;
} finally {
lock.unlock();
}
}
}
7. Работа с регулярными выражениями
import java.util.regex.*;
// Валидация email (упрощённая, для демонстрации)
public static boolean isValidEmail(String email) {
String pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
return email != null && email.matches(pattern);
}
// Извлечение всех совпадений по шаблону
public static List<String> extractWords(String text) {
Pattern p = Pattern.compile("\\b\\w+\\b");
Matcher m = p.matcher(text);
List<String> words = new ArrayList<>();
while (m.find()) {
words.add(m.group());
}
return words;
}
// Замена с использованием лямбды (JDK 9+)
public static String maskDigits(String s) {
return Pattern.compile("\\d")
.matcher(s)
.replaceAll(match -> "*");
}
8. Рефлексия: безопасное получение поля/метода
import java.lang.reflect.Field;
import java.lang.reflect.Method;
// Получение значения приватного поля (только для тестов/отладки!)
public static Object getPrivateFieldValue(Object obj, String fieldName) throws Exception {
Class<?> clazz = obj.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
}
// Вызов приватного метода
public static Object callPrivateMethod(Object obj, String methodName, Class<?>[] paramTypes, Object... args) throws Exception {
Method method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(obj, args);
}
⚠️ Рефлексия ломает инкапсуляцию и снижает производительность. Используйте только при крайней необходимости (например, в тестовых фреймворках или сериализаторах).
9. Работа с базой данных через JDBC (без ORM)
import java.sql.*;
// Настройка подключения (используйте connection pooling в продакшене — HikariCP, Tomcat JDBC)
public static Connection createConnection(String url, String user, String password) throws SQLException {
return DriverManager.getConnection(url, user, password);
}
// Выполнение SELECT с маппингом строк → объекты
record User(long id, String name, String email) {}
public static List<User> findUsersByName(Connection conn, String pattern) throws SQLException {
String sql = "SELECT id, name, email FROM users WHERE name ILIKE ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%" + pattern + "%");
try (ResultSet rs = stmt.executeQuery()) {
List<User> users = new ArrayList<>();
while (rs.next()) {
users.add(new User(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email")
));
}
return users;
}
}
}
// Выполнение INSERT с возвратом сгенерированного ID
public static long insertUser(Connection conn, String name, String email) throws SQLException {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, name);
stmt.setString(2, email);
int affected = stmt.executeUpdate();
if (affected == 0) throw new SQLException("Insert failed");
try (ResultSet keys = stmt.getGeneratedKeys()) {
if (keys.next()) {
return keys.getLong(1);
} else {
throw new SQLException("No generated key");
}
}
}
}
// Безопасная транзакция с откатом при ошибке
public static void updateUserEmail(Connection conn, long userId, String newEmail) throws SQLException {
conn.setAutoCommit(false);
try {
try (PreparedStatement stmt = conn.prepareStatement("UPDATE users SET email = ? WHERE id = ?")) {
stmt.setString(1, newEmail);
stmt.setLong(2, userId);
stmt.executeUpdate();
}
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}
💡 В продакшене рекомендуется использовать пулы соединений (
HikariDataSource) иtry-with-resourcesдля всех ресурсов (Connection,PreparedStatement,ResultSet).
10. Шаблоны проектирования — готовые к использованию реализации
a) Singleton (ленивая инициализация, потокобезопасный, без synchronized)
public class DatabaseConfig {
private static class Holder {
static final DatabaseConfig INSTANCE = new DatabaseConfig();
}
private DatabaseConfig() {
// запрет прямой инициализации
}
public static DatabaseConfig getInstance() {
return Holder.INSTANCE;
}
// пример данных
public String getHost() { return "localhost"; }
public int getPort() { return 5432; }
}
b) Builder (для неизменяемого объекта с обязательными/опциональными полями)
public final class HttpRequest {
private final String method;
private final URI uri;
private final Map<String, String> headers;
private final byte[] body;
private HttpRequest(Builder builder) {
this.method = builder.method;
this.uri = builder.uri;
this.headers = Map.copyOf(builder.headers); // защита от мутации
this.body = builder.body == null ? new byte[0] : builder.body.clone();
}
public static class Builder {
private final String method;
private final URI uri;
private final Map<String, String> headers = new LinkedHashMap<>();
private byte[] body;
public Builder(String method, URI uri) {
this.method = Objects.requireNonNull(method);
this.uri = Objects.requireNonNull(uri);
}
public Builder header(String name, String value) {
headers.put(Objects.requireNonNull(name), Objects.requireNonNull(value));
return this;
}
public Builder body(byte[] data) {
this.body = data; // клонирование — в конструкторе
return this;
}
public HttpRequest build() {
return new HttpRequest(this);
}
}
// геттеры (только для чтения)
public String method() { return method; }
public URI uri() { return uri; }
public Map<String, String> headers() { return headers; }
public byte[] body() { return body.clone(); }
}
Использование:
var req = new HttpRequest.Builder("POST", URI.create("https://api.example.com/data"))
.header("Content-Type", "application/json")
.body("{\"key\":\"value\"}".getBytes(StandardCharsets.UTF_8))
.build();
c) Observer (на основе интерфейсов JDK — java.util.Observer устарел, делаем свой)
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
@FunctionalInterface
public interface Observer<T> {
void update(T event);
}
public class Observable<T> {
private final Set<Observer<T>> observers = new CopyOnWriteArraySet<>();
public void addObserver(Observer<T> observer) {
observers.add(observer);
}
public void removeObserver(Observer<T> observer) {
observers.remove(observer);
}
protected void notifyObservers(T event) {
observers.forEach(o -> o.update(event));
}
}
// Пример использования
class TemperatureSensor extends Observable<Double> {
private double currentTemp;
public void setTemperature(double temp) {
this.currentTemp = temp;
notifyObservers(temp); // оповещаем всех
}
}
11. Валидация данных (Jakarta Validation — jakarta.validation)
Требуется зависимость:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.2</version>
</dependency>
import jakarta.validation.*;
import jakarta.validation.constraints.*;
record RegistrationRequest(
@NotBlank(message = "Имя не может быть пустым")
@Size(min = 2, max = 50)
String name,
@Email(message = "Некорректный email")
String email,
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@$!%*#?&]{8,}$",
message = "Пароль: мин. 8 симв., буквы + цифры")
String password
) {}
public class ValidatorUtil {
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static <T> Set<ConstraintViolation<T>> validate(T object) {
return validator.validate(object);
}
// Удобный метод: бросает исключение при нарушениях
public static <T> void validateOrThrow(T object) {
Set<ConstraintViolation<T>> violations = validate(object);
if (!violations.isEmpty()) {
String messages = violations.stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.collect(Collectors.joining("; "));
throw new IllegalArgumentException("Validation failed: " + messages);
}
}
}
Использование:
var req = new RegistrationRequest("A", "bad-email", "123");
ValidatorUtil.validateOrThrow(req); // → IllegalArgumentException
12. Логирование через SLF4J + Logback (рекомендованный стек)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataService {
private static final Logger log = LoggerFactory.getLogger(DataService.class);
public void processData(String data) {
log.debug("Получены данные: {}", data); // не выводится, если уровень INFO
try {
// ... обработка
log.info("Данные '{}' успешно обработаны", data);
} catch (Exception e) {
log.error("Ошибка при обработке данных '{}'", data, e); // полный стек
}
}
}
Файл logback.xml (в src/main/resources):
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<!-- Отдельный уровень для вашего пакета -->
<logger name="ru.ituniverse" level="DEBUG" />
</configuration>
13. Простая CLI-утилита (ввод/вывод, аргументы командной строки)
import java.util.Scanner;
public class CliApp {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Использование: java CliApp <команда> [аргументы]");
System.out.println("Доступные команды: greet, sum, exit");
interactiveMode();
return;
}
String cmd = args[0];
if ("greet".equals(cmd) && args.length > 1) {
System.out.println("Привет, " + String.join(" ", Arrays.copyOfRange(args, 1, args.length)) + "!");
} else if ("sum".equals(cmd) && args.length > 1) {
try {
int sum = Arrays.stream(Arrays.copyOfRange(args, 1, args.length))
.mapToInt(Integer::parseInt)
.sum();
System.out.println("Сумма: " + sum);
} catch (NumberFormatException e) {
System.err.println("Все аргументы после 'sum' должны быть целыми числами.");
}
} else if ("exit".equals(cmd)) {
System.exit(0);
} else {
System.err.println("Неизвестная команда: " + cmd);
}
}
private static void interactiveMode() {
Scanner scanner = new Scanner(System.in);
System.out.println("Режим REPL. Введите команду (или 'exit' для выхода):");
while (true) {
System.out.print("> ");
String line = scanner.nextLine().trim();
if (line.isEmpty()) continue;
String[] tokens = line.split("\\s+");
if ("exit".equals(tokens[0])) break;
main(tokens); // рекурсивный вызов с аргументами
}
scanner.close();
}
}
14. Работа с ZIP-архивами (чтение/создание)
import java.nio.file.*;
import java.util.zip.*;
// Создание ZIP из директории
public static void zipDirectory(Path sourceDir, Path zipFile) throws Exception {
try (var zos = new ZipOutputStream(Files.newOutputStream(zipFile))) {
Files.walk(sourceDir)
.filter(Files::isRegularFile)
.forEach(path -> {
ZipEntry entry = new ZipEntry(sourceDir.relativize(path).toString());
try {
zos.putNextEntry(entry);
Files.copy(path, zos);
zos.closeEntry();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
// Извлечение ZIP в директорию
public static void unzip(Path zipFile, Path targetDir) throws Exception {
Files.createDirectories(targetDir);
try (var zis = new ZipInputStream(Files.newInputStream(zipFile))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
Path outputPath = targetDir.resolve(entry.getName()).normalize();
if (!outputPath.startsWith(targetDir)) {
throw new SecurityException("Попытка извлечения вне целевой директории: " + entry.getName());
}
if (entry.isDirectory()) {
Files.createDirectories(outputPath);
} else {
Files.createDirectories(outputPath.getParent());
Files.copy(zis, outputPath, StandardCopyOption.REPLACE_EXISTING);
}
zis.closeEntry();
}
}
}
15. Unit-тесты (JUnit 5 + AssertJ)
Требуется зависимость:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
import org.junit.jupiter.api.*;
import static org.assertj.core.api.Assertions.*;
class UserServiceTest {
private UserService service;
private InMemoryUserRepository repo;
@BeforeEach
void setUp() {
repo = new InMemoryUserRepository();
service = new UserService(repo);
}
@Test
@DisplayName("Создание пользователя возвращает ненулевой ID")
void createUser_returnsNonZeroId() {
User user = new User(0, "Alice", "alice@example.com");
User saved = service.createUser(user);
assertThat(saved.id()).isGreaterThan(0);
assertThat(saved.name()).isEqualTo("Alice");
}
@Test
@DisplayName("Поиск по несуществующему ID возвращает Optional.empty()")
void findById_nonExisting_returnsEmpty() {
Optional<User> found = service.findById(999L);
assertThat(found).isEmpty();
}
@Test
@DisplayName("Валидация: пустое имя вызывает исключение")
void createUser_emptyName_throwsValidationException() {
User invalid = new User(0, "", "test@test.com");
assertThatThrownBy(() -> service.createUser(invalid))
.isInstanceOf(ValidationException.class)
.hasMessageContaining("Имя не может быть пустым");
}
@Nested
@DisplayName("Фильтрация пользователей")
class FilteringTests {
@BeforeEach
void populate() {
service.createUser(new User(0, "Alice", "a@example.com"));
service.createUser(new User(0, "Bob", "b@example.com"));
service.createUser(new User(0, "Charlie", "c@example.com"));
}
@Test
void byNamePrefix_returnsMatching() {
var results = service.findByNamePrefix("Al");
assertThat(results).hasSize(1)
.extracting(User::name)
.containsExactly("Alice");
}
}
}
16. Работа с XML: StAX (потоковый, эффективный, без DOM)
StAX (
javax.xml.stream) — стандартная часть JDK. Не требует внешних библиотек.
a) Чтение XML (pull-parser)
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import java.io.InputStream;
public record Book(String isbn, String title, String author) {}
public static List<Book> parseBooks(InputStream xmlStream) throws XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
XMLEventReader reader = factory.createXMLEventReader(xmlStream);
List<Book> books = new ArrayList<>();
String isbn = null, title = null, author = null;
boolean insideBook = false;
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
StartElement start = event.asStartElement();
String name = start.getName().getLocalPart();
if ("book".equals(name)) {
insideBook = true;
isbn = start.getAttributeByName(new QName("isbn"))?.getValue();
} else if ("title".equals(name) && insideBook) {
title = reader.getElementText(); // автопереход к следующему событию
} else if ("author".equals(name) && insideBook) {
author = reader.getElementText();
}
} else if (event.isEndElement()) {
EndElement end = event.asEndElement();
if ("book".equals(end.getName().getLocalPart()) && insideBook) {
books.add(new Book(isbn, title, author));
isbn = title = author = null;
insideBook = false;
}
}
}
reader.close();
return books;
}
Входной XML:
<library>
<book isbn="978-0134685991">
<title>Effective Java</title>
<author>Joshua Bloch</author>
</book>
</library>
b) Запись XML (StAX writer)
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
public static void writeBooks(OutputStream out, List<Book> books) throws XMLStreamException {
XMLOutputFactory factory = XMLOutputFactory.newFactory();
XMLStreamWriter writer = factory.createXMLStreamWriter(out, "UTF-8");
writer.writeStartDocument("UTF-8", "1.0");
writer.writeStartElement("library");
for (Book b : books) {
writer.writeStartElement("book");
writer.writeAttribute("isbn", b.isbn());
writer.writeStartElement("title");
writer.writeCharacters(b.title());
writer.writeEndElement(); // </title>
writer.writeStartElement("author");
writer.writeCharacters(b.author());
writer.writeEndElement(); // </author>
writer.writeEndElement(); // </book>
}
writer.writeEndElement(); // </library>
writer.writeEndDocument();
writer.close();
}
17. Работа с бинарными данными: DataOutputStream / DataInputStream
import java.io.*;
// Запись структурированных бинарных данных (самодельный «протокол»)
public static void writeUserBinary(DataOutputStream dos, String name, int age, boolean active) throws IOException {
dos.writeUTF(name); // длина + UTF-8 строка
dos.writeInt(age);
dos.writeBoolean(active);
}
// Чтение в том же порядке
public static record UserBin(String name, int age, boolean active) {}
public static UserBin readUserBinary(DataInputStream dis) throws IOException {
return new UserBin(
dis.readUTF(),
dis.readInt(),
dis.readBoolean()
);
}
// Пример: сериализация в файл и обратно
public static void demoBinaryIO() throws IOException {
Path temp = Files.createTempFile("user", ".dat");
try (DataOutputStream dos = new DataOutputStream(Files.newOutputStream(temp))) {
writeUserBinary(dos, "Иван", 30, true);
}
try (DataInputStream dis = new DataInputStream(Files.newInputStream(temp))) {
UserBin u = readUserBinary(dis);
System.out.println(u); // UserBin[name=Иван, age=30, active=true]
}
Files.deleteIfExists(temp);
}
💡 Для сложных сценариев — рассмотрите Protocol Buffers или Apache Avro.
18. Загрузка конфигурации: java.util.Properties и fallback-логика
import java.io.*;
import java.util.Properties;
public class ConfigLoader {
private final Properties props = new Properties();
public ConfigLoader() {
// Порядок загрузки: 1) system properties → 2) env → 3) файл
props.putAll(System.getProperties());
props.putAll(System.getenv());
// Загрузка application.properties из classpath
try (InputStream is = ConfigLoader.class.getResourceAsStream("/application.properties")) {
if (is != null) {
props.load(is);
}
} catch (IOException e) {
// логируем, но не падаем — допускаем отсутствие файла
System.err.println("Не удалось загрузить application.properties: " + e.getMessage());
}
}
public String getString(String key, String defaultValue) {
return props.getProperty(key, defaultValue);
}
public int getInt(String key, int defaultValue) {
String val = props.getProperty(key);
if (val == null) return defaultValue;
try {
return Integer.parseInt(val.trim());
} catch (NumberFormatException e) {
System.err.printf("Некорректное значение для %s: '%s', используется %d%n", key, val, defaultValue);
return defaultValue;
}
}
public boolean getBoolean(String key, boolean defaultValue) {
String val = props.getProperty(key);
if (val == null) return defaultValue;
return "true".equalsIgnoreCase(val.trim()) || "1".equals(val.trim());
}
}
Файл src/main/resources/application.properties:
app.port=8080
app.debug=true
db.url=jdbc:postgresql://localhost:5432/mydb
Использование:
ConfigLoader cfg = new ConfigLoader();
int port = cfg.getInt("app.port", 8080);
boolean debug = cfg.getBoolean("app.debug", false);
19. Простой LRU-кэш на основе LinkedHashMap
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LRUCache(int maxSize) {
super(maxSize, 0.75f, true); // accessOrder = true → LRU
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
// Удобные методы
public V computeIfAbsent(K key, java.util.function.Function<? super K, ? extends V> mappingFunction) {
return super.computeIfAbsent(key, mappingFunction);
}
}
Использование:
var cache = new LRUCache<String, String>(100);
cache.put("key1", "value1");
String v = cache.computeIfAbsent("key2", k -> expensiveOperation(k));
// После 101-й вставки самый старый (по обращению) элемент удалится автоматически
⚠️ Не потокобезопасен. Для многопоточности — оберните в
Collections.synchronizedMap()или используйтеCaffeine.
20. Пагинация коллекции (без БД)
import java.util.List;
import java.util.stream.Collectors;
public record Page<T>(List<T> content, int number, int size, long totalElements) {
public int totalPages() {
return (int) Math.ceil((double) totalElements / size);
}
public boolean hasNext() { return number < totalPages() - 1; }
public boolean hasPrevious() { return number > 0; }
}
public static <T> Page<T> paginate(List<T> source, int page, int size) {
if (page < 0) throw new IllegalArgumentException("page >= 0");
if (size <= 0) throw new IllegalArgumentException("size > 0");
int fromIndex = Math.min(page * size, source.size());
int toIndex = Math.min(fromIndex + size, source.size());
List<T> content = source.subList(fromIndex, toIndex);
return new Page<>(content, page, size, source.size());
}
Использование:
List<String> items = IntStream.range(0, 150).mapToObj(i -> "Item-" + i).toList();
Page<String> p1 = paginate(items, 0, 20); // первая страница, 20 элементов
Page<String> p2 = paginate(items, 1, 20); // вторая
System.out.println("Стр. " + p1.number() + ", всего: " + p1.totalPages()); // 8
21. Graceful shutdown (корректная остановка приложения)
import java.util.concurrent.*;
public class GracefulShutdown {
private final ExecutorService executor;
private final Thread shutdownHook;
public GracefulShutdown(int poolSize) {
this.executor = Executors.newFixedThreadPool(poolSize);
this.shutdownHook = new Thread(this::shutdownNow, "shutdown-hook");
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public void submit(Runnable task) {
if (executor.isShutdown()) {
throw new RejectedExecutionException("Executor is shutting down");
}
executor.submit(task);
}
// Блокирующая остановка: ждём завершения всех задач (до таймаута)
public void shutdownAndWait(long timeoutSeconds) {
executor.shutdown();
try {
if (!executor.awaitTermination(timeoutSeconds, TimeUnit.SECONDS)) {
System.err.println("Таймаут ожидания завершения задач, принудительная остановка...");
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
// Неблокирующая остановка (через hook)
private void shutdownNow() {
System.out.println("Получен сигнал завершения — останавливаю executor...");
executor.shutdownNow();
}
// Утилита: дождаться SIGINT (Ctrl+C) вручную — полезно для CLI-приложений
public static void waitForInterrupt() {
try {
new CountDownLatch(1).await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Использование в main():
public static void main(String[] args) {
GracefulShutdown gs = new GracefulShutdown(4);
for (int i = 0; i < 10; i++) {
final int id = i;
gs.submit(() -> {
try {
Thread.sleep(2000);
System.out.println("Задача " + id + " завершена");
} catch (InterruptedException e) {
System.out.println("Задача " + id + " прервана");
Thread.currentThread().interrupt();
}
});
}
// Для CLI: ждать Ctrl+C
GracefulShutdown.waitForInterrupt();
// Или явно инициировать остановку
// gs.shutdownAndWait(5);
}
22. Валидация JSON-схем (с networknt/json-schema-validator)
Зависимость (MIT-лицензия):
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.4.2</version>
</dependency>
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
public class JsonSchemaValidator {
private static final ObjectMapper mapper = new ObjectMapper();
private final JsonSchema schema;
public JsonSchemaValidator(String schemaJson) throws Exception {
JsonNode schemaNode = mapper.readTree(schemaJson);
this.schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012)
.getSchema(schemaNode);
}
public Set<ValidationMessage> validate(String jsonData) throws Exception {
JsonNode data = mapper.readTree(jsonData);
return schema.validate(data);
}
// Удобный boolean-метод
public boolean isValid(String jsonData) {
try {
return validate(jsonData).isEmpty();
} catch (Exception e) {
return false;
}
}
}
Пример схемы (user.schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"age": { "type": "integer", "minimum": 0, "maximum": 150 }
},
"required": ["name", "age"]
}
Использование:
String schemaJson = Files.readString(Path.of("user.schema.json"));
JsonSchemaValidator validator = new JsonSchemaValidator(schemaJson);
String validJson = """{"name":"Alice","age":30}""";
System.out.println(validator.isValid(validJson)); // true
String invalidJson = """{"name":""}""";
Set<ValidationMessage> errors = validator.validate(invalidJson);
errors.forEach(err -> System.out.println(err.getMessage()));
// → $.age: is missing but it is required
// → $.name: may not be empty
23. Планирование задач: ScheduledExecutorService
import java.util.concurrent.*;
public class TaskScheduler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// Запуск задачи через фиксированную задержку (после завершения предыдущей)
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return scheduler.scheduleWithFixedDelay(task, initialDelay, delay, unit);
}
// Запуск с фиксированной частотой (по времени, даже если задача ещё не завершилась)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
}
// Однократный запуск с задержкой
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
return scheduler.schedule(task, delay, unit);
}
// Корректная остановка
public void shutdown() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
// Пример: регулярная очистка кэша
public static void demo() {
var scheduler = new TaskScheduler();
// Чистим кэш каждые 5 минут, начиная через 1 минуту
Runnable cleanCache = () -> {
System.out.println("[" + java.time.LocalTime.now() + "] Очистка кэша...");
// cache.clear();
};
scheduler.scheduleAtFixedRate(cleanCache, 1, 5, TimeUnit.MINUTES);
// Через 30 секунд — останов
scheduler.schedule(scheduler::shutdown, 30, TimeUnit.SECONDS);
}
}
24. Продвинутый CLI с picocli (аннотации, подкоманды, валидация)
Зависимость:
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.6</version>
</dependency>
a) Основной класс с подкомандами
import picocli.CommandLine;
import picocli.CommandLine.*;
@Command(name = "app", mixinStandardHelpOptions = true,
version = "app 1.0",
description = "Утилита для работы с данными",
subcommands = {
EncodeCommand.class,
DecodeCommand.class,
ConfigCommand.Show.class,
ConfigCommand.Set.class
})
public class MainCommand implements Runnable {
@Option(names = {"-v", "--verbose"}, description = "Подробный вывод")
private boolean verbose;
@Spec private CommandSpec spec;
@Override
public void run() {
if (verbose) {
System.out.println("Режим: verbose");
}
// Если вызван без подкоманды — показываем help
spec.commandLine().usage(System.out);
}
public static void main(String[] args) {
int exitCode = new CommandLine(new MainCommand()).execute(args);
System.exit(exitCode);
}
}
b) Подкоманда: кодирование Base64
@Command(name = "encode", description = "Кодирует строку в Base64")
public class EncodeCommand implements Runnable {
@Parameters(index = "0", description = "Строка для кодирования")
private String input;
@Option(names = {"-r", "--raw"}, description = "Без переносов строк")
private boolean raw;
@Override
public void run() {
String encoded = java.util.Base64.getEncoder()
.encodeToString(input.getBytes(StandardCharsets.UTF_8));
if (raw) {
System.out.print(encoded);
} else {
System.out.println(encoded);
}
}
}
c) Вложенная подкоманда: работа с конфигом
@Command(name = "config", description = "Работа с конфигурацией",
subcommands = {Show.class, Set.class})
public class ConfigCommand {
@Command(name = "show", description = "Показать значение")
public static class Show implements Runnable {
@Parameters(index = "0", description = "Ключ")
String key;
@Override
public void run() {
ConfigLoader cfg = new ConfigLoader();
System.out.println(cfg.getString(key, "<не задано>"));
}
}
@Command(name = "set", description = "Установить значение (временно, в памяти)")
public static class Set implements Runnable {
@Parameters(index = "0") String key;
@Parameters(index = "1") String value;
@Override
public void run() {
// В реальности — запись в файл или env
System.setProperty(key, value);
System.out.printf("Установлено: %s = %s%n", key, value);
}
}
}
Запуск:
java -jar app.jar encode "Привет"
# → 0J/RgNC40LLQtdGC0YHRgtCy0YPRjiDQstCw0L3QutGC0YDQsNC7
java -jar app.jar config show app.port
# → 8080
java -jar app.jar --help
# → подробная справка
25. Встроенный HTTP-сервер (com.sun.net.httpserver.HttpServer)
⚠️ Пакет
com.sun.*— не часть официального API, но присутствует во всех OpenJDK-сборках. Подходит для микросервисов, health-check, dev-серверов.
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
public class SimpleHttpServer {
public static void start(int port) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext("/ping", new PingHandler());
server.createContext("/echo", new EchoHandler());
server.createContext("/users", new UsersHandler());
// Пул потоков для обработки запросов
server.setExecutor(Executors.newFixedThreadPool(4));
server.start();
System.out.println("HTTP-сервер запущен на порту " + port);
}
static class PingHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "{\"status\":\"ok\",\"ts\":" + System.currentTimeMillis() + "}";
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
exchange.sendResponseHeaders(200, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
exchange.close();
}
}
static class EchoHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String query = exchange.getRequestURI().getQuery();
String body = exchange.getRequestBody().readAllBytes().length > 0 ?
new String(exchange.getRequestBody().readAllBytes()) : "";
String response = String.format(
"{\"method\":\"%s\",\"path\":\"%s\",\"query\":\"%s\",\"body\":\"%s\"}",
exchange.getRequestMethod(),
exchange.getRequestURI().getPath(),
query == null ? "" : query,
body
);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(200, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
exchange.close();
}
}
// Пример: GET /users → JSON-массив
static class UsersHandler implements HttpHandler {
private static final String USERS_JSON = """
[{"id":1,"name":"Алиса"},{"id":2,"name":"Боб"}]
""";
@Override
public void handle(HttpExchange exchange) throws IOException {
if (!"GET".equals(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
exchange.close();
return;
}
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
byte[] body = USERS_JSON.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
exchange.close();
}
}
}
Запуск:
public static void main(String[] args) throws Exception {
SimpleHttpServer.start(8080);
GracefulShutdown.waitForInterrupt(); // из предыдущего примера
}
26. Работа с ByteBuffer (NIO, эффективная обработка бинарных данных)
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
// Чтение из массива байт с извлечением int, string, double
public static void demoByteBuffer() {
byte[] data = new byte[100];
ByteBuffer buf = ByteBuffer.wrap(data);
// Запись
buf.putInt(12345);
buf.put("Привет".getBytes(StandardCharsets.UTF_8));
buf.putDouble(3.14159);
// Сброс позиции для чтения
buf.flip();
// Чтение в том же порядке
int i = buf.getInt(); // 12345
byte[] strBytes = new byte[12]; // "Привет" в UTF-8 — 12 байт
buf.get(strBytes);
String s = new String(strBytes, StandardCharsets.UTF_8); // "Привет"
double d = buf.getDouble(); // 3.14159
System.out.printf("i=%d, s='%s', d=%.5f%n", i, s, d);
}
// Чтение из файла с прямым буфером (off-heap)
public static void readWithDirectBuffer(Path file) throws IOException {
try (var ch = FileChannel.open(file, StandardOpenOption.READ)) {
ByteBuffer buf = ByteBuffer.allocateDirect(4096);
while (ch.read(buf) != -1) {
buf.flip();
// Обработка данных в buf
while (buf.hasRemaining()) {
byte b = buf.get();
// ...
}
buf.clear();
}
}
}
💡
allocateDirect()— для тяжёлых I/O-нагрузок (меньше копий между Java heap и native memory).
27. Генерация Excel-отчётов (Apache POI — .xlsx)
Зависимость:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.3.0</version>
</dependency>
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.time.LocalDateTime;
import java.util.List;
public record ReportRow(String name, int value, LocalDateTime timestamp) {}
public static void writeExcelReport(Path file, List<ReportRow> rows) throws Exception {
try (Workbook wb = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(file.toFile())) {
Sheet sheet = wb.createSheet("Отчёт");
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("Имя");
header.createCell(1).setCellValue("Значение");
header.createCell(2).setCellValue("Время");
CellStyle timestampStyle = wb.createCellStyle();
CreationHelper helper = wb.getCreationHelper();
timestampStyle.setDataFormat(helper.createDataFormat().getFormat("yyyy-mm-dd hh:mm:ss"));
for (int i = 0; i < rows.size(); i++) {
ReportRow r = rows.get(i);
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(r.name());
row.createCell(1).setCellValue(r.value());
Cell tsCell = row.createCell(2);
tsCell.setCellValue(r.timestamp());
tsCell.setCellStyle(timestampStyle);
}
// Автоподбор ширины
for (int i = 0; i < 3; i++) sheet.autoSizeColumn(i);
wb.write(fos);
}
}
28. Генерация PDF-документов (iText 7 Core — AGPL, free for dev)
Зависимость:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.5</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.5</version>
</dependency>
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.TextAlignment;
public static void writePdfReport(Path file, List<ReportRow> rows) throws Exception {
try (PdfWriter writer = new PdfWriter(file.toFile());
PdfDocument pdf = new PdfDocument(writer);
Document doc = new Document(pdf)) {
doc.add(new Paragraph("Отчёт по данным").setTextAlignment(TextAlignment.CENTER).setBold());
Table table = new Table(3);
table.addHeaderCell("Имя");
table.addHeaderCell("Значение");
table.addHeaderCell("Время");
for (ReportRow r : rows) {
table.addCell(r.name());
table.addCell(String.valueOf(r.value()));
table.addCell(r.timestamp().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
doc.add(table);
}
}
29. Базовая интеграция с OAuth2 (Client Credentials Flow)
import java.net.http.*;
import java.net.URI;
import java.time.Duration;
import java.util.Base64;
public class OAuth2Client {
private final String tokenUrl;
private final String clientId;
private final String clientSecret;
private final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
private String cachedToken = null;
private long tokenExpiry = 0;
public OAuth2Client(String tokenUrl, String clientId, String clientSecret) {
this.tokenUrl = tokenUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
// Получение access_token (кэшируется до истечения срока)
public synchronized String getAccessToken() throws Exception {
long now = System.currentTimeMillis();
if (cachedToken != null && now < tokenExpiry) {
return cachedToken;
}
String credentials = clientId + ":" + clientSecret;
String authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes());
String formData = "grant_type=client_credentials";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(tokenUrl))
.header("Authorization", authHeader)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(formData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("OAuth2 token request failed: " + response.statusCode());
}
// Парсим JSON: {"access_token":"abc","expires_in":3600}
var json = new org.json.JSONObject(response.body());
cachedToken = json.getString("access_token");
int expiresIn = json.getInt("expires_in");
tokenExpiry = now + (expiresIn - 60) * 1000L; // минус 60 сек — запас
return cachedToken;
}
// Пример защищённого вызова
public String callApi(String apiUrl) throws Exception {
String token = getAccessToken();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.header("Authorization", "Bearer " + token)
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("API call failed: " + response.statusCode());
}
return response.body();
}
}
30. Мониторинг и метрики: Micrometer Core (standalone)
Зависимость:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.13.5</version>
</dependency>
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
public class MetricsSetup {
private final MeterRegistry registry;
public MetricsSetup() {
this.registry = new SimpleMeterRegistry(); // или StatsConfig, Prometheus — ниже
// Регистрация системных метрик
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
}
// Создание счётчика
public Counter createCounter(String name, String... tags) {
return Counter.builder(name)
.tags(tags)
.register(registry);
}
// Создание таймера (для замера длительности)
public Timer createTimer(String name, String... tags) {
return Timer.builder(name)
.tags(tags)
.register(registry);
}
// Гейдж — для отслеживания текущего состояния (например, размер очереди)
public void bindGauge(String name, Supplier<Number> supplier, String... tags) {
Gauge.builder(name, supplier)
.tags(tags)
.register(registry);
}
// Экспорт в Prometheus-формате (по HTTP или в файл)
public String scrapePrometheus() {
return io.micrometer.prometheus.PrometheusMeterRegistry prom =
new io.micrometer.prometheus.PrometheusMeterRegistry(
new io.micrometer.prometheus.PrometheusConfig() {
@Override public String get(String key) { return null; }
@Override public Duration step() { return Duration.ofSeconds(10); }
});
prom.add(registry); // копируем метрики
return prom.scrape();
}
// Пример использования
public static void demo() {
MetricsSetup ms = new MetricsSetup();
Counter requests = ms.createCounter("http.requests", "status", "200");
Timer processTimer = ms.createTimer("data.processing");
// Симуляция
for (int i = 0; i < 5; i++) {
requests.increment();
processTimer.record(() -> {
try { Thread.sleep(50 + (long)(Math.random() * 100)); } catch (InterruptedException e) {}
});
}
System.out.println(ms.scrapePrometheus());
// → # HELP http_requests
// → http_requests{status="200",} 5.0
// → # HELP data_processing_seconds_max
// → data_processing_seconds_max 0.147
}
}
31. Валидация XML по XSD (встроенный SchemaFactory)
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.InputStream;
public class XmlValidator {
private final Validator validator;
public XmlValidator(InputStream xsdStream) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(xsdStream));
this.validator = schema.newValidator();
}
public void validate(InputStream xmlStream) throws Exception {
validator.validate(new StreamSource(xmlStream));
}
public boolean isValid(InputStream xmlStream) {
try {
validate(xmlStream);
return true;
} catch (Exception e) {
System.err.println("XML invalid: " + e.getMessage());
return false;
}
}
}
Пример XSD (user.xsd):
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="user">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:positiveInteger"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
Использование:
try (InputStream xsd = XmlValidator.class.getResourceAsStream("/user.xsd");
InputStream xml = XmlValidator.class.getResourceAsStream("/user-valid.xml")) {
XmlValidator validator = new XmlValidator(xsd);
System.out.println(validator.isValid(xml)); // true/false
}
32. Сериализация/десериализация через Jackson (без аннотаций, конфигурируемо)
Зависимость:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class JacksonUtils {
private static final ObjectMapper MAPPER = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// Сериализация любого объекта в JSON
public static String toJson(Object obj) {
try {
return MAPPER.writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException("JSON serialization failed", e);
}
}
// Десериализация с заданным типом (TypeReference для generic-ов)
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return MAPPER.readValue(json, clazz);
} catch (Exception e) {
throw new RuntimeException("JSON deserialization failed for " + clazz, e);
}
}
public static <T> T fromJson(String json, TypeReference<T> typeRef) {
try {
return MAPPER.readValue(json, typeRef);
} catch (Exception e) {
throw new RuntimeException("JSON deserialization failed", e);
}
}
// Утилита: безопасное чтение (возвращает Optional.empty() при ошибке)
public static <T> Optional<T> tryFromJson(String json, Class<T> clazz) {
try {
return Optional.of(MAPPER.readValue(json, clazz));
} catch (Exception e) {
return Optional.empty();
}
}
}
Использование:
record Event(String type, LocalDateTime ts, Map<String, Object> payload) {}
Event ev = new Event("login", LocalDateTime.now(), Map.of("user", "alice"));
String json = JacksonUtils.toJson(ev);
// → {"type":"login","ts":"2025-11-13T14:30:00","payload":{"user":"alice"}}
Event restored = JacksonUtils.fromJson(json, Event.class);
List<Event> events = JacksonUtils.fromJson("[...]", new TypeReference<>() {});
33. WebSocket-клиент и сервер (JSR 356 — javax.websocket)
Требуется реализация, например Tyrus:
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly-server</artifactId>
<version>2.1.5</version>
</dependency>
a) Сервер
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/echo")
public class EchoEndpoint {
private static final Set<Session> sessions = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session) {
sessions.add(session);
System.out.println("Соединение открыто: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session session) {
String response = "Echo: " + message + " (от " + session.getId() + ")";
sessions.forEach(s -> {
try { s.getBasicRemote().sendText(response); }
catch (Exception e) { e.printStackTrace(); }
});
}
@OnClose
public void onClose(Session session) {
sessions.remove(session);
System.out.println("Соединение закрыто: " + session.getId());
}
@OnError
public void onError(Session session, Throwable error) {
System.err.println("Ошибка WS: " + error.getMessage());
}
}
Запуск сервера:
import org.glassfish.tyrus.server.Server;
public class WsServer {
public static void main(String[] args) throws Exception {
Server server = new Server("localhost", 8025, "/ws", null, EchoEndpoint.class);
server.start();
System.out.println("WS-сервер: ws://localhost:8025/ws/echo");
GracefulShutdown.waitForInterrupt();
server.stop();
}
}
b) Клиент (standalone)
import javax.websocket.*;
import java.net.URI;
public class WsClient {
public static void connectAndSend(String uri, String message) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(new ClientEndpoint(), URI.create(uri));
session.getBasicRemote().sendText(message);
Thread.sleep(2000);
session.close();
}
@ClientEndpoint
public static class ClientEndpoint {
@OnMessage
public void onMessage(String msg) {
System.out.println("Получено от сервера: " + msg);
}
}
}
34. Работа с Kafka (без Spring — kafka-clients)
Зависимость:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.8.0</version>
</dependency>
a) Producer
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerExample {
public static void send(String bootstrapServers, String topic, String key, String value) {
Properties props = new Properties();
props.put("bootstrap.servers", bootstrapServers);
props.put("key.serializer", StringSerializer.class);
props.put("value.serializer", StringSerializer.class);
props.put("acks", "1");
props.put("retries", 3);
props.put("enable.idempotence", true);
try (Producer<String, String> producer = new KafkaProducer<>(props)) {
ProducerRecord<String, String> record = new ProducerRecord<>(topic, key, value);
producer.send(record, (metadata, exception) -> {
if (exception != null) {
System.err.println("Ошибка отправки: " + exception.getMessage());
} else {
System.out.printf("Отправлено: topic=%s, partition=%d, offset=%d%n",
metadata.topic(), metadata.partition(), metadata.offset());
}
});
producer.flush();
}
}
}
b) Consumer (с ручным управлением offset)
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
public class KafkaConsumerExample {
public static void poll(String bootstrapServers, String groupId, String topic) {
Properties props = new Properties();
props.put("bootstrap.servers", bootstrapServers);
props.put("group.id", groupId);
props.put("key.deserializer", StringDeserializer.class);
props.put("value.deserializer", StringDeserializer.class);
props.put("auto.offset.reset", "earliest");
props.put("enable.auto.commit", false); // ручной коммит
try (Consumer<String, String> consumer = new KafkaConsumer<>(props)) {
consumer.subscribe(Collections.singletonList(topic));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("Получено: [%s] key=%s, value=%s%n",
record.topic(), record.key(), record.value());
}
if (!records.isEmpty()) {
consumer.commitSync(); // синхронный коммит
}
}
}
}
}
35. Распределённая блокировка через Redis (Jedis + Lua)
Зависимость:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.3</version>
</dependency>
import redis.clients.jedis.Jedis;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RedisDistributedLock {
private final Jedis jedis;
private final String lockKey;
private final String lockValue; // уникальный идентификатор владельца
private boolean acquired = false;
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
public RedisDistributedLock(Jedis jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
this.lockValue = UUID.randomUUID().toString();
}
// Блокирующая попытка захвата (с таймаутом)
public boolean lock(long waitTimeMs, long leaseTimeMs) throws InterruptedException {
long deadline = System.currentTimeMillis() + waitTimeMs;
while (System.currentTimeMillis() < deadline) {
// SET key value NX PX
String reply = jedis.set(lockKey, lockValue, "NX", "PX", leaseTimeMs);
if ("OK".equals(reply)) {
acquired = true;
return true;
}
Thread.sleep(50);
}
return false;
}
// Неблокирующая попытка
public boolean tryLock(long leaseTimeMs) {
String reply = jedis.set(lockKey, lockValue, "NX", "PX", leaseTimeMs);
acquired = "OK".equals(reply);
return acquired;
}
// Освобождение (только своим владельцем)
public void unlock() {
if (acquired) {
jedis.eval(UNLOCK_SCRIPT, 1, lockKey, lockValue);
acquired = false;
}
}
// Поддержка try-with-resources
public void close() {
unlock();
}
}
Использование:
try (Jedis jedis = new Jedis("localhost", 6379);
RedisDistributedLock lock = new RedisDistributedLock(jedis, "critical-section")) {
if (lock.lock(2000, 10000)) { // ждать до 2 сек, удерживать 10 сек
// критическая секция
System.out.println("Выполняю защищённую операцию...");
Thread.sleep(1000);
} else {
System.err.println("Не удалось захватить блокировку");
}
}
36. Генерация UML-диаграмм из кода (PlantUML + Java-анализ)
Зависимость:
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>1.2024.8</version>
</dependency>
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class PlantUmlGenerator {
// Генерация диаграммы по PlantUML-коду
public static void generateDiagram(String plantUmlCode, Path outputPath) throws Exception {
SourceStringReader reader = new SourceStringReader(plantUmlCode);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
reader.outputImage(baos, new FileFormatOption(FileFormat.PNG));
Files.write(outputPath, baos.toByteArray());
}
}
// Простая генерация класс-диаграммы по списку классов (на основе reflection)
public static String generateClassDiagram(List<Class<?>> classes) {
StringBuilder sb = new StringBuilder("@startuml\n");
sb.append("hide empty members\n");
for (Class<?> cls : classes) {
sb.append("class ").append(cls.getSimpleName()).append(" {\n");
for (java.lang.reflect.Field f : cls.getDeclaredFields()) {
String type = f.getType().getSimpleName();
String name = f.getName();
sb.append(" + ").append(type).append(" ").append(name).append("\n");
}
sb.append("}\n");
}
// Пример наследования
for (Class<?> cls : classes) {
Class<?> sup = cls.getSuperclass();
if (sup != null && sup != Object.class) {
sb.append(sup.getSimpleName()).append(" <|-- ").append(cls.getSimpleName()).append("\n");
}
}
sb.append("@enduml");
return sb.toString();
}
}
Использование:
String puml = PlantUmlGenerator.generateClassDiagram(List.of(User.class, Admin.class));
PlantUmlGenerator.generateDiagram(puml, Path.of("classes.png"));
// → генерирует PNG-файл с диаграммой