Серия JVM (4) — Подробное объяснение механизма загрузки классов JVM

Java задняя часть JVM Bootstrap

предисловие

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

текст

(1) Что такое загрузчик классов

Короче говоря, загрузчик классов используется для.classв файлеинформация о байт-кодеперевести на конкретный的java.lang.Classобъектинструменты для процесса.

Конкретный процесс:

  1. Во время фактического процесса загрузки классаJVMбудет все.classв файле байт-кодадвоичные данныеЧтение в память и импорт в область данных времени выполненияобласть методасередина.
  2. при первом вызове классаАктивная загрузкаилипассивная загрузкаКогда загрузчик класса выполняет процесс загрузки класса для этого класса -нагрузка,соединять(проверять,Подготовить,Разобрать),инициализация.
  3. Если класс загружен успешно,куча памятисоздаст новыйClassобъект,ClassОбъект инкапсулирует класс вобласть методавнутриструктура данных.

ClassОписание процесса создания объекта:

(2) Процесс загрузки класса

Процесс загрузки класса делится на три шага (пять этапов):нагрузка -> соединять(проверять,Подготовить,Разобрать) ->инициализация.

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

Описание процесса загрузки класса:

1. Загрузить

load: найти и загрузить классдвоичные данныепроцесс.

Описание процесса загрузки:

  1. по классамполное имядолжность.classфайл и получить егодвоичный поток байтов.
  2. поток байтов, представленныйстатическая структура храненияпреобразовать вобласть методаизструктуры данных времени выполнения.
  3. существуетJavaкучасоздать такойjava.lang.Classобъект, как данные в области методаподъезд.

2. Подключение

Соединения: включеныпроверять,Подготовить,РазобратьТри шага.

а) проверить

Проверка: убедитесь, что загруженный классправильность. Аутентификация является первым шагом на этапе подключения и используется для обеспеченияClassСоответствует ли информация в потоке байтов требованиям виртуальной машины.

Конкретная форма проверки:

  1. проверка формата файла: Убедитесь, что поток байтов соответствуетClassСпецификация формата файла, например, следует ли начинать с0xCAFEBABEStart, находятся ли основной и дополнительный номера версий в пределах диапазона обработки текущей виртуальной машины и имеют ли константы в пуле констант неподдерживаемые типы.
  2. проверка метаданных: семантический анализ информации, описываемой байт-кодом (примечание: отличиеjavacсемантический анализ на этапе компиляции), чтобы убедиться, что описываемая им информация соответствует требованиям спецификации языка Java; например: имеет ли этот класс родительский класс, за исключениемjava.lang.Objectза пределами.
  3. Проверка байт-кода: посредством анализа потока данных и потока управления определено, что семантика программы является законной и логичной.
  4. Проверка символьной ссылки: убедитесь, что действие синтаксического анализа выполняется правильно.

б) Подготовить

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

Конкретное поведение:

  1. В настоящее время выделение памяти включает толькопеременная класса(static), не включаяпеременная экземпляра,переменная экземплярабудет в объектесоздавать экземплярКогда объект выделяется кусками вJavaкучасередина.
  2. установить здесьПервоначальный значениеОбычно тип данныхнулевое значение по умолчанию(как0,0L,null,falseд.), а не бытьJavaв кодеявное присвоение.

в) Анализ

Анализ: поставьте класс правильнопостоянный пулвнутриСимволическая ссылкапреобразовать впрямая цитата.

Действие анализа в основном длякласс или интерфейс,поле,метод класса,метод интерфейса,тип метода,дескриптор методаиквалификатор места вызоваи т.д. Выполняется 7 видов символьных ссылок.

3. Инициализировать

инициализация: дастатическая переменная классаприсвоить правильные начальные значения (отметить и соединитьпроцесс разборадифференцировать).

инициализированная цель

  1. реализует декларациюстатическая переменная классаинициализация заданного начального значения;
  2. реализация для использованиястатический блок кодаИнициализация установленного начального значения.

шаги инициализации

  1. Если этот класс ненагрузка,соединять, то сначаланагрузка,соединятьтакой;
  2. если такойпрямой родительский классне был инициализирован, первыйинициализацияего непосредственный родительский класс;
  3. если в классе естьоператор инициализации, операторы инициализации выполняются последовательно.

время для инициализации

  1. Создайте экземпляр класса (newключевое слово);
  2. java.lang.reflectметоды в пакете (например:Class.forName(“xxx”));
  3. к классустатическая переменнаясделать доступ или назначение;
  4. доступ к вызывающему классустатический метод;
  5. инициализировать классПодкласс,отецсам также инициализируется;
  6. как программазагрузочная запись,Включаютmainметод (например:SpringBootначальный класс).

(3) Активная ссылка и пассивная ссылка класса

Активное цитирование

Активная ссылка: на этапе загрузки класса выполнять тольконагрузка,соединятьдействовать, неинициализацияработать.

Несколько форм активного цитирования

  1. Создайте экземпляр класса (newключевое слово);
  2. java.lang.reflectметоды в пакете (например:Class.forName(“xxx”));
  3. к классустатическая переменнаясделать доступ или назначение;
  4. доступ к вызывающему классустатический метод;
  5. инициализировать классПодкласс,отецсам также инициализируется;
  6. как программазагрузочная запись,Включаютmainметод (например:SpringBootначальный класс).

Активная ссылка 1 — основной метод в начальном классе

Пример кода:

public class OptimisticReference0 {
    static {
        System.out.println(OptimisticReference0.class.getSimpleName() + " is referred!");
    }

    public static void main(String[] args) {
        System.out.println();
    }
}

результат операции:

OptimisticReference0 is referred!

Active Reference 2 — создание подкласса запускает инициализацию родительского класса

Пример кода:

public class OptimisticReference1 {
    public static class Parent {
        static {
            System.out.println(Parent.class.getSimpleName() + " is referred!");
        }
    }

    public static class Child extends Parent {
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
        }
    }

    public static void main(String[] args) {
        new Child();
    }
}

результат операции:

Parent is referred! Child is referred!

Active Reference 3 — Доступ к статической переменной класса

Пример кода:

public class OptimisticReference2 {
    public static class Child {
        protected static String name;
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
            name = "Child";
        }
    }

    public static void main(String[] args) {
        System.out.println(Child.name);
    }
}

результат операции:

Child is referred! Child

Активная ссылка 4 — присвоение статической переменной классу

Пример кода:

public class OptimisticReference3 {
    public static class Child {
        protected static String name;
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
        }
    }

    public static void main(String[] args) {
        Child.name = "Child";
    }
}

результат операции:

Child is referred!

Active Reference 5 — используйте механизм отражения, предоставляемый пакетом java.lang.reflect.

Пример кода:

public class OptimisticReference4 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("org.ostenant.jdk8.learning.examples.reference.optimistic.Child");
    }
}

результат операции:

Child is referred!

пассивное цитирование

Пассивная ссылка: на этапе загрузки класса он будет выполненнагрузка,соединятьиинициализацияработать.

Существует несколько форм пассивного цитирования:

  1. Ссылка по подклассуотецизстатическое поле, не приводит кПодклассинициализация;
  2. определяющий классссылка на массивинет назначения, не вызовет инициализацию этого класса;
  3. класс доступа определенпостоянный, не запускает инициализацию этого класса.

Пассивная ссылка 1 — подкласс ссылается на статические поля суперкласса, не вызывая инициализации подкласса.

Пример кода:

public class NegativeReference0 {
    public static class Parent {
        public static String name = "Parent";
        static {
            System.out.println(Parent.class.getSimpleName() + " is referred!");
        }
    }

    public static class Child extends Parent {
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
        }
    }

    public static void main(String[] args) {
        System.out.println(Child.name);
    }
}

результат операции:

Parent is referred! Parent

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

Пример кода:

public class NegativeReference1 {
    public static class Child {
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
        }
    }

    public static void main(String[] args) {
        Child[] childs = new Child[10];
    }
}

результат операции:

нет выхода

Пассивная ссылка 3 — доступ к константам, определенным классом, не запускает инициализацию этого класса

Образец кода:

public class NegativeReference2 {
    public static class Child {
        public static final String name = "Child";
        static {
            System.out.println(Child.class.getSimpleName() + " is referred!");
        }
    }

    public static void main(String[] args) {
        System.out.println(Child.name);
    }
}

результат операции:

Child

(4) Погрузчики трех классов

загрузчик классов:загрузчик классовотвечает за загрузкутип(классы и интерфейсы) и дать им уникальное имя для их идентификации.

Организация загрузчиков классов

отношение загрузчика классов

  1. Bootstrap ClassloaderвJavaИнициализируется после запуска виртуальной машины.
  2. Bootstrap Classloaderответственный за погрузкуExtClassLoader, и воляExtClassLoaderРодительский загрузчик настроен наBootstrap Classloader
  3. Bootstrap ClassloaderзагруженExtClassLoader, он загрузитсяAppClassLoader, и воляAppClassLoaderРодительский загрузчик указан какExtClassLoader.

Роль загрузчика классов

Class Loader Метод реализации конкретный класс реализации Цель, отвечающая за загрузку
Bootstrap Loader C++ Реализовано на С++ %JAVA_HOME%/jre/lib/rt.jarа также-XbootclasspathПуть, указанный параметром, и библиотека классов в
Extension ClassLoader Java sun.misc.Launcher$ExtClassLoader %JAVA_HOME%/jre/lib/extпод дорожкой иjava.ext.dirsБиблиотека классов по пути, указанному системной переменной
Application ClassLoader Java sun.misc.Launcher$AppClassLoader Classpathа также-classpath,-cpКласс, указывающий расположение, указанное в каталоге, илиjarдокументация, она жеJavaЗагрузчик классов программы по умолчанию

Особенности загрузчиков классов

  • Иерархия: загрузчики классов в Java организованы в иерархию родитель-потомок. Загрузчик классов Bootstrap — отец всех загрузчиков.
  • прокси-режим: в зависимости от иерархической структуры прокси-серверы классов могут передаваться между загрузчиками. Когда загрузчик загружает класс, он сначала проверяет, был ли он загружен в родительском загрузчике. Если верхний загрузчик загрузил класс, класс будет использоваться напрямую. В противном случае загрузчик класса запросит загрузку класса
  • Ограничения видимости: дочерний загрузчик может искать классы в родительском загрузчике, но родительский загрузчик не может искать классы в дочернем загрузчике.
  • Удаление запрещено: загрузчик классов может загружать класс, но не выгружать его, но вы можете удалить текущий загрузчик классов и создать новый загрузчик классов для загрузки.

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

каждыйзагрузчик классовесть один из своихПространства именИспользуется для сохранения загруженных классов. Когда загрузчик классов загружает класс, он используетглобально квалифицированное имя класса(Fully Qualified Class Name), чтобы выполнить поиск, чтобы определить, был ли уже загружен класс.

JVMиDalvikУникальная идентификация классаClassLoader id + PackageName + ClassName, так что в работающей программе их может быть дваИмена пакетовиимя классаточно такой же класс. а если эти двасвоего родане черезClassLoaderзагрузка, не может разместить экземпляр классасильный поворотДля другого класса этоClassLoaderизоляция.

Чтобы разрешить загрузчик классаИзолируйте проблему,JVMпредставилМеханизм родительского делегирования.

(5) Механизм родительского делегирования

Основная идея: Во-первых,вверх дномПроверьте, является ли классзагружен; Второй,низходящийпытатьсякласс нагрузки.

Конкретный процесс загрузки

  1. когдаAppClassLoaderзагрузить одинclass, он не будет пытаться сначала загрузить класс сам по себе, а запросит загрузку классаделегироватьдаватьзагрузчик родительского классаExtClassLoaderзаканчивать.
  2. когдаExtClassLoaderзагрузить одинclass, он не будет пытаться сначала загрузить класс сам по себе, а запросит загрузку классаделегироватьдаватьBootStrapClassLoaderзаканчивать.
  3. еслиBootStrapClassLoaderЗагрузка не удалась (например, в%JAVA_HOME%/jre/libне найдено вclass),Будет использоватьсяExtClassLoaderпопытаться загрузить;
  4. еслиExtClassLoaderтакже не загружается, он будет использоватьAppClassLoaderзагрузить, еслиAppClassLoaderтакже не загружается, будет сообщено об исключенииClassNotFoundException.

Анализ исходного кода

ClassLoader.class

  1. loadClass(): указав классПолное имя, загрузчиком классовобнаружить,нагрузка,Создайтеи вернуть классjava.lang.Classобъект.

ClassLoaderпройти черезloadClass()метод реализованМеханизм родительского делегирования, для классадинамическая нагрузка.

loadClass()сам по себе являетсярекурсивный восходящий вызовпроцесс.

  • вверх дномПроверьте, является ли классзагружен

    1. пройти первымfindLoadedClass()метод из самыхЗагрузчик нижнего классаНачните проверять, загружен ли класс.
    2. Если уже загружен, согласноresolveПараметры определяют, следует ли выполнятьсоединятьобработать и вернутьClassобъект.
    3. Если не загрузится, пройтиparent.loadClass()Делегируйте его загрузчику родительского класса для выполнения тех же проверок (по умолчанию без обработки соединения).
    4. до того какпогрузчик высшего класса,СейчасparentКогда пусто, поfindBootstrapClassOrNull()способ попробоватьBootstrap ClassLoaderПроверьте целевой класс в .
  • низходящийпытатьсякласс нагрузки

    1. Если целевой класс все еще не найден, то изBootstrap ClassLoaderначать, черезfindClass()метод, чтобы попробовать соответствующийкаталог классаСпуститесь вниз и загрузите целевой класс.
    2. Если загрузка прошла успешно, согласноresolveПараметры определяют, следует ли выполнятьсоединятьобработать и вернутьClassобъект.
    3. Если нагрузка не удалась, тозагрузчик подклассапопробуйте загрузить до самогоЗагрузчик нижнего классатакже не загружается и в конечном итоге выбрасываетClassNotFoundException.
  1. findLoadedClass()

найти текущийв кеше загрузчика классовБыл ли загружен целевой класс.findLoadedClass()на самом деле вызывает базовыйnativeметодfindLoadedClass0().

  1. findBootstrapClassOrNull()

найти топBootstrapзагрузчик классовтого, был ли загружен целевой класс. такой же,findBootstrapClassOrNull()на самом деле вызывает базовыйnativeметодfindBootstrapClass().

  1. findClass()

ClassLoaderдаjava.langв упаковкеабстрактный класс, а также все загрузчики классов (кромеBootstrap) базовый класс,findClass()даClassLoaderАбстрактный метод для загрузки целевого класса, предоставляемого подклассами.

Уведомление:Bootstrap ClassLoaderне принадлежитJVMуровень, он не подчиняетсяClassLoaderправила загрузкиBootstrap classLoaderПодклассов нет.

  1. defineClass()

defineClass()даClassLoaderМетоды, предоставляемые подклассам, которые могут преобразовывать файлы .classдокументдвоичные данныепреобразовать в законныйjava.lang.Classобъект.

(6) Динамическая загрузка классов

Несколько способов загрузки классов

  • пройти черезКомандная строкаНачатоJVMначальная нагрузка;
  • пройти черезClass.forName()метод динамической загрузки;
  • пройти черезClassLoader.loadClass()Методы загружаются динамически.

Class.forName() и ClassLoader.loadClass()

  • Class.forName(): поставить класс.classфайл загружен вJVM, интерпретируя класс и выполняяstaticстатический блок кода;
  • ClassLoader.loadClass(): просто загрузите файл .class вJVM, не будет выполнятьсяstaticкодовый блокв содержании, только еслиnewInstanceбудет казнен.

(7) Инициализация объектов

Порядок инициализации объекта

Статическая переменная/статический блок кода -> нормальный блок кода -> конструктор

  1. отецстатическая переменнаяистатический блок кода(первый объявленный будет выполнен первым);
  2. Подклассстатическая переменнаяистатический блок кода(первый объявленный будет выполнен первым);
  3. отецОбычная переменная-члениобычный кодовый блок(первый объявленный будет выполнен первым);
  4. родительский классКонструктор;
  5. ПодклассОбычная переменная-члениобычный кодовый блок(первый объявленный будет выполнен первым);
  6. подклассКонструктор.

Пример инициализации объекта

Parent.java

Children.java

Tester.java

Результаты теста:

Результаты теста показывают:JVMПри создании объекта следуйте порядку инициализации вышеуказанных объектов.

(8) Пользовательский загрузчик классов

Напишите свой собственный загрузчик классов

На этапе анализа исходного кода мы объяснили, как добитьсяпользовательский загрузчик классов, теперь начинаемненавидетьсобственный загрузчик классов.

Шаг 1: Определите целевой класс для загрузкиParent.javaиChildren.java.

Parent.java

package org.ostenant.jdk8.learning.examples.classloader.custom;

public class Parent {
    protected static String CLASS_NAME;
    protected static String CLASS_LOADER_NAME;
    protected String instanceID;

	// 1.先执行静态变量和静态代码块(只在类加载期间执行一次)
    static {
        CLASS_NAME = Parent.class.getName();
        CLASS_LOADER_NAME = Parent.class.getClassLoader().toString();
        System.out.println("Step a: " + CLASS_NAME + " is loaded by " + CLASS_LOADER_NAME);
    }

    // 2.然后执行变量和普通代码块(每次创建实例都会执行)
    {
        instanceID = this.toString();
        System.out.println("Step c: Parent instance is created: " + CLASS_LOADER_NAME + " -> " + instanceID);
    }

    // 3.然后执行构造方法
    public Parent() {
        System.out.println("Step d: Parent instance:" + instanceID + ", constructor is invoked");
    }

    public void say() {
        System.out.println("My first class loader...");
    }
}

Children.java

package org.ostenant.jdk8.learning.examples.classloader.custom;

public class Children extends Parent {
    static {
        CLASS_NAME = Children.class.getName();
        CLASS_LOADER_NAME = Children.class.getClassLoader().toString();
        System.out.println("Step b: " + CLASS_NAME + " is loaded by " + CLASS_LOADER_NAME);
    }

    {
        instanceID = this.toString();
        System.out.println("Step e: Children instance is created: " + CLASS_LOADER_NAME + " -> " + instanceID);
    }

    public Children() {
        System.out.println("Step f: Children instance:" + instanceID + ", constructor is invoked");
    }

    public void say() {
        System.out.println("My first class loader...");
    }
}

Шаг 2. Реализуйте собственный загрузчик классовCustomClassLoader

CustomClassLoader.java

public class CustomClassLoader extends ClassLoader {
    private String classPath;

    public CustomClassLoader(String classPath) {
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> c = findLoadedClass(name); // 可省略
        if (c == null) {
            byte[] data = loadClassData(name);
            if (data == null) {
                throw new ClassNotFoundException();
            }
            return defineClass(name, data, 0, data.length);
        }
        return null;
    }

    protected byte[] loadClassData(String name) {
        try {
            // package -> file folder
            name = name.replace(".", "//");
            FileInputStream fis = new FileInputStream(new File(classPath + "//" + name + ".class"));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len = -1;
            byte[] b = new byte[2048];
            while ((len = fis.read(b)) != -1) {
                baos.write(b, 0, len);
            }
            fis.close();
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Шаг 3. Проверьте процесс загрузки загрузчика классов.

CustomerClassLoaderTester.java

  1. При запуске тестовой программы один за другимкопироватьинагрузкабыть загруженнымисходный файл целевого класса.
    private static final String CHILDREN_SOURCE_CODE_NAME = SOURCE_CODE_LOCATION + "Children.java";
    private static final String PARENT_SOURCE_CODE_NAME = SOURCE_CODE_LOCATION + "Parent.java";
    private static final List<String> SOURCE_CODE = Arrays.asList(CHILDREN_SOURCE_CODE_NAME, PARENT_SOURCE_CODE_NAME);

    static {
        SOURCE_CODE.stream().map(path -> new File(path))
            // 路径转文件对象
            .filter(f -> !f.isDirectory())
            // 文件遍历
            .forEach(f -> {
            // 拷贝后源代码
            File targetFile = copySourceFile(f);
            // 编译源代码
            compileSourceFile(targetFile);
        });
    }
  1. Скопируйте один исходный файл в пользовательский загрузчик классовкаталог загрузки класса.
    protected static File copySourceFile(File f) {
        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
            reader = new BufferedReader(new FileReader(f));
            // package ...;
            String firstLine = reader.readLine();

            StringTokenizer tokenizer = new StringTokenizer(firstLine, " ");
            String packageName = "";
            while (tokenizer.hasMoreElements()) {
                String e = tokenizer.nextToken();
                if (e.contains("package")) {
                    continue;
                } else {
                    packageName = e.trim().substring(0, e.trim().length() - 1);
                }
            }

            // package -> path
            String packagePath = packageName.replace(".", "//");
            // java file path
            String targetFileLocation = TARGET_CODE_LOCALTION + "//" + packagePath + "//";

            String sourceFilePath = f.getPath();
            String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf("\\") + 1);

            File targetFile = new File(targetFileLocation, fileName);
            File targetFileLocationDir = new File(targetFileLocation);
            if (!targetFileLocationDir.exists()) {
                targetFileLocationDir.mkdirs();
            }
            // writer
            writer = new BufferedWriter(new FileWriter(targetFile));
            // 写入第一行
            writer.write(firstLine);
            writer.newLine();
            writer.newLine();

            String input = "";
            while ((input = reader.readLine()) != null) {
            writer.write(input);
                writer.newLine();
            }

            return targetFile;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
  1. к скопированному.javaИсходный файл компилируется вручную и создается в каталоге того же уровня..classдокумент.
    protected static void compileSourceFile(File f) {
        try {
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(f);

            // 执行编译任务
            CompilationTask task = javaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
            task.call();
            standardFileManager.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  1. пройти черезпользовательский загрузчик классовнагрузкаChildrenизjava.lang.Class<?>объект, который затем создается с использованием отраженияChildrenобъект экземпляра.
    @Test
    public void test() throws Exception {
        // 创建自定义类加载器
        CustomClassLoader classLoader = new CustomClassLoader(TARGET_CODE_LOCALTION); // E://myclassloader//classpath
        // 动态加载class文件到内存中(无连接)
        Class<?> c = classLoader.loadClass("org.ostenant.jdk8.learning.examples.classloader.custom.Children");
        // 通过反射拿到所有的方法
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if ("say".equals(method.getName())) {
                // 通过反射拿到children对象
                Object children = c.newInstance();
                // 调用children的say()方法
                method.invoke(children);
                break;
            }
        }
    }

Тестовый написанный загрузчик классов

(1) Тестовый сценарий 1

  1. резервstaticблок кода, поместите целевой классChildren.javaиParent.javaкопироватьв каталог classloaded, а затем вручнуюкомпилировать.
  2. резервПротестируйте целевой класс в каталоге проекта.Children.javaиParent.java.

Вывод результатов теста:

Анализ результатов испытаний:

Мы успешно создалиChildrenобъект и вызвал егоsay()метод. Однако, просмотрев журнал консоли, вы можете обнаружить, что загрузка класса все еще используетсяAppClassLoader,CustomClassLoaderне вступило в силу.

ПроверятьCustomClassLoaderКаталог загрузки классов:

У нас есть мы в каталоге классовкопироватьикомпилироватьизParentиChidrenдокумент.

Проанализируйте причины:

из-заParent.javaиChildren.java, который не удаляется после копирования. Привести кAppClassLoaderприоритет в своемClasspathподоказатьсяи удалосьнагрузкацелевой класс.

(2) Тестовый сценарий 2

  1. закомментируйтеstaticБлок кода (со скомпилированными целевыми классами в каталоге классов.classдокумент).
  2. УдалитьПротестируйте целевой класс в каталоге проекта.Children.javaиParent.java.

Вывод результатов теста:

Анализ результатов испытаний:

Мы успешно прошлипользовательский загрузчик классовЦелевой класс загружен. созданныйChildrenобъект и вызвал егоsay()метод.

На этом наш собственный простой загрузчик классов готов!

Справочная литература

Чжоу Чжимин, Глубокое понимание виртуальной машины Java: расширенные функции и лучшие практики JVM, Machinery Industry Press


Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack

零壹技术栈

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