предисловие
Эта статья расскажет от мелкого к глубокомуJava
Процесс и принцип загрузки классов, дальнейший анализ исходного кода загрузчика классов и завершение пользовательского загрузчика классов.
текст
(1) Что такое загрузчик классов
Короче говоря, загрузчик классов используется для.class
в файлеинформация о байт-кодеперевести на конкретный的java.lang.Class
объектинструменты для процесса.
Конкретный процесс:
- Во время фактического процесса загрузки класса
JVM
будет все.class
в файле байт-кодадвоичные данныеЧтение в память и импорт в область данных времени выполненияобласть методасередина. - при первом вызове классаАктивная загрузкаилипассивная загрузкаКогда загрузчик класса выполняет процесс загрузки класса для этого класса -нагрузка,соединять(проверять,Подготовить,Разобрать),инициализация.
- Если класс загружен успешно,куча памятисоздаст новый
Class
объект,Class
Объект инкапсулирует класс вобласть методавнутриструктура данных.
Class
Описание процесса создания объекта:
(2) Процесс загрузки класса
Процесс загрузки класса делится на три шага (пять этапов):нагрузка -> соединять(проверять,Подготовить,Разобрать) ->инициализация.
нагрузка,проверять,ПодготовитьиинициализацияПорядок, в котором происходят эти четыре стадии, таков:определенный,ифаза синтаксического анализаможет произойти после фазы инициализации, также известной какдинамическая привязкаилипозднее связывание.
Описание процесса загрузки класса:
1. Загрузить
load: найти и загрузить классдвоичные данныепроцесс.
Описание процесса загрузки:
- по классамполное имядолжность
.class
файл и получить егодвоичный поток байтов. - поток байтов, представленныйстатическая структура храненияпреобразовать вобласть методаизструктуры данных времени выполнения.
- существует
Java
кучасоздать такойjava.lang.Class
объект, как данные в области методаподъезд.
2. Подключение
Соединения: включеныпроверять,Подготовить,РазобратьТри шага.
а) проверить
Проверка: убедитесь, что загруженный классправильность. Аутентификация является первым шагом на этапе подключения и используется для обеспеченияClass
Соответствует ли информация в потоке байтов требованиям виртуальной машины.
Конкретная форма проверки:
-
проверка формата файла: Убедитесь, что поток байтов соответствует
Class
Спецификация формата файла, например, следует ли начинать с0xCAFEBABE
Start, находятся ли основной и дополнительный номера версий в пределах диапазона обработки текущей виртуальной машины и имеют ли константы в пуле констант неподдерживаемые типы. -
проверка метаданных: семантический анализ информации, описываемой байт-кодом (примечание: отличие
javac
семантический анализ на этапе компиляции), чтобы убедиться, что описываемая им информация соответствует требованиям спецификации языка Java; например: имеет ли этот класс родительский класс, за исключениемjava.lang.Object
за пределами. - Проверка байт-кода: посредством анализа потока данных и потока управления определено, что семантика программы является законной и логичной.
- Проверка символьной ссылки: убедитесь, что действие синтаксического анализа выполняется правильно.
б) Подготовить
Подготовка: к урокустатическая переменнаяВыделите память и инициализируйте ее какПо умолчанию. В процессе подготовки обычно выделяется структура для хранения информации о классе, эта структура содержит определенные в классеПеременные-члены,методиинформация об интерфейсеЖдать.
Конкретное поведение:
- В настоящее время выделение памяти включает толькопеременная класса(
static
), не включаяпеременная экземпляра,переменная экземплярабудет в объектесоздавать экземплярКогда объект выделяется кусками вJava
кучасередина. - установить здесьПервоначальный значениеОбычно тип данныхнулевое значение по умолчанию(как
0
,0L
,null
,false
д.), а не бытьJava
в кодеявное присвоение.
в) Анализ
Анализ: поставьте класс правильнопостоянный пулвнутриСимволическая ссылкапреобразовать впрямая цитата.
Действие анализа в основном длякласс или интерфейс,поле,метод класса,метод интерфейса,тип метода,дескриптор методаиквалификатор места вызоваи т.д. Выполняется 7 видов символьных ссылок.
3. Инициализировать
инициализация: дастатическая переменная классаприсвоить правильные начальные значения (отметить и соединитьпроцесс разборадифференцировать).
инициализированная цель
- реализует декларациюстатическая переменная классаинициализация заданного начального значения;
- реализация для использованиястатический блок кодаИнициализация установленного начального значения.
шаги инициализации
- Если этот класс ненагрузка,соединять, то сначаланагрузка,соединятьтакой;
- если такойпрямой родительский классне был инициализирован, первыйинициализацияего непосредственный родительский класс;
- если в классе естьоператор инициализации, операторы инициализации выполняются последовательно.
время для инициализации
- Создайте экземпляр класса (
new
ключевое слово); -
java.lang.reflect
методы в пакете (например:Class.forName(“xxx”)
); - к классустатическая переменнаясделать доступ или назначение;
- доступ к вызывающему классустатический метод;
- инициализировать классПодкласс,отецсам также инициализируется;
- как программазагрузочная запись,Включают
main
метод (например:SpringBoot
начальный класс).
(3) Активная ссылка и пассивная ссылка класса
Активное цитирование
Активная ссылка: на этапе загрузки класса выполнять тольконагрузка,соединятьдействовать, неинициализацияработать.
Несколько форм активного цитирования
- Создайте экземпляр класса (
new
ключевое слово); -
java.lang.reflect
методы в пакете (например:Class.forName(“xxx”)
); - к классустатическая переменнаясделать доступ или назначение;
- доступ к вызывающему классустатический метод;
- инициализировать классПодкласс,отецсам также инициализируется;
- как программазагрузочная запись,Включают
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 — подкласс ссылается на статические поля суперкласса, не вызывая инициализации подкласса.
Пример кода:
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) Погрузчики трех классов
загрузчик классов:загрузчик классовотвечает за загрузкутип(классы и интерфейсы) и дать им уникальное имя для их идентификации.
Организация загрузчиков классов
отношение загрузчика классов
-
Bootstrap Classloader
вJava
Инициализируется после запуска виртуальной машины. -
Bootstrap Classloader
ответственный за погрузкуExtClassLoader
, и воляExtClassLoader
Родительский загрузчик настроен наBootstrap Classloader
-
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) Механизм родительского делегирования
Основная идея: Во-первых,вверх дномПроверьте, является ли классзагружен; Второй,низходящийпытатьсякласс нагрузки.
Конкретный процесс загрузки
- когда
AppClassLoader
загрузить одинclass
, он не будет пытаться сначала загрузить класс сам по себе, а запросит загрузку классаделегироватьдаватьзагрузчик родительского классаExtClassLoader
заканчивать. - когда
ExtClassLoader
загрузить одинclass
, он не будет пытаться сначала загрузить класс сам по себе, а запросит загрузку классаделегироватьдаватьBootStrapClassLoader
заканчивать. - если
BootStrapClassLoader
Загрузка не удалась (например, в%JAVA_HOME%/jre/lib
не найдено вclass
),Будет использоватьсяExtClassLoader
попытаться загрузить; - если
ExtClassLoader
также не загружается, он будет использоватьAppClassLoader
загрузить, еслиAppClassLoader
также не загружается, будет сообщено об исключенииClassNotFoundException
.
Анализ исходного кода
ClassLoader.class
-
loadClass(): указав классПолное имя, загрузчиком классовобнаружить,нагрузка,Создайтеи вернуть класс
java.lang.Class
объект.
ClassLoader
пройти черезloadClass()
метод реализованМеханизм родительского делегирования, для классадинамическая нагрузка.
loadClass()
сам по себе являетсярекурсивный восходящий вызовпроцесс.
-
вверх дномПроверьте, является ли классзагружен
- пройти первым
findLoadedClass()
метод из самыхЗагрузчик нижнего классаНачните проверять, загружен ли класс. - Если уже загружен, согласно
resolve
Параметры определяют, следует ли выполнятьсоединятьобработать и вернутьClass
объект. - Если не загрузится, пройти
parent.loadClass()
Делегируйте его загрузчику родительского класса для выполнения тех же проверок (по умолчанию без обработки соединения). - до того какпогрузчик высшего класса,Сейчас
parent
Когда пусто, поfindBootstrapClassOrNull()
способ попробоватьBootstrap ClassLoader
Проверьте целевой класс в .
- пройти первым
-
низходящийпытатьсякласс нагрузки
- Если целевой класс все еще не найден, то из
Bootstrap ClassLoader
начать, черезfindClass()
метод, чтобы попробовать соответствующийкаталог классаСпуститесь вниз и загрузите целевой класс. - Если загрузка прошла успешно, согласно
resolve
Параметры определяют, следует ли выполнятьсоединятьобработать и вернутьClass
объект. - Если нагрузка не удалась, тозагрузчик подклассапопробуйте загрузить до самогоЗагрузчик нижнего классатакже не загружается и в конечном итоге выбрасывает
ClassNotFoundException
.
- Если целевой класс все еще не найден, то из
- findLoadedClass()
найти текущийв кеше загрузчика классовБыл ли загружен целевой класс.
findLoadedClass()
на самом деле вызывает базовыйnative
методfindLoadedClass0()
.
- findBootstrapClassOrNull()
найти топ
Bootstrap
загрузчик классовтого, был ли загружен целевой класс. такой же,findBootstrapClassOrNull()
на самом деле вызывает базовыйnative
методfindBootstrapClass()
.
- findClass()
ClassLoader
даjava.lang
в упаковкеабстрактный класс, а также все загрузчики классов (кромеBootstrap
) базовый класс,findClass()
даClassLoader
Абстрактный метод для загрузки целевого класса, предоставляемого подклассами.Уведомление:
Bootstrap ClassLoader
не принадлежитJVM
уровень, он не подчиняетсяClassLoader
правила загрузкиBootstrap classLoader
Подклассов нет.
- 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) Инициализация объектов
Порядок инициализации объекта
Статическая переменная/статический блок кода -> нормальный блок кода -> конструктор
- отецстатическая переменнаяистатический блок кода(первый объявленный будет выполнен первым);
- Подклассстатическая переменнаяистатический блок кода(первый объявленный будет выполнен первым);
- отецОбычная переменная-члениобычный кодовый блок(первый объявленный будет выполнен первым);
- родительский классКонструктор;
- ПодклассОбычная переменная-члениобычный кодовый блок(первый объявленный будет выполнен первым);
- подклассКонструктор.
Пример инициализации объекта
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
- При запуске тестовой программы один за другимкопироватьинагрузкабыть загруженнымисходный файл целевого класса.
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);
});
}
- Скопируйте один исходный файл в пользовательский загрузчик классовкаталог загрузки класса.
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;
}
- к скопированному
.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();
}
}
- пройти черезпользовательский загрузчик классовнагрузка
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
-
резерв
static
блок кода, поместите целевой классChildren.java
иParent.java
копироватьв каталог classloaded, а затем вручнуюкомпилировать. -
резервПротестируйте целевой класс в каталоге проекта.
Children.java
иParent.java
.
Вывод результатов теста:
Анализ результатов испытаний:
Мы успешно создали
Children
объект и вызвал егоsay()
метод. Однако, просмотрев журнал консоли, вы можете обнаружить, что загрузка класса все еще используетсяAppClassLoader
,CustomClassLoader
не вступило в силу.
ПроверятьCustomClassLoader
Каталог загрузки классов:
У нас есть мы в каталоге классовкопироватьикомпилироватьиз
Parent
иChidren
документ.
Проанализируйте причины:
из-за
Parent.java
иChildren.java
, который не удаляется после копирования. Привести кAppClassLoader
приоритет в своемClasspath
подоказатьсяи удалосьнагрузкацелевой класс.
(2) Тестовый сценарий 2
-
закомментируйте
static
Блок кода (со скомпилированными целевыми классами в каталоге классов.class
документ). -
УдалитьПротестируйте целевой класс в каталоге проекта.
Children.java
иParent.java
.
Вывод результатов теста:
Анализ результатов испытаний:Мы успешно прошлипользовательский загрузчик классовЦелевой класс загружен. созданный
Children
объект и вызвал егоsay()
метод.
На этом наш собственный простой загрузчик классов готов!
Справочная литература
Чжоу Чжимин, Глубокое понимание виртуальной машины Java: расширенные функции и лучшие практики JVM, Machinery Industry Press
Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.