← Назад к вопросам

Есть ли вредные привычки

1.2 Junior🔥 61 комментариев
#Soft Skills и карьера

Комментарии (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

Заключение: Основные вредные привычки

  1. null вместо Optional → Используй Optional
  2. Ignore exceptions → Логируй и обрабатывай правильно
  3. Catch Exception → Лови специфичные исключения
  4. Resource leaks → Try-with-resources
  5. Огромные synchronized блоки → Минимальная защита
  6. Copy-paste код → Abstraction и DRY
  7. String вместо Enum → Type-safe Enum
  8. Наследование вместо композиции → Preferir composition
  9. Side effects в Stream → Чистые функции
  10. Огромные методы → SRP — один метод = одна задача
  11. Нет тестов → Unit testing everywhere
  12. No API versioning → Версионируй API

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