生命周期有:加载(Loading)--》验证(Verification)--》准备(Preparation)---》解析(Resolution)--》初始化(Initiation)---》使用(Using)----》卸载(Unloading)。 其中标黄的验证---》准备---》解析被称为连接(Linking)。
就代码执行而言:
1.连接阶段:不执行程序员代码
2.加载阶段:可以重写ClassLoader来执行我们的代码
3.连接后阶段:执行该类的代码
ClassLoader是为了将class文件的byte数组,主要的方法是findClass用于查找类。
Java7有个迅速将File编程byte[]的方法:
byte[] cLassBytes = null;
Path path;
try {
path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
cLassBytes = Files.readAllBytes(path);
每个ClassLoader对象都有一个ClassLoader类型的字段,暂且成为parent。当调用本类的loadClass方法加载类时,会先调用parent的ClassLoader,如果他没找到,才会调用本类的findClass方法去查找类。(这样,应用的Launcher$AppClassLoader也可以通过BootStrapClassLoader来调用String类了)。
系统查找类的顺序是:
1).Bootstrap classes: the runtime classes in rt.jar, internationalization classes in i18n.jar, and others.
2).Installed extensions: classes in JAR files in the lib/ext directory of the JRE, and in the system-wide, platform-specific extension directory (such as /usr/jdk/packages/lib/ext on the Solaris™ Operating System, but note that use of this directory applies only to Java™ 6 and later).
3).The class path: classes, including classes in JAR files, on paths specified by the system property java.class.path. If a JAR file on the class path has a manifest with the Class-Path
attribute, JAR files specified by the Class-Path
attribute will be searched also. By default, thejava.class.path
property's value is .
, the current directory. You can change the value by using the -classpath or -cp command-line options, or setting the CLASSPATH
environment variable. The command-line options override the setting of the CLASSPATH
environment variable.
中文就是:
1)rt.jar等(这个jar是什么呢?你用java -verbose 类名 运行下就看到了:Loaded java.io.File from C:\Program Files\Java\jre1.8.0_31\lib\rt.jar,也就是经常调用的String、Long等类,属于SDK中的)(用BootStrapClassLoader加载)
2) SDK中的拓展类,包名为javax的(java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思);
(用Launcher$ExtClassLoader加载)
3) 系统环境变量CLASSPATH的路径,当前目录(或者用命令行带 -classpath重新)
(用Launcher$AppClassLoader加载)
可是我的类文件在D:\MyScript\TestClassLoader.class不在上述三种。
2:解决方案
1.场景:我想加载一个d盘上的类,路径是:D:\MyScript\TestClassLoader.class,其不在上述三种情况。
通过重写一个ClassLoader进行加载,不过有现成的URLClassLoader我们就不要重造轮子了。
public class MyClassLoader extends ClassLoader{
@Override
protected Class> findClass(String className)
throws ClassNotFoundException {
byte[] cLassBytes = null;
//Java 7有下列API
Path path;
try {
path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
cLassBytes = Files.readAllBytes(path);
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
Class cLass = defineClass(cLassBytes, 0, cLassBytes.length);
return cLass;
}
}
Class.forName("TestClassLoader", true, new MyClassLoader());
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// This URL for a directory will be searched *recursively*
URL classes;
try {
classes = new URL( "file:///D:/MyScript/" );
ClassLoader custom =
new URLClassLoader( new URL[] { classes }, systemClassLoader );
// this class should be loaded from your directory
Class< ? > clazz = custom.loadClass( "TestClassLoader" );
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
URLClassLoader用的URL只能接受目录和jar包:
结尾: /代表该目录下的来; 非/而是文件则默认为jar包。
贴一个jar包的代码:
public URL findResource(String name) {
try {
File file = new File(jarFile);
String url = file.toURL().toString();
return new URL("jar:"+url+"!/"+name);
} catch (Exception e) {
return null;
}
}
另外我用反射来验证类是否加载成功了,clazz.newInstance()也可以生成相关代码。