有三种类型的Java内置ClassLoader.
引导类加载器(Bootstrap Class Loader):用于加载jdk内部类。通常加载rt.jar和其它的核心类,例如java.lang.*包类。
扩展类加载器(Extensions Class Loader):它加载来自于jdk扩展的目录的类,通常是$JAVA_HOME/lib/ext目录。
系统类加载器(System Class Loader):它加载的当前类路径的类可以被设置的同时,调用 可以使用-cp或者-classpath,命令行选项的程序。
ClassLoader是分级的,当一个类被加载,那么先找其父类加载器去加载,在运行时,保持这种方式,不断往上一级一级的加载,如果父类加载器没有找到类,再由加载器本身去加载类。
那么输入以下ClassLoader:
System.out.println(String.class.getClassLoader());
System.out.println(ZipInfo.class.getClassLoader());
System.out.println(Connection.class.getClassLoader());
System.out.println(Gun.class.getClassLoader());
输出为:
null
sun.misc.Launcher$ExtClassLoader@6fd7bd04
sun.misc.Launcher$AppClassLoader@2ac510e3
sun.misc.Launcher$AppClassLoader@2ac510e3
其中Gun 类就是之前声明的枪类。
可以看出引导类加载器加载的类在jdk的lib/目录下不包括ext目录部分,但是无法得到类加载器,这是为什么呢?不为什么,Java规定的。
扩展类加载器就是加载jdk里面lib/ext目录下的。ExtClassLoader.
系统类加载器就是加载我们自己创建的类和引入的三方的库类等。AppClassLoader.
由此可以看出我们自己定义的所有类,引用的其它库类都是APPclassLoader来加载的。
Java类的加载就知道了,那么类的静态加载和动态加载是怎么回事呢?
Java类静态加载,就是我们通常的new 一个对象的时候,这个对象在编译的时候就已经被提供了。
可以这么理解Java的动态加载。在Java运行时,类生成的对象,没有被预先提供。而去加载这个类。
动态编译使用类文件:
ClassLoader加载的类是.class文件到内存。那么从.java文件编译到.class。我们怎么主动去做?把它编译成.class文件。Java给提供了一些方法。
首先我们创建一个Dagger类:
public class Dagger {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
并放到D盘的sun目录下;
JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager filemanager =
complier.getStandardFileManager(null, null, null);
Iterable it = filemanager.getJavaFileObjects("D:\\sun\\Dagger.java");
CompilationTask task = complier.getTask(null, filemanager, null, null, null, it);
task.call(); //
try {
filemanager.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
执行完这些代码后,会发现多了Dagger.class:
那么我们怎么使用这个Dagger.class呢?
我们的.java类文件不是在项目的库中或者项目的java源目录中,而是在其它目录,其它地方。比如:项目在D盘创建,而我需要的另一个.java文件在C盘,那么怎么用C盘的这个类呢?
由我们的类加载器完成:
File file=new File("D:\\sun\\");
URL url=null;
try {
url = file.toURL();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
URLClassLoader urlClassLoader=new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
Class<?> loadClass = urlClassLoader.loadClass("Dagger");
Object newInstance = loadClass.newInstance();
Field declaredField = loadClass.getDeclaredField("name");
declaredField.setAccessible(true);
declaredField.set(newInstance, "瑞士军刀");
Method method = loadClass.getMethod("getName");
Object invoke = method.invoke(newInstance);
System.out.println(invoke); 注意:里面的try cac
he太多,省略。
输出为:
瑞士军刀
可以看出,我们可以直接调用来自于各个地方的.class类文件,包括从网络中获取的,也就是说,在更新程序的时候,只需要更新几个类的这种,就简单的多了,程序运行时就可以升级。不必重启程序。
显然,我们需要的不仅仅是一个或者几个类 的改变。会是一个jar包的形式。
动态加载jar包
新建一个类:
package com.test2.util;
public class Gun {
private String name;
private int bullet;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBullet() {
return bullet;
}
public void setBullet(int bullet) {
this.bullet = bullet;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "{"+name+","+bullet+","+address+"}";
}
}
把这个包打成jar包的形式,暂且命名test2.jar.放到d盘的sun目录下。
那么引用jar包
File file=new File("D:\\sun\\test2.jar");
URL url=null;
try {
url = file.toURL();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
URLClassLoader urlClassLoader=new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
Class<?> loadClass = urlClassLoader.loadClass("com.test2.util.Gun");
Object newInstance = loadClass.newInstance();
Field name = loadClass.getDeclaredField("name");
Field bullet=loadClass.getDeclaredField("bullet");
Field address=loadClass.getDeclaredField("address");
name.setAccessible(true);
bullet.setAccessible(true);
address.setAccessible(true);
name.set(newInstance, "手枪");
bullet.set(newInstance, 20);
address.set(newInstance, "美国");
Method method = loadClass.getMethod("toString");
Object invoke = method.invoke(newInstance);
System.out.println(invoke);
注意:里面的try cache太多,省略。
执行这段代码输出: