欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 动态加载jar和class文件实例解析

程序员文章站 2023-10-20 12:53:07
本文研究的主要是java 动态加载jar和class文件的相关内容,具体如下。 java中类文件加载是动态的。也就是说当我们用到的时候才会去加载,如果不用的话,就不会去加...

本文研究的主要是java 动态加载jar和class文件的相关内容,具体如下。

java中类文件加载是动态的。也就是说当我们用到的时候才会去加载,如果不用的话,就不会去加载我们的类。

java为我们提供了两种动态机制。第一种是隐式机制。第二种是显示机制。如下:

两种方法:

  • 隐式机制 :new一个对象 + 调用类的静态方法
  • 显式机制 :由 java.lang.class的forname()方法加载
    由 java.lang.classloader的loadclass()方法加载

1、class.forname

class.forname()方法具有两个形式:

  • public static class forname(string classname)
  • public static class forname(string classname, boolean initialize,classloader loader)

参数说明:

  • classname - 所需类的完全限定名 (必须包含包名,否则出错!)
  • initialize - 是否必须初始化类 (静态代码块的初始化)
  • loader - 用于加载类的类加载器

调用只有一个参数的forname()方法等效于 class.forname(classname, true, loader)。

这两个方法,最后都要连接到原生方法forname0().

而三个参数的forname(),最后调用的是: forname0(name, initialize, loader);

不管使用的是new 來实例化某个类、或是使用只有一个参数的class.forname()方法,内部都隐含了“载入类 + 运行静态代码块”的步骤。

而使用具有三个参数的class.forname()方法时,如果第二个参数为false,那么类加载器只会加载类,而不会初始化静态代码块,只有当实例化这个类的时候,静态代码块才会被初始化,静态代码块是在类第一次实例化的时候才初始化的。

2、java.lang.classloader

classloader就是用来load class的,当一个class被加载的时候,这个class所引用到的所有class也会被加载,而且这种加载是递归的,也就是说,如果a引用到b,b 引用到c,那么当a被加载的时候,b也会被加载,而b被加载的时候,c也会加载。如此递归直到所有需要的class都加载好。

package com.demo.test;
import java.io.bytearrayoutputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.ioexception;
import java.lang.reflect.field;
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.method;
import java.net.malformedurlexception;
import java.net.url;
import java.net.urlclassloader;
public class dynamicloaddemo {
	enum filetype {
		jar, class, other
	}
	static class myclassloader extends classloader {
		public synchronized class<?> loadclass(string name, file file) throws filenotfoundexception {
			class<?> cls = findloadedclass(name);
			if(cls != null) {
				return cls;
			}
			fileinputstream fis = new fileinputstream(file);
			bytearrayoutputstream baos = new bytearrayoutputstream();
			byte[] buffer = new byte[1024];
			int len;
			try {
				while (true) {
					len = fis.read(buffer);
					if (len == -1) {
						break;
					}
					baos.write(buffer, 0, len);
				}
				//fileinputstream的flush是空操作,因为flush的作用是把缓存中的东西写入实体(硬盘或网络流)中,这里没有这种必要所以为空
				//baos.flush();
				byte[] data = baos.tobytearray();
				return defineclass(null, data, 0, data.length);
			}
			catch (ioexception e) {
				e.printstacktrace();
			}
			finally {
				try {
					baos.close();
				}
				catch (ioexception e) {
					e.printstacktrace();
				}
				try {
					fis.close();
				}
				catch (ioexception e) {
					e.printstacktrace();
				}
			}
			return null;
		}
	}
	public static void main(string[] args) {
		string classname = "com.demo.test.helloworld";
		string paths[] = { "helloworld.jar", "helloworld.class" };
		for (string path : paths) {
			string lowerpath = path.tolowercase();
			filetype filetype = filetype.other;
			if (lowerpath.endswith(".jar") || lowerpath.endswith(".zip")) {
				filetype = filetype.jar;
			} else if (lowerpath.endswith(".class")) {
				filetype = filetype.class;
			}
			if (filetype == filetype.other) {
				return;
			}
			file file = new file(path);
			if (!file.exists()) {
				return;
			}
			try {
				url url = file.touri().tourl();
				system.out.println(url.tostring());
				class<?> cls = null;
				switch (filetype) {
					case jar:
					          urlclassloader classloader = new urlclassloader(new url[] { url }, thread.currentthread().getcontextclassloader());
					cls = classloader.loadclass(classname);
					break;
					case class:
					          myclassloader myclassloader = new myclassloader();
					cls = myclassloader.loadclass(classname, file);
					break;
					default:
					          break;
				}
				if (cls == null) {
					return;
				}
				// 实例变量
				field field = cls.getdeclaredfield("hello");
				if (!field.isaccessible()) {
					field.setaccessible(true);
				}
				system.out.println(field.get(cls.newinstance()));
				// 调用静态不带参数方法
				method staticmethod = cls.getdeclaredmethod("saystatichello", null);
				if (!staticmethod.isaccessible()) {
					staticmethod.setaccessible(true);
				}
				// 如果函数的返回值是void,就会返回null
				staticmethod.invoke(cls, null);
				// 实例带参数方法方法
				method method = cls.getdeclaredmethod("say", string.class);
				if (!method.isaccessible()) {
					method.setaccessible(true);
				}
				object ret = method.invoke(cls.newinstance(), "hello world");
				system.out.println(ret);
			}
			catch (malformedurlexception e) {
				e.printstacktrace();
			}
			catch (classnotfoundexception e) {
				e.printstacktrace();
			}
			catch (nosuchmethodexception e) {
				e.printstacktrace();
			}
			catch (securityexception e) {
				e.printstacktrace();
			}
			catch (illegalaccessexception e) {
				e.printstacktrace();
			}
			catch (illegalargumentexception e) {
				e.printstacktrace();
			}
			catch (invocationtargetexception e) {
				e.printstacktrace();
			}
			catch (instantiationexception e) {
				e.printstacktrace();
			}
			catch (nosuchfieldexception e) {
				e.printstacktrace();
			}
			catch (filenotfoundexception e) {
				e.printstacktrace();
			}
		}
	}
}

结果:

Java 动态加载jar和class文件实例解析

总结

以上就是本文关于java 动态加载jar和class文件实例解析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!