Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Java разработчик: Вредные привычки, которые убивают качество кода
Да, есть множество вредных привычек в Java разработке, которые я видел у многих (и сам них когда-то делал). Давайте их рассмотрим и обсудим, как от них избавиться.
1. Использование null вместо Optional
// ❌ ВРЕДНАЯ ПРИВЫЧКА
public User findUser(String id) {
return userRepository.findById(id); // может вернуть null
}
public void processUser(String id) {
User user = findUser(id);
if (user != null) { // Проверка null везде
System.out.println(user.getName());
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД
public Optional<User> findUser(String id) {
return userRepository.findById(id);
}
public void processUser(String id) {
findUser(id)
.ifPresent(user -> System.out.println(user.getName()));
}
Проблемы null:
- NullPointerException везде
- Сложно отследить, где может быть null
- Больше проверок = больше кода
2. Игнорирование исключений (Silent Fail)
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Silent fail
public void saveData(Data data) {
try {
repository.save(data);
} catch (IOException e) {
// Ничего не делаем! Проблема скрывается
}
}
// ❌ ЕЩЁ ХУЖЕ
public void processFile() {
try {
readFile();
} catch (Exception e) {
// Что-то упало, но мы не знаем что
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД
public void saveData(Data data) throws IOException {
repository.save(data); // Пробросим выше
}
// ✅ ИЛИ логируй и обрабатывай
public void processFile() {
try {
readFile();
} catch (IOException e) {
logger.error("Failed to read file", e); // Логируем с контекстом
throw new RuntimeException("Processing failed", e);
}
}
3. Catch Exception вместо специфичных исключений
// ❌ ВРЕДНАЯ ПРИВЫЧКА
public void process() {
try {
readFile();
parseData();
saveDatabase();
} catch (Exception e) { // Ловим всё!
System.out.println("Error");
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД
public void process() throws IOException, ParseException {
readFile(); // IOException
parseData(); // ParseException
saveDatabase(); // DatabaseException
}
// ИЛИ специфичные обработчики
public void process() {
try {
readFile();
} catch (IOException e) {
logger.error("File not found", e);
}
try {
parseData();
} catch (ParseException e) {
logger.error("Invalid format", e);
}
}
4. Забывчивость про ресурсы (Resource Leak)
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Утечка ресурсов
public String readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
BufferedReader br = new BufferedReader(reader);
String line = br.readLine();
// Если выше выпадет exception — reader и br не закроются!
br.close();
reader.close();
return line;
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Try-with-resources
public String readFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
// Автоматически закроется, даже если выпадет exception
}
}
// ✅ ИЛИ с базой данных
public void queryDatabase() throws SQLException {
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
// Всё закроется автоматически
}
}
5. Чрезмерное использование synchronized
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Огромные synchronized блоки
public class Service {
private int counter = 0;
public synchronized void process(Data data) { // ВСЁ синхронизировано!
counter++;
expensiveNetworkCall(); // 10 секунд
logData(data); // 5 секунд
updateDatabase(data); // 3 секунды
counter--;
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Минимальная защита
public class Service {
private int counter = 0;
private final Object lock = new Object();
public void process(Data data) {
synchronized (lock) {
counter++; // ТОЛЬКО счётчик
}
expensiveNetworkCall();
logData(data);
updateDatabase(data);
synchronized (lock) {
counter--;
}
}
}
// ✅ ЕЩЁ ЛУЧШЕ: Atomic вместо synchronized
private AtomicInteger counter = new AtomicInteger(0);
public void process(Data data) {
counter.incrementAndGet();
expensiveNetworkCall();
logData(data);
updateDatabase(data);
counter.decrementAndGet();
}
6. Copy-Paste вместо abstraction
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Копируем код везде
public class UserService {
public User getUser(String id) {
try {
User user = userRepository.findById(id);
if (user == null) throw new NotFoundException();
logger.info("User found");
return user;
} catch (Exception e) {
logger.error("Failed to get user", e);
throw new RuntimeException(e);
}
}
}
public class OrderService {
public Order getOrder(String id) {
try {
Order order = orderRepository.findById(id);
if (order == null) throw new NotFoundException(); // КОПИЯ!
logger.info("Order found");
return order;
} catch (Exception e) {
logger.error("Failed to get order", e);
throw new RuntimeException(e);
}
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Extract common logic
public abstract class BaseService<T> {
protected abstract Repository<T> getRepository();
public T getById(String id) {
T entity = getRepository().findById(id)
.orElseThrow(NotFoundException::new);
logger.info("{} found", entity.getClass().getSimpleName());
return entity;
}
}
public class UserService extends BaseService<User> {
@Override
protected Repository<User> getRepository() {
return userRepository;
}
}
7. Неправильное использование String вместо Enum
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Строки везде
public class Order {
private String status; // Может быть что угодно: "pending", "PENDING", "pending ", null
public void processOrder() {
if (status.equals("pending")) { // Опасно!
// обработка
}
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Enum
public enum OrderStatus {
PENDING,
PROCESSING,
COMPLETED,
CANCELLED
}
public class Order {
private OrderStatus status; // Type-safe
public void processOrder() {
if (status == OrderStatus.PENDING) {
// обработка
}
}
}
8. Неправильное наследование (IS-A vs HAS-A)
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Наследование вместо композиции
public class Stack extends Vector { // ❌ Stack НЕ должен наследовать Vector
// Теперь у Stack есть все методы Vector, включая remove(0), get(5), etc
// Нарушение принципа Liskov Substitution
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Композиция
public class Stack<T> {
private List<T> items = new ArrayList<>(); // HAS-A, не IS-A
public void push(T item) {
items.add(item);
}
public T pop() {
return items.remove(items.size() - 1);
}
}
9. Mutating в Stream
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Side effects в Stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.map(n -> {
System.out.println(n); // Side effect!
return n * 2;
})
.forEach(System.out::println);
// ❌ ИЛИ мутирование переменной извне
List<Integer> result = new ArrayList<>();
numbers.stream()
.forEach(n -> result.add(n * 2)); // Плохо!
// ✅ ПРАВИЛЬНЫЙ ПОДХОД
List<Integer> result = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
// ✅ Отладка через peek
numbers.stream()
.peek(System.out::println)
.map(n -> n * 2)
.collect(Collectors.toList());
10. Огромные методы (God Methods)
// ❌ ВРЕДНАЯ ПРИВЫЧКА: 500 строк кода в одном методе
public void processOrder(Order order) {
// Валидация
// Расчёт цены
// Проверка склада
// Создание счёта
// Отправка письма
// Логирование
// Всё в одном методе!
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Single Responsibility
public void processOrder(Order order) {
validateOrder(order);
calculatePrice(order);
checkInventory(order);
createInvoice(order);
sendConfirmationEmail(order);
logOrderProcessing(order);
}
private void validateOrder(Order order) { ... }
private void calculatePrice(Order order) { ... }
private void checkInventory(Order order) { ... }
private void createInvoice(Order order) { ... }
private void sendConfirmationEmail(Order order) { ... }
private void logOrderProcessing(Order order) { ... }
11. Недостаточное тестирование
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Нет тестов
public class PaymentService {
public boolean processPayment(Order order) {
// Критичный бизнес-код без тестов
}
}
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Тесты везде
@Test
public void testProcessPaymentSuccess() {
Order order = new Order(100.0);
boolean result = paymentService.processPayment(order);
assertTrue(result);
}
@Test
public void testProcessPaymentInsufficientFunds() {
Order order = new Order(1000000.0);
assertThrows(InsufficientFundsException.class,
() -> paymentService.processPayment(order));
}
12. Игнорирование версионирования API
// ❌ ВРЕДНАЯ ПРИВЫЧКА: Разломали API для всех
// v1: GET /api/users/{id} -> {"id": 1, "name": "John"}
// v2: GET /api/users/{id} -> {"userId": 1, "fullName": "John"} // Сломали старый код!
// ✅ ПРАВИЛЬНЫЙ ПОДХОД: Версионирование
// GET /api/v1/users/{id} -> {"id": 1, "name": "John"}
// GET /api/v2/users/{id} -> {"userId": 1, "fullName": "John"} // Новые клиенты используют v2
Заключение: Основные вредные привычки
- null вместо Optional → Используй Optional
- Ignore exceptions → Логируй и обрабатывай правильно
- Catch Exception → Лови специфичные исключения
- Resource leaks → Try-with-resources
- Огромные synchronized блоки → Минимальная защита
- Copy-paste код → Abstraction и DRY
- String вместо Enum → Type-safe Enum
- Наследование вместо композиции → Preferir composition
- Side effects в Stream → Чистые функции
- Огромные методы → SRP — один метод = одна задача
- Нет тестов → Unit testing everywhere
- No API versioning → Версионируй API
Основной принцип: Думай о будущих разработчиках (и о себе через месяц). Пиши код, который легко читать, поддерживать и расширять.