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

用.class文件创建对象

程序员文章站 2022-04-03 16:04:28
...

      第一步:  给你一个编译好的class文件以及它的包名,创建一个对象出来。

      1)class文件源代码

      

package com.wsc.classloader;

public class Tool{

	public void print() {
	}
}

    2)使用javac Tool.java 编译成class文件

 

    3)将Tool.class文件读取到内存中,生成byte[]数组

    

	/**
	 * 加载class文件
	 * 
	 * @param clazzPath
	 *            class绝对文件路径
	 * @return 字节数组
	 * @throws IOException
	 */
	private byte[] loadClassFile(String clazzPath) throws IOException {
		FileInputStream fis = new FileInputStream(clazzPath);
		BufferedInputStream bis = new BufferedInputStream(fis);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		byte[] buffer = new byte[1024 * 256];
		int ch = 0;
		while ((ch = bis.read(buffer, 0, buffer.length)) != -1) {
			baos.write(buffer, 0, ch);
		}
		return baos.toByteArray();
	}

      4)自定义ClassLoader,使用ClassLoader中的defineClass方法:protected final Class<?> defineClass(String name, byte[] b, int off, int len)。参数分别是类名称,class文件对应的字节数组,起始位置和终止位置。

     

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class<?> c = findLoadedClass(name);
		if (c == null) {
			c = defineClass(name, data, 0, data.length);
		}
		return c;
	}

 整体代码是:

package com.wsc.classloader;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class ClassLoaderOne extends ClassLoader {

	public static void main(String[] args) throws Exception {

		ClassLoaderOne loader = new ClassLoaderOne(
				"E:\\JAVA\\JAVAFX\\ClassLoader\\libs\\Tool.class");
		Class<?> clazz = loader.loadClass("com.wsc.classloader.Tool");
		Object o = clazz.newInstance();
		System.out.println(o.getClass().getClassLoader());

	}

	private byte[] data;

	public ClassLoaderOne(String clazzPath) throws IOException {
		data = loadClassFile(clazzPath);
	}

	/**
	 * 加载class文件
	 * 
	 * @param clazzPath
	 *            class绝对文件路径
	 * @return 字节数组
	 * @throws IOException
	 */
	private byte[] loadClassFile(String clazzPath) throws IOException {
		FileInputStream fis = new FileInputStream(clazzPath);
		BufferedInputStream bis = new BufferedInputStream(fis);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		byte[] buffer = new byte[1024 * 256];
		int ch = 0;
		while ((ch = bis.read(buffer, 0, buffer.length)) != -1) {
			baos.write(buffer, 0, ch);
		}
		return baos.toByteArray();
	}

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class<?> c = findLoadedClass(name);
		if (c == null) {
			c = defineClass(name, data, 0, data.length);
		}
		return c;
	}

}

 感觉是这样的,跑一下:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
	at java.lang.ClassLoader.preDefineClass(Unknown Source)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at com.wsc.classloader.ClassLoaderOne.main(ClassLoaderOne.java:14)

   意思是:禁止加载名为java.lang的包。

   原因是:虽然Tool类中没有使用任何引入java.lang下类,但是它的父类Object是在java.lang下的,classloader加载Tool类时会把它所有的关系网都加载出来才行,父类Object肯定是要加载的。

   这样就简单了!无非多写一个If else.使用父加载器(类加载器都有一个父类加载器)加载即可。

  

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class<?> c = findLoadedClass(name);
		if (name.equals("java.lang.Object")) {
			ClassLoader parent = getParent();
			c = parent.loadClass(name);
		}
		if (c == null) {
			c = defineClass(name, data, 0, data.length);
		}
		return c;
	}

   跑一下结果是:

  

com.wsc.classloader.ClassLoaderOne@ca470

  第二步:新的问题

    

		Method[] methods = clazz.getMethods();
		for (int i = 0; i < methods.length; i++) {
			String name = methods[i].getName();
			System.out.println(name);
			Class<?>[] params = methods[i].getParameterTypes();
			for (int j = 0; j < params.length; j++) {
				System.out.println(params[j].toString());
			}
		}

   这个时候还是会报刚才的错误,因为Method类也在java.lang包下,只能在增加一个If else.

   显然,代码应该这样写

   

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class<?> c = findLoadedClass(name);
		if (c == null) {
			// 如果父加载器不为null,使用父类加载器加载(比如Object,HashMap等核心类)
			if (getParent() != null) {
				try {
					c = getParent().loadClass(name);
				} catch (Exception e) {
					// 父类可能没加载,则抛异常
				}

			}
			// 如果父类加载器没有加载,再使用自定义加载器加载
			if (c == null) {
				c = defineClass(name, data, 0, data.length);
			}
		}

		return c;
	}

   打印结果:

  

toString
print
class java.lang.String
getClass
hashCode
equals
class java.lang.Object
notify
notifyAll
wait
long
int
wait
wait
long
com.wsc.classloader.ClassLoaderOne@fcfa52

   第三步:如果本地可以通过.class文件创建,远程当然也已同一个道理(如果需要加密,在本地多一个解密即可)。如果class文件是远程调用的话,本地一般使用接口或者反射两种方法调用。首选是接口,反射一是效率,而是要清楚所有的方法名称、参数名称过于麻烦。

  由于远程加载class文件到本地,如果出错很难定位出错位置。幸好,classloader使用规则默认是根据URLClassLoader来使用的,会先根据检查本地是否有该类,所以可以直接将源码放在本地即可调试,当然发布的时候一定要删除。

   如图:
用.class文件创建对象
            
    
    博客分类: JAVA基础 类加载器classloaderJVM
    通过这个基本的入门程序可以了解ClassLoader的基本流程。
    

   

 

  • 用.class文件创建对象
            
    
    博客分类: JAVA基础 类加载器classloaderJVM
  • 大小: 10.9 KB