Java: разница между Class.forName и ClassLoader.loadClass
Недавно натолкнулся на какой-то код, который заставил меня задуматься. Какая разница между:
Class theClass = Class.forName("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();
и
Class theClass = ClassLoader.loadClass("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();
Являются ли они синонимами? В некоторых случаях предпочтительнее другого? Что делать и не использовать эти два метода?
Спасибо заранее.
Ответы
Ответ 1
Class.forName() всегда будет использовать ClassLoader вызывающего, тогда как ClassLoader.loadClass() может указать другой ClassLoader. Я считаю, что Class.forName также инициализирует загруженный класс, тогда как метод ClassLoader.loadClass() не делает этого сразу (он не инициализируется до тех пор, пока он не будет использоваться в первый раз).
Только что нашел эту статью, когда вы хотите подтвердить мое резюме поведения инициализации. Похоже, что большая часть информации вы ищете:
http://www.javaworld.com/javaworld/javaqa/2003-03/01-qa-0314-forname.html
Это использование довольно круто, хотя я никогда не использовал его раньше:
Class.forName(String, boolean, ClassLoader)
Он позволяет указать ClassLoader, а логический параметр определяет, должен ли класс инициализироваться при его загрузке или нет.
Ответ 2
Ответ Shaun более или менее правильный, за исключением нескольких пропусков/небольших ошибок:
-
Class.forName
связывает класс w/ClassLoader (независимо от того, какой-либо другой родитель загружает его для реального), поэтому ClassLoader.findLoadedClass
будет успешным в следующий раз. Что очень, очень важно, большинство ClassLoader будут пытаться Class c = findLoadedClass(name); if (c!=null) return c;
в качестве первых утверждений обходить всю часть поиска/поиска. Вызов класса ClassLoader.load напрямую не добавит класс к загруженным.
Случай имеет значение при загрузке через графическую структуру ClassLoader, т.е. не использует родительский только для первого поиска.
- Инициализация класса выполняется в loadclass класса ClassLoader с таким кодом:
if (resolve) resolveClass(c);
, и ClassLoader может фактически пропустить решение, которое оно считает неприемлемым, но возможно.
Что делать и не использовать эти два метода?
Если у вас нет очень сильной идеи, почему вы хотите ClassLoader.loadClass(String)
, не используйте ее напрямую. Во всех остальных случаях всегда полагайтесь на Class.forName(name, true, classLoader)
.
Загрузка всего класса рядом с искусством и не может быть покрыта простым ответом (не шутите о части искусства)
Ответ 3
Когда вы используете Class.forName("SomeImpl")
, вы получаете класс через текущий загрузчик классов (т.е. загрузчик класса, в который вы вызываете вызов метода). Он также будет инициализировать класс. Это фактически так же, как вызов Class.forName("SomeImpl", true, currentLoader)
, где currentLoader
будет загрузчиком классов вызывающего. См. Подробности здесь.
Второй метод требует, чтобы первый загрузчик классов выбирался первым. Не пишите, как ClassLoader.loadClass("SomeImpl")
, поскольку это не статический метод. Вам потребуется что-то вроде
final ClassLoader cl = this.getClass().getClassLoader();
Class theClass = cl.loadClass("SomeImpl");
Помните, что подклассы ClassLoader должны переопределять метод findClass, а не loadClass
. Это то же самое, что вызов метода (защищенного) loadClass("SomeImpl", false)
, где второй аргумент указывает, должно ли быть выполнено соединение или нет.
Есть более тонкие отличия... Метод loadClass
ожидает имя двоичного класса, заданное спецификацией языка Java, а forName
также может использоваться со строками, представляющими примитивные типы или классы массивов.
В целом, лучше всего использовать Class.forName
, если необходимо указать конкретный загрузчик классов и должен ли он быть инициализирован или нет, а затем дать возможность реализовать остальные. Использование загрузчиков классов напрямую полезно для поиска ресурсов в банке или в пути к классам.
Ответ 4
Эта строка не будет компилироваться:
Class theClass = ClassLoader.loadClass("SomeImpl");
потому что loadClass не является статическим методом ClassLoader.
Чтобы устранить эту проблему, создайте объект ClassLoader следующим образом одним из трех возможных способов:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = Main.class.getClassLoader(); // Assuming in class Main
ClassLoader classLoader = getClass().getClassLoader(); // works in any class
затем вызовите:
Class theClass = ClassLoader.loadClass("SomeImpl");
-dbednar
Ответ 5
Метод loadClass()
не может быть вызван как static
. Создайте подкласс для ClassLoader
и еще несколько других методов для выполнения операций. Можно создать собственный загрузчик классов, расширив класс ClassLoader
. В функциональном оба пути одинаковы.