做自己的类加载器

虚拟机的核心是通过类加载器来加载.class文件,然后进行相应的解析执行。那么我们可以自己做类加载器,手动加载需要的.class以进行解析执行,从而扩展虚拟机的功能。

以下内容摘自API文档:

应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。

网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。

代码示例:

自己的类加载器 MyClassLoader

package cn.hncu;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import org.junit.Test; public class MyClassLoader extends ClassLoader{
public Class<?> findClass(String name){
//name = "e:\\cn\\hncu\\Person.class"
Class c = null;
FileInputStream in;
byte[] b=null; //通过IO或网络把字节码数据读取到buf[]当中。进一步地,
//如果我们自己熟悉字节码的生成格式,那么也可自己用程序生成。
//本例,我们是把硬盘中的一个外部字节码文件的数据读取到buf[]当中
//1
try {
in = new FileInputStream(name);
byte[] buf = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();//字节流
int len=0;
while((len=in.read(buf))!=-1){
baos.write(buf, 0, len);
}
in.close();
baos.close();
b = baos.toByteArray();
//2 ---1-2这里可以抽取出来写一个loadClassData方法
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
c = defineClass("cn.hncu.Person", b, 0, b.length);
return c;
} @Test
public void testClassData() throws ReflectiveOperationException{
String className="cn.hncu.Person";
//用Java的类加载器加载一个
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
System.out.println((Person)obj); System.out.println("-------------------");
className = "e:\\cn\\hncu\\Person.class";
Class c2 = findClass(className);
Object obj2 = c2.newInstance();
System.out.println(obj2); System.out.println((Person)obj2);//这句是有问题的
//※不同类加载器加载的对象是无法强转---可以理解是不同的生存空间
//Person p2 = (Person) obj2;//会挂的。
//因为obj2的生存空间是MyClassLoader,而Person的生成空间是AppClassLoader
//System.out.println(p2); } }

测试结果:

看,最后那句不能输出吧。

因为不是一个类加载器的。

作自己的测试工具MyJUnit

(注解与反射共同使用的案例 )

相关说明:

1)JUnit用的是@Test注解,我们用@MyTest注解。

2)JUnit已经嵌入到MyEclipse当中,我们自己的MyJUnit只要能独立运行就可以(不嵌入),同时这样我们也不方便在MyJUnit中以参数方式接收到被测试类的类名与方法名,只能以键盘输入的方式接收。

3)JUnit能实现指定单个方法来调用执行,由于不能利用MyEclipse传参,因此我们在MyJUnit程序中遍历所有的方法并通过判断是否声明@MyTest注解来决定是否调用执行该方法。

下面实现了运行任意目录下的实现了@MyTest注解的方法:

需要输入绝对路径名和类的完整名字。

注解:@MyTest

package cn.hncu.myJunit;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)//运行时也存在,必须要加这个
@Target (ElementType.METHOD)//限制注解只能加在方法上
public @interface MyTest { }

测试类:TestPerson

package cn.hncu.myJunit;
/**
* 测试用的
* @author 陈浩翔
*
* @version 1.0 2016-5-6
*/
public class TestPerson { public void run1(){
System.out.println("run1...");
} @MyTest
public void run2(){
System.out.println("run2...");
} public void run3(){
System.out.println("run3...");
} @MyTest
public void run4(){
System.out.println("run4...");
} public void run5(){
System.out.println("run5...");
} }

MyClassLoader类:自己写的类加载器

package cn.hncu.myJunit;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; /**
* 自己写的类加载器
* @author 陈浩翔
*
* @version 1.0 2016-5-6
*/
public class MyClassLoader extends ClassLoader{ //我把它分成2个方法写了。
public Class<?> findClass(String name, String className) {
try {
byte b[] = loadClassData(name);
Class c = defineClass(className, b, 0, b.length);
return c;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
} private static byte[] loadClassData(String name) throws IOException {
byte buf[] = new byte[1024];
FileInputStream in = new FileInputStream(name);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len=0;
while((len=in.read(buf))!=-1){
out.write(buf, 0, len);
}
in.close();
out.close();
byte b[] = out.toByteArray();
return b;
}
}

main方法类:

package cn.hncu.myJunit;

import java.lang.reflect.Method;
import java.util.Scanner; import cn.hncu.myJunit.MyClassLoader; /**
* @author 陈浩翔
* @version 1.0 2016-5-6
*/
public class MyJunit { public static void main(String[] args) throws ReflectiveOperationException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入需要运行的类的绝对路径(路径中不能有空格,需要类的.class文件):");
String name = sc.next();
System.out.println("请输入类的名称(包含包名):");
String className = sc.next();
Class c = (new MyClassLoader()).findClass(name, className);
//获得那个类了。 //那个类必须要有空参构造方法
Object obj = c.newInstance(); //获得这个类所有声明的方法,包括私有的
Method ms[] = c.getDeclaredMethods();
for(Method m:ms){
if(m.isAnnotationPresent(MyTest.class)){
m.invoke(obj, null);
}
}
}
}

运行测试结果:

现在我把class文件移动到D盘了。

再看运行结果:

这个可以有很多改进的地方,就比如每次输入路径都很麻烦,

我们可以做一个图形界面,让我们自己选择。

这样就方便多了。

Java---注解、类加载器-加强-实现运行任意目录下class中加了@MyTest的空参方法的更多相关文章

  1. Java---实现运行任意目录下class中加了@MyTest的空参方法(实现图形界面)

    说明: 因为上个代码,总是要输入完整的绝对路径,比较麻烦,于是,就写了这个小程序,直接进入文件对话框选择需要运行的class文件. 只需要提前输入完整的类名. 注意:加的MyTest必须打个包,加上: ...

  2. Java虚拟机类加载器及双亲委派机制

    所谓的类加载器(Class Loader)就是加载Java类到Java虚拟机中的,前面<面试官,不要再问我"Java虚拟机类加载机制"了>中已经介绍了具体加载class ...

  3. Java 虚拟机类加载器

    虚拟机设计团队把类加载阶段张的”通过一个类的全限定名来获取此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为”类加载器”. ...

  4. java面向对象--类加载器及Class对象

    类加载器 jvm 和 类的关系 当调用 java命令运行一个java程序时,会启动一个java虚拟机进程.同一个jvm的所有线程.所有变量都处于同一个进程里,都使用该jvm进程的内存区. jvm进程终 ...

  5. java ClassLoader类加载器

    原文 首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java ...

  6. Java之类加载器(Class Loader)

    JVM默认有三个类加载器: Bootstrap Loader Bootstrap Loader通常有C编写,贴近底层操作系统.是JVM启动后,第一个创建的类加载器. Extended Loader E ...

  7. 深入探讨java的类加载器

    类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1.0 就出现了,最初是为了满足 Ja ...

  8. java自定义类加载器

    前言 java反射,最常用的Class.forName()方法.做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是ec ...

  9. Java的类加载器

    一.类加载器的概念 类加载器(class loader)用来加载 Java 类到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 ...

随机推荐

  1. Java实现ajax

    jsp端的代码,sucess:function(){} 里面就是返回的处理 function ChangeTime(){ alert("www"); var startYmd = ...

  2. Axiom3D学习日记 5.Frame Listeners, and Input Handling

    Frame Listeners In Ogre's C++, we would register a class to receive notification before and after a ...

  3. 利用SQLiteOpenHelper创建数据库,进行增删改查操作

    Android中提供SQLiteOpenHelper类,在该类的构造器中,调用Context中的方法创建并打开一个指定名称的数据库对象.继承和扩展SQLiteOpenHelper类主要做的工作就是重写 ...

  4. app包中的fragment和v4包中的fragment的使用的区别

    app包中的fragment和v4包中的fragment的使用的区别 1.尽量不要用app包中的fragment,因为这个是在3.0之后才有的,支持的版本太高,在低版本中是是用不了的 2.androi ...

  5. JAVA package与import机制

    JAVA package与import机制 http://files.cnblogs.com/files/misybing/JAVA-package-and-import.pdf import org ...

  6. Android开发--去掉标题栏

    Android开发中为了尽可能美观,会去掉标题栏.去掉标题栏有三种方法. 一.在Activity代码里实现 在代码中实现以下方法: this.requestWindowFeature(Window.F ...

  7. 最简单的基于FFmpeg的推流器(以推送RTMP为例)

    ===================================================== 最简单的基于FFmpeg的推流器系列文章列表: <最简单的基于FFmpeg的推流器(以 ...

  8. T-SQL语言基础

    1.T-SQL语言 CREATE:创建新对象,包括数据库.表.视图.过程.触发器和函数等常见数据库对象. ALTER:修改已有对象的结构. DROP:用来删除已有的对象.有些对象是无法删除的,因为它们 ...

  9. nuc970连接jlink进行单步调试的设置

    在 USB mode 下, 先跟 NuWriter 接上, 然后用以下的设定. 按 Keil 的 debug (不是 download to flash)就可以接上了.

  10. ARM平台的内核模块编写与安装

       Linux 系统一直在不断地发展,而相应地她的代码量也在不断的增大,直接导致的结果就是她的可执行镜像就变得越来越庞大.那么问题来了,如果将所有的镜像文件一次性地复制到内存中,那么所需的空间就非常 ...