Механизм отражения, предоставляемый Java, позволяет вам проверять информацию о классе во время выполнения.
Загрузка классов в Java
Java будет загружать класс, когда ему действительно нужно использовать класс, вместо того, чтобы загружать все классы при запуске программы, потому что большинство пользователей используют только часть ресурсов программы и загружают ее, когда им нужны определенные функции. системных ресурсов более эффективным.
Загрузка класса относится к.class
Двоичные данные в файле считываются в内存
, поместите его в JVM方法区
внутрь, затем в堆
площадь для созданияjava.lang.Class
Объект, используемый для инкапсуляции структуры данных класса в области методов. Конечный продукт загрузки класса находится в области кучиClass对象
, объект Class инкапсулирует структуру данных класса в области методов и предоставляет программистам Java интерфейс для доступа к структуре данных в области методов.
Все типы в Java, включая примитивные типы (
int
, long
, float
д.), даже массивы имеют связанные с ними объекты класса Class.
Объекты класса автоматически генерируются JVM.Каждый раз, когда класс загружается, JVM автоматически генерирует для него объект класса.
Объект класса
экземпляр.getClass()
по объектуgetClass()
получить каждый实例
Соответствующий объект класса
String name = "hello";
Class stringClass = name.getClass();
System.out.println("类的名称:" + stringClass.getName());
System.out.println("是否为接口:" + stringClass.isInterface());
System.out.println("是否为基本类型:" + stringClass.isPrimitive());
System.out.println("是否为数组:" + stringClass.isArray());
System.out.println("父类名称:" + stringClass.getSuperclass().getName());
类的名称:java.lang.String
是否为接口:false
是否为基本类型:false
是否为数组:false
父类名称:java.lang.Object
имя класса.класс
Вы также можете напрямую использовать следующий метод, чтобы получить объект класса класса String.
Class stringClass = String.class;
Class.forName()
В некоторых приложениях нельзя заранее знать, какой класс загрузит пользователь, можно использовать статический метод классаforName()
для динамической загрузки категорий
Class c = Class.forName(args[0]);
System.out.println("类的名称:" + c.getName());
System.out.println("是否为接口:" + c.isInterface());
System.out.println("是否为基本类型:" + c.isPrimitive());
System.out.println("是否为数组:" + c.isArray());
System.out.println("父类名称:" + c.getSuperclass().getName());
$ java Demo1 java.util.Scanner
类的名称:java.util.Scanner
是否为接口:false
是否为基本类型:false
是否为数组:false
父类名称:java.lang.Object
Существует две версии Class.forName(), в приведенной выше версии указывается только полное имя класса, а другая версия позволяет указать имя класса, выполнять ли блок статического кода при загрузке и выполнять загрузчик класса (ClassLoader
)
static Class forName(String name, boolean initialize, ClassLoader loader)
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// Class.forName() 加载类 默认会执行初始化块
Class.forName("Test2");
// Class.forName() 加载类 第二个参数 可以控制是否执行初始化块
Class.forName("Test2", false, loader);
class Test2 {
static {
System.out.println("静态初始化块执行了!");
}
}
Получить информацию от объекта класса
Объект класса представляет загруженную категорию.После получения объекта класса вы можете получить информацию, относящуюся к категории.package
, constructor
, field
, method
и другая информация.
Каждый тип информации имеет соответствующую категорию
- package: java.lang.reflect.Package
- constructor: java.lang.reflect.Constructor
- field: java.lang.reflect.Field
- method: java.lang.reflect.Method
Class c = Class.forName(args[0]);
System.out.println("包信息package:" + c.getPackage());
System.out.println("类修饰符modifier:" + c.getModifiers());
System.out.println("构造方法constructor:");
Arrays.stream(c.getDeclaredConstructors()).forEach(System.out::println);
System.out.println("成员变量fields:");
Arrays.stream(c.getDeclaredFields()).forEach(System.out::println);
$ java Demo1 java.util.ArrayList
包信息package:package java.util
类修饰符modifier:1
构造方法constructor:
public java.util.ArrayList(java.util.Collection)
public java.util.ArrayList()
public java.util.ArrayList(int)
成员变量fields:
private static final long java.util.ArrayList.serialVersionUID
private static final int java.util.ArrayList.DEFAULT_CAPACITY
private static final java.lang.Object[] java.util.ArrayList.EMPTY_ELEMENTDATA
private static final java.lang.Object[] java.util.ArrayList.DEFAULTCAPACITY_EMPTY_ELEMENTDATA
transient java.lang.Object[] java.util.ArrayList.elementData
private int java.util.ArrayList.size
private static final int java.util.ArrayList.MAX_ARRAY_SIZE
Загрузчик классов ClassLoader
Java загружает классы только тогда, когда они нужны.Class Loader
быть реализованным.
когда вы пытаетесь выполнитьjava xxx
команда, java попытается найтиJRE
каталог установки, затем найдитеjvm.dll
, затем запустите JVM и выполните операции инициализации, а затем сгенерируйтеBootstrapLoader
,Bootstrap Loader
будет загружатьсяExtended Loader
, и установитеExtended Loader
РодительBootstrapLoader
, тогдаBootstrap Loader
будет загружатьсяApplication Loader
, и воляApplication Loader
Родитель настроен наExtended Loader
Запустите загрузчик классов
BootstrapLoader
поискsun.boot.library.path
класс, указанный в , вы можете использоватьSystem.getProperty("sun.boot.library.path")
получить
/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib
загрузчик класса расширения
Extended Loader
(sun.misc.Launcher$ExtClassLoader
) написан на Java и ищет системные параметрыjava.ext.dirs
категория, указанная вSystem.getProperty("java.ext.dirs")
получить
/Users/dsying/Library/Java/Extensions:
/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/ext:
/Library/Java/Extensions:
/Network/Library/Java/Extensions:
/System/Library/Java/Extensions:/usr/lib/java
загрузчик класса приложения
Application Loader
(sun.misc.Launcher$AppClassLoader
) написан на Java и ищет системные параметрыjava.class.path
категория, указанная вSystem.getProperty("java.class.path")
получить, используяjava xxx
выполнение команды.class
файл байт-кода, вы можете передать-cp
Установка параметровclasspath
java –cp ./classes SomeClass
Связь между загрузчиками классов
ClassLoader loader Thread.currentThread().getContextClassLoader();
// sun.misc.Launcher$AppClassLoader@18b4aac2 应用类加载器
System.out.println(loader);
// sun.misc.Launcher$ExtClassLoader@610455d6 扩展类加载器
System.out.println(loader.getParent());
// Bootstrap ClassLoader 启动类加载器(用C语言实现,所以此处返回null)
System.out.println(loader.getParent().getParent());
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@610455d6
null
Существует три способа загрузки класса:
- Когда командная строка запускает приложение, оно инициализируется и загружается JVM.
- пройти через
Class.forName()
Метод динамической загрузки - пройти через
ClassLoader.loadClass()
Метод динамической загрузки
Разница между Class.forName() и ClassLoader.loadClass()
-
Class.forName()
: Помимо загрузки файла .class класса в jvm, он также будет интерпретировать класс и выполнять статический блок в классе; -
ClassLoader.loadClass()
: Только сделать одно, то есть загрузить файл .class в jvm, содержимое в static не будет выполняться, а статический блок будет выполняться только в newInstance. -
Class.forName(name, initialize, loader)
Функции с параметрами также могут управлять загрузкой статических блоков. И только метод newInstance() вызывается вызовом конструктора для создания объекта класса.
Механизм загрузки классов JVM
-
全盘负责
, когда загрузчик классов отвечает за загрузку класса, другие классы, от которых зависит класс, и ссылки также будут загружены загрузчиком классов, если для загрузки не отображается другой загрузчик классов. -
父类委托
, пусть загрузчик родительского класса попытается сначала загрузить класс и попытается загрузить класс только из своего собственного пути к классам, если загрузчик родительского класса не может загрузить класс -
缓存机制
, механизм кэширования гарантирует, что все загруженные классы будут кэшированы.Когда класс необходимо использовать в программе, загрузчик классов сначала будет искать класс в области кэша, и система будет считывать соответствующий класс, только если области кэша не существует.Двоичные данные преобразуются в объект класса и сохраняются в буфере. Вот почему после изменения класса необходимо перезапустить JVM, чтобы изменение программы вступило в силу.
Модель родительского делегирования
Рабочий процесс модели родительского делегирования таков: если загрузчик класса получает запрос на загрузку класса, он не будет пытаться сначала загрузить класс сам по себе, а делегирует запрос родительскому загрузчику для его завершения и т. д. Следовательно, все Запросы на загрузку класса в конечном итоге должны быть переданы загрузчику классов запуска верхнего уровня, только если родительский загрузчик не находит требуемый класс в своей области поиска, т.е. загрузка не может быть завершена, дочерний загрузчик попытается сделать это на своем собственный Загрузите класс.
Механизм родительского делегирования:
- когда
AppClassLoader
При загрузке класса он не пытается сначала загрузить сам класс, а делегирует запрос на загрузку класса загрузчику родительского класса.ExtClassLoader
заканчивать. - когда
ExtClassLoader
При загрузке класса он не пытается загрузить сам класс в первую очередь, а делегирует запрос на загрузку классаBootStrapClassLoader
заканчивать. - если
BootStrapClassLoader
Загрузка не удалась (например, в$JAVA_HOME/jre/lib
класс не найден), будем использоватьExtClassLoader
попытаться загрузить; - как
ExtClassLoader
также не загружается, он будет использоватьAppClassLoader
загрузить, еслиAppClassLoader
также не загружается, будет сообщено об исключенииClassNotFoundException
.
Анализ исходного кода ClassLoader:
public Class<?> loadClass(String name)throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
// 首先判断该类型是否已经被加载
Class c = findLoadedClass(name);
if (c == null) {
//如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
try {
if (parent != null) {
//如果存在父类加载器,就委派给父类加载器加载
c = parent.loadClass(name, false);
} else {
//如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
//如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
Значение модели родительского делегирования:
- Системный класс предотвращает появление в памяти нескольких копий одного и того же байт-кода.
- Обеспечение безопасной и стабильной работы Java-программ
пользовательский загрузчик
Пользовательские загрузчики классов обычно наследуются отClassLoader
класс, обращенный сверхуloadClass
метод для анализа, нам нужно только переписатьfindClass
метод. Ниже мы используем пример для демонстрации процесса настройки загрузчика классов:
package com.github.hcsp.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
// 存放字节码文件的目录
private final File bytecodeFileDirectory;
public MyClassLoader(File bytecodeFileDirectory) {
this.bytecodeFileDirectory = bytecodeFileDirectory;
}
// 还记得类加载器是做什么的么?
// "从外部系统中,加载一个类的定义(即Class对象)"
// 请实现一个自定义的类加载器,将当前目录中的字节码文件加载成为Class对象
// 提示,一般来说,要实现自定义的类加载器,你需要覆盖以下方法,完成:
//
// 1.如果类名对应的字节码文件存在,则将它读取成为字节数组
// 1.1 调用ClassLoader.defineClass()方法将字节数组转化为Class对象
// 2.如果类名对应的字节码文件不存在,则抛出ClassNotFoundException
//
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getByteArrayFromFile(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
}
byte[] getByteArrayFromFile(String className) throws ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
File file = new File(bytecodeFileDirectory, className + ".class");
int len = 0;
try {
byte[] bufferSize = new byte[1024];
FileInputStream fis = new FileInputStream(file);
while ((len = fis.read(bufferSize)) != -1) {
bos.write(bufferSize, 0, len);
}
} catch (FileNotFoundException e) {
throw new ClassNotFoundException();
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
public static void main(String[] args) throws Exception {
File projectRoot = new File(System.getProperty("basedir", System.getProperty("user.dir")));
MyClassLoader myClassLoader = new MyClassLoader(projectRoot);
Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");
Object testClassInstance = testClass.getConstructor().newInstance();
String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);
System.out.println(message);
}
}
пользовательский загрузчик классовосновнойлежит в对字节码文件的获取
, если это зашифрованный байт-код, вам нужно расшифровать файл в этом классе. Поскольку это всего лишь демонстрация, я не шифровал файл класса, поэтому процесса расшифровки нет.
разное
Создание объектов с помощью отражения
Вы можете использовать классnewInstance()
метод создания экземпляра
Class c = Class.forName(className);
Object obj = c.newInstance();
метод вызова
Метод в классе можно получить с помощью отражения, а класс, соответствующий методу,java.lang.reflect.Method
, вы можете использовать егоinvoke()
метод для вызова указанного метода
Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");
Object testClassInstance = testClass.getConstructor().newInstance();
String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);