Java中能够在运行时加载和从新加载类,虽然并不像咱们想像中那么简单。本文将解释什么时候、怎样在Java中加载、从新加载类。
你能够争论动态加载类是Java反射的一部分仍是Java核心的一部分。无论怎样,我把它放在了Java反射中,由于没有更好的地方放置它。java
Java程序的全部类都是使用 java.lang.ClassLoader
的一些子类加载的。所以,动态加载类也必须使用 java.lang.ClassLoader
的子类。
当一个类加载,它所引用的类也会被加载。类加载模式是递归加载的,直到全部须要的类加载完毕。这可能并非应用程序的全部类。未被引用的类在引用前不会被加载。web
类加载在Java中被组织成层级。当你建立一个独立的ClassLoader
,你必须提供一个父级ClassLoader
。若是ClassLoader
被请求加载一个类,它会请求它的父级ClassLoader
去加载它。若是父级类加载器找不到这个类,子类加载器会尝试自加载。ide
类加载器加载类的步骤以下:编码
当你实现一个可以重载类的类加载器时,你须要从这个序列中偏离一点。不该请求父类加载程序加载要重装的类。稍后再谈。url
动态加载类很是简单。全部你须要作的是得到一个ClassLoader
并调用它的loadClass()
方法。示例以下:设计
public class MainClass { public static void main(String[] args){ ClassLoader classLoader = MainClass.class.getClassLoader(); try { Class aClass = classLoader.loadClass("com.jenkov.MyClass"); System.out.println("aClass.getName() = " + aClass.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
动态类从新加载有一些挑战。Java内建的类加载器在加载类以前总会检查类是否已被加载。所以,使用Java的内置类加载器不可能从新加载类。从新加载一个类你必须实现本身的ClassLoader
子类。
即便使用类加载器的自定义子类,也会遇到挑战。全部已被加载的类都须要被连接。这个方法是final的,所以不能被你的ClassLoader
子类重载。resolve()
方法不容许ClassLoader
实例连接一个类2次。所以,每当你须要从新加载类时,你必须从新建立一个ClassLoader
类的实例。这不是不可能的,但必须知道什么时候设计类从新加载。code
如上文述,不能使用加载指定类的ClassLoader
从新加载这个类。所以,必须使用不一样的ClassLoader
加载这个类。可是,这会带来新的问题。
Java程序中加载的每个类都以其全限定名(包名+类名)标识,而且由ClassLoader
实例加载。这意味着,类MyObject
由类加载器A加载,是和由类加载器B加载的同一个类MyObject
不相同。模拟代码以下:orm
MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
注意,类MyObject
在代码中是如何引用的,是做为object
类型的变量。这致使MyObject
类被已加载过这个类的驻留代码的类加载器加载。
若是myClassReloadingFactory
对象工厂使用与驻留代码不一样的类加载器加载MyObject
,你不能强制转换从新加载的Object
类型的变量MyObject
为MyObject
类型。由于这两个MyObject
由不一样的类加载器加载,他们被视为不一样的类,尽管他们拥有相同的全限定名。尝试强转一个object的类为另外一个类的引用将抛出ClassCastException
。
有可能绕过这个限制,可是你必须用两种方式来改变你的代码:对象
这里是示例代码:继承
MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
若是变量类型是接口或超类,上面的代码都会正常运行,接口或超类在从新加载实现或子类时不会被从新加载。
为了上面代码的正常运行,你固然须要实现本身的类加载器,让接口或超类由其父类加载。当你的类加载器被请求加载MyObject
时,它也会被请求加载MyObjectInterface
接口或者MyObjectSuperclass
类,由于它们被MyObject
类在内部引用。你的类加载器必须把类加载委派给相同的类加载器,即加载了接口或超类的类加载器。
上文包含了不少内容。让咱们看一下简单的示例。下面是一个简单的ClassLoader
子类。注意它如何将类加载委托给它的父类,除了它想要重装的一个类以外。若是类加载被委派给了它的父类,它之后将不能被从新加载。记住,一个类只能被同一个ClassLoader
实例加载。
如前所述,这只是一个示例,它显示了类加载器的行为的基本知识。这并非一个你的类加载器的生产就绪的模板。你的类加载器可能并不只限于一个类,多是一个你想要从新加载的类的集合。此外,你也不能硬编码class path。
public class MyClassLoader extends ClassLoader{ public MyClassLoader(ClassLoader parent) { super(parent); } public Class loadClass(String name) throws ClassNotFoundException { if(!"reflection.MyObject".equals(name)) return super.loadClass(name); try { String url = "file:C:/data/projects/tutorials/web/WEB-INF/" + "classes/reflection/MyObject.class"; URL myUrl = new URL(url); URLConnection connection = myUrl.openConnection(); InputStream input = connection.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = input.read(); while(data != -1){ buffer.write(data); data = input.read(); } input.close(); byte[] classData = buffer.toByteArray(); return defineClass("reflection.MyObject", classData, 0, classData.length); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
下面是使用MyClassLoader
的示例:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader(); MyClassLoader classLoader = new MyClassLoader(parentClassLoader); Class myObjectClass = classLoader.loadClass("reflection.MyObject"); AnInterface2 object1 = (AnInterface2) myObjectClass.newInstance(); MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance(); //create new class loader so classes can be reloaded. classLoader = new MyClassLoader(parentClassLoader); myObjectClass = classLoader.loadClass("reflection.MyObject"); object1 = (AnInterface2) myObjectClass.newInstance(); object2 = (MyObjectSuperClass) myObjectClass.newInstance(); }
reflection.MyObject
类是由自定义类加载器加载的。注意,它是如何继承一个超类、实现一个接口的。这只是为了这个例子。在你的代码中,只须要两个中的一个,继承超类或实现接口。
public class MyObject extends MyObjectSuperClass implements AnInterface2{ //... body of class ... override superclass methods // or implement interface methods }