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

В какой момент класс загружается приложением

2.0 Middle🔥 141 комментариев
#JVM и управление памятью#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В какой момент класс загружается приложением?

Класс загружается в момент первого использования, а не при запуске приложения. Java использует ленивую загрузку (lazy loading) классов через механизм ClassLoader.

Фазы загрузки класса

Загрузка класса состоит из трёх этапов:

1. Loading (загрузка бинарного кода)

// ClassLoader читает .class файл и создает Class объект
public class MyClass {
    public void method() { }
}

// MyClass загружается когда:
// 1. new MyClass()  - явное создание
// 2. MyClass.staticMethod()  - вызов статического метода
// 3. Class.forName("MyClass")  - рефлексия

2. Linking (связывание)

  • Проверка (Verification) - проверка корректности bytecode
  • Подготовка (Preparation) - выделение памяти для статических полей
  • Разрешение (Resolution) - преобразование символических ссылок в реальные адреса

3. Initialization (инициализация)

public class Example {
    static {  // статический инициализатор
        System.out.println("Class initialized");
    }
    
    private static int counter = 0;  // инициализируется здесь
}

Когда класс загружается

public class Main {
    public static void main(String[] args) {
        // ClassA НЕ загружена на этом этапе
        
        ClassA obj = new ClassA();  // ЗДЕСЬ загружается ClassA
        // 1. ClassLoader читает ClassA.class
        // 2. Проверяет, валиден ли bytecode
        // 3. Выделяет память для статических полей
        // 4. Выполняет статические инициализаторы
        // 5. Создает экземпляр
    }
}

public class ClassA {
    private static final String NAME = "ClassA";  // инициализируется при загрузке
    
    static {
        System.out.println("ClassA loaded!");  // выполняется при загрузке
    }
}

Иерархия загрузки классов

┌─────────────────────────────┐
│  Bootstrap ClassLoader      │ (ядро Java - Object, String, etc)
└──────────┬──────────────────┘
           │
┌──────────▼──────────────────┐
│  Extension ClassLoader      │ (классы из ext/)
└──────────┬──────────────────┘
           │
┌──────────▼──────────────────┐
│  Application ClassLoader    │ (наш код из classpath)
└─────────────────────────────┘

Примеры моментов загрузки

1. Явное создание объекта

MyClass obj = new MyClass();  // MyClass загружается здесь

2. Вызов статического метода

Math.sqrt(4);  // Math класс загружается при первом вызове

3. Доступ к статическому полю

System.out.println(Integer.MAX_VALUE);  // Integer загружается

4. Использование class literal

Class<?> clazz = String.class;  // String загружается

5. Рефлексия

Class<?> clazz = Class.forName("java.util.ArrayList");
// ArrayList загружается через рефлексию

6. instanceof оператор

if (obj instanceof MyClass) {  // MyClass загружается
    // работа
}

Порядок загрузки в наследовании

public class Parent {
    static {
        System.out.println("Parent loaded");
    }
}

public class Child extends Parent {
    static {
        System.out.println("Child loaded");
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        // Вывод:
        // Parent loaded
        // Child loaded
        // (родитель загружается первым!)
    }
}

Статические инициализаторы

public class Database {
    private static Connection conn;
    
    static {  // выполняется при загрузке класса
        System.out.println("Инициализируем базу данных...");
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
            System.out.println("База данных инициализирована");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection() {
        return conn;
    }
}

public class Main {
    public static void main(String[] args) {
        // Database класс еще не загружен
        System.out.println("Приложение стартовало");
        
        // ЗДЕСЬ Database загружается и выполняется статический блок
        Connection conn = Database.getConnection();
    }
}

Ленивая загрузка vs Eager загрузка

Lazy (по умолчанию в Java)

public class LazyExample {
    public static void main(String[] args) {
        // HeavyClass не загружена
        
        if (someCondition) {
            HeavyClass obj = new HeavyClass();  // загружается только если нужна
        }
    }
}

Eager (явное предзагрузка)

public class EagerExample {
    static {
        // Явно загружаем класс при загрузке EagerExample
        try {
            Class.forName("com.example.HeavyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Время загрузки класса

public class LoadingTiming {
    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        
        // В этот момент MyClass еще не загружена
        System.out.println("Starting...");
        
        // ЗДЕСЬ MyClass загружается (может быть задержка 10-100мс)
        MyClass obj = new MyClass();
        
        long duration = System.currentTimeMillis() - start;
        System.out.println("First use took: " + duration + "ms");
        
        // Второе использование
        start = System.currentTimeMillis();
        MyClass obj2 = new MyClass();
        duration = System.currentTimeMillis() - start;
        System.out.println("Second use took: " + duration + "ms");  // намного быстрее
    }
}

Проблемы с загрузкой классов

1. ClassNotFoundException

try {
    Class.forName("com.nonexistent.MyClass");
} catch (ClassNotFoundException e) {
    // Класс не найден в classpath
    e.printStackTrace();
}

2. NoClassDefFoundError (link time)

public class Main {
    public static void main(String[] args) {
        MissingClass obj = new MissingClass();  // NoClassDefFoundError
    }
}

3. Циклические зависимости при инициализации

public class ClassA {
    static {
        ClassB.methodB();  // ClassB начинает загружаться
    }
}

public class ClassB {
    static {
        ClassA.methodA();  // ClassA уже загружается - может быть проблема
    }
}

ClassLoader иерархия

public class ClassLoaderExample {
    public static void main(String[] args) {
        // Получить ClassLoader для класса
        ClassLoader loader = String.class.getClassLoader();
        System.out.println(loader);  // null (bootstrap)
        
        ClassLoader loader2 = ArrayList.class.getClassLoader();
        System.out.println(loader2);  // java.base
        
        ClassLoader loader3 = Main.class.getClassLoader();
        System.out.println(loader3);  // sun.misc.Launcher$AppClassLoader
        
        // Родитель загрузчика
        System.out.println(loader3.getParent());  // sun.misc.Launcher$ExtClassLoader
    }
}

Оптимизация загрузки

Избегайте загрузки всех классов при старте

// Плохо: загружает все классы при инициализации
public class HeavyInitializer {
    static {
        loadAllClasses();  // все классы в памяти сразу
    }
}

// Хорошо: загружайте только когда нужно
public class LazyInitializer {
    public static Object getInstance(String className) throws ClassNotFoundException {
        return Class.forName(className).newInstance();
    }
}

Вывод

Класс загружается в момент первого использования, не при запуске приложения. Это происходит через:

  1. ClassLoader - берет .class файл
  2. Verification - проверяет корректность
  3. Linking - подготавливает память
  4. Initialization - выполняет статические блоки

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