类加载器无法完全按照您的预期执行。
引用相关问答的another answer:
Java 将始终使用加载正在执行的代码的类加载器。
你的例子:
public static void main(String[] args) {
// whatever you do here...
Car car = new Car(); // ← this code is already bound to system class loader
}
你能得到的最接近的方法是使用 child-first (parent-last) 类加载器,例如 this one,用你的 jar 实例化它,然后使用反射创建一个Car 来自那个罐子。
Cara.jar 内的类:
package com.acme;
public class Car {
public String honk() {
return "Honk honk!";
}
}
您的主要应用程序:
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
ParentLastURLClassLoader classLoader = new ParentLastURLClassLoader(
Arrays.asList(new File("/home/lib/custom/a.jar").toURI().toURL()));
Class<?> carClass = classLoader.loadClass("com.acme.Car");
Object someCar = carClass.newInstance();
Object result = carClass.getMethod("honk").invoke(someCar);
System.out.println(result); // Honk honk!
}
注意:如果您的类路径中还有一个com.acme.Car 类,那不是同一个类,因为一个类是由其全名和类加载器标识的。
为了说明这一点,假设我使用了我的本地 Car 类,如下所示,carClass 由我的自定义类加载器按上述方式加载:
Car someCar = (Car) carClass.newInstance();
// java.lang.ClassCastException: com.acme.Car cannot be cast to com.acme.Car
可能会造成混淆,但这是因为名称本身并不能识别类。该演员表无效,因为这两个班级不同。它们可能有不同的成员,或者它们可能有相同的成员但实现不同,或者它们可能逐字节相同:它们不是同一个类。
现在,这不是一个很有用的东西。
当您的 jar 中的自定义类实现一个通用 API 时,这些东西变得很有用,主程序知道如何使用。
例如,假设接口Vehicle(具有方法String honk())在公共类路径中,而您的Car在a.jar中并实现Vehicle。
ParentLastURLClassLoader classLoader = new ParentLastURLClassLoader(
Arrays.asList(new File("/home/lib/custom/a.jar").toURI().toURL()));
Class<?> carClass = classLoader.loadClass("com.acme.Car");
Vehicle someCar = (Vehicle) carClass.newInstance(); // Now more useful
String result = someCar.honk(); // can use methods as normal
System.out.println(result); // Honk honk!
这类似于 servlet 容器的作用:
- 您的应用程序实现了 servlet API(例如,实现
javax.servlet.Servlet 的类)
- 它被打包成一个war文件,servlet容器可以通过自定义类加载器加载
- 部署描述符(
web.xml 文件)告诉 servlet 容器它需要实例化的 servlet(类)的名称(就像我们上面所做的那样)
- 那些是
Servlets 的类,servlet 容器可以这样使用它们