Это 10-й день моего участия в Gengwen Challenge.Подробности о мероприятии:Обновить вызов
Эта статья участвует в "Java Theme Month - Java Development Practice", подробнее см.Ссылка на мероприятие
Статьи по Теме
Аннотации и размышления Java:Аннотации и отражение Java
предисловие
Изучив процесс загрузки класса, нам удобнее понять механизм и принцип отражения.
Во-первых, процесс загрузки класса
1. Загрузить
Загрузите содержимое байт-кода файла класса в память, преобразуйте эти статические данные в структуру данных времени выполнения области метода, а затем сгенерируйте объект java.lang.Class, представляющий класс.
2. ссылка
Включите двоичные данные класса в JRE.
- Проверка: убедитесь, что загруженная информация о классе соответствует спецификации JVM и что нет проблем с безопасностью.
- Подготовка: Этап формального выделения памяти для переменных класса (статических) и установки начального значения переменных класса по умолчанию, эта память будет выделена в области методов
- Решение: процесс замены символической ссылки (имя константы) пула констант виртуальной машины на прямую ссылку (адрес).
3. Инициализировать: виртуальная машина JVM отвечает за инициализацию класса.
- Процесс выполнения метода конструктора класса(). Метод конструктора класса () создается комбинацией действий присваивания и операторов в статических блоках кода, которые автоматически собирают все переменные класса в классе во время компиляции. (Конструктор класса предназначен для создания информации о классе, а не для создания объектов этого класса)
- При инициализации класса, если обнаруживается, что его родительский класс не был инициализирован, необходимо сначала начать инициализацию своего родительского класса.
- Виртуальная машина обеспечивает правильную блокировку и синхронизацию метода класса () в многопоточной среде.
Схема выглядит следующим образом:Тестовый код:
public class test01 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.num);
}
}
class A{
static {
System.out.println("静态代码块初始化!");
num = 500;
}
static Integer num = 100;
public A(){
System.out.println("无参构造初始化!");
}
}
Результат выполнения следующий:
в заключении:
①Сначала выполните блок статического кода, затем присвойте 500 номеру
② Затем выполните конструктор без аргументов, который на самом деле является нашей новой операцией.
③Выполнить число = 100;
④Вы можете просто получить последовательность загрузки классов!
Во-вторых, инициализация класса
Код преамбулы:
public class test01 {
static {
System.out.println("我是main类的静态代码块。main类被加载啦~");
}
public static void main(String[] args) {
}
}
class A{
static int b = 2;
static {
System.out.println("父类A被加载~");
}
}
class B extends A{
static {
System.out.println("子类B被加载~");
b = 500;
}
static int m = 100;
static final int M = 1;
}
Этот вопрос можно обсудить в двух случаях: 1. Активная ссылка класса (должна произойти инициализация класса)
- При запуске виртуальной машины сначала инициализируйте класс, в котором находится основной метод.
- НОВЫЙ объект класса
- вызовите статические элементы (кроме окончательных констант) и статическими методами класса
- Используйте методы пакета java.lang.reflect для вызовов рефлексии для классов.
- При инициализации класса, если его родительский класс не был инициализирован, его родительский класс будет инициализирован первым
public static void main(String[] args) {
//主动引用
B b = new B();
}
результат:
public static void main(String[] args) {
//反射也会产生主动引用
Class.forName("com.dbright.Test.B");
}
2. Пассивные ссылки на классы (инициализация класса не происходит)
- При доступе к статическому полю будет инициализирован только класс, который фактически объявляет поле.Например, когда на статическую переменную родительского класса ссылаются через подкласс, подкласс не будет инициализирован.
- Определение ссылки на класс через массив не вызывает инициализацию этого класса
- Ссылочные константы не запускают инициализацию этого класса (константы сохраняются в пуле констант вызывающего класса на этапе связывания)
public static void main(String[] args) {
//调用父类静态变量
System.out.println(B.b);
}
public static void main(String[] args) {
//定义自身数组
B[] array = new B[5];
}
public static void main(String[] args) {
//调用自身静态变量
System.out.println(B.M);
}
В-третьих, загрузчик классов
загрузчик классовэффект: загрузить содержимое байт-кода файла класса в память, преобразовать эти статические данные в структуру данных времени выполнения области методов, а затем сгенерировать объект java.lang.Class, представляющий этот класс в куче в качестве данных в доступе к области методов. Вход. Загрузчики классов в основном делятся на три уровня:
- Загрузчик классов Bootstrap:Написанный на C++, это загрузчик классов, поставляемый с JVM, который отвечает за основную библиотеку платформы Java и используется для загрузки основной библиотеки классов. Загрузчик не может напрямую получить Java\jdk1.8\jre\lib\rt.jar
- Загрузчик класса расширения:Отвечает за упаковку пакета jar в каталоге jre/lib/ext или пакета jar в каталоге, указанном параметром -D java.ext.dirs, в рабочую библиотеку.
- Загрузчик системного класса:Отвечает за упаковку классов и jar-файлов в каталог, на который указывает java -classpath или -D java.class.path, и является наиболее часто используемым загрузчиком.
Кэш класса:Стандартный загрузчик классов JavaSE может находить классы по запросу, но как только класс загружается в загрузчик классов, он остается загруженным (кэшируется) в течение определенного периода времени, но механизм сборки мусора JVM может повторно использовать эти объекты класса.
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader); //sun.misc.Launcher$AppClassLoader@18b4aac2
//获取系统类加载的父类加载器-->扩展类加载器
ClassLoader parent = loader.getParent();
System.out.println(parent); //sun.misc.Launcher$ExtClassLoader@74a14482
//获取扩展类加载器的父类加载器-->根加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1); //null
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.dbright.Test.Test02").getClassLoader();
System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2
//测试JDK内部类是谁加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader); //null
// 如何获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
/*
D:\Software\Java\jdk1.8\jre\lib\charsets.jar;
D:\Software\Java\jdk1.8\jre\lib\deploy.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\access-bridge-64.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\cldrdata.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\dnsns.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\jaccess.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\jfxrt.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\localedata.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\nashorn.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\sunec.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\sunjce_provider.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\sunmscapi.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\sunpkcs11.jar;
D:\Software\Java\jdk1.8\jre\lib\ext\zipfs.jar;
D:\Software\Java\jdk1.8\jre\lib\javaws.jar;
D:\Software\Java\jdk1.8\jre\lib\jce.jar;
D:\Software\Java\jdk1.8\jre\lib\jfr.jar;
D:\Software\Java\jdk1.8\jre\lib\jfxswt.jar;
D:\Software\Java\jdk1.8\jre\lib\jsse.jar;
D:\Software\Java\jdk1.8\jre\lib\management-agent.jar;
D:\Software\Java\jdk1.8\jre\lib\plugin.jar;
D:\Software\Java\jdk1.8\jre\lib\resources.jar;
D:\Software\Java\jdk1.8\jre\lib\rt.jar;
D:\NEU\learn\java\project\out\production\project;
D:\NEU\learn\java\project\src\com\lib\commons-io-2.8.0.jar;
D:\Software\IntelliJ IDEA 2020.1.4\lib\idea_rt.jar
*/
//双亲委派机制:如果上层加载器存在不会使用下层定义类
}
}
Механизм родительского делегирования: если существует загрузчик верхнего уровня, класс определения нижнего уровня не будет использоваться.
Впереди долгий путь, и я обязательно буду его искать вдоль и поперёк~
Если вы думаете, что я блогеры хорошо пишу! Писать нелегко, пожалуйста, ставьте лайки, подписывайтесь и комментируйте, чтобы поощрять блоггеров ~ хахах