JAVA运行时动态加载类

  想必大家在J2EE开发中一定会纳闷Servelt的改变,伴随的是Tomcat的重启。JAVA是否能够动态加载类呢?答案是肯定的。当然这不局限于J2EE,也可以做更多的拓展。
Let's Hack The Code:

Java Files List:
ClassLoaderTest/
        IC.java
        Test.java
ClassLoaderTest/1/
          IC.java
          C.java
ClassLoaderTest/2/
          IC.java
          C.java
// 定义接口
IC.java

public interface IC

{

  public void action();

}

  


//实现接口1
C.java 

public class C implements IC

{

  public void action(){

    System.out.println("Hi i am A class.");

  }

}

  


//实现接口2
C.java

public class C implements IC

{

  public void action(){  

    System.out.println("Hi i am A NEW! class.");

  }

}

  



//测试类
Test.java

import java.util.*;

import java.net.*;

import java.io.*;



public class Test implements Runnable

{

  public Test(){

    new Thread(this).start();

  }



  public void run(){

    System.out.println("do Test run()...");

    Scanner sc = new Scanner(System.in);

    while(true){

      System.out.print("\nPls enter the input.\n1. do one more time\n0.exit the program\nonly accepted '1' or '0'\n~>");

      String in = sc.next();

      if("1".equals(in)){

        System.out.println("**********************************");

        System.out.println("Do load().");

        load();

        System.out.println("\n\n");

      }else if("0".equals(in)){

        System.out.println("Bye.");

        System.exit(0);

      }else{

        continue;

      }

    }

  }



  private void load(){

    String jarName = "C.jar";

    try{

      File file = new File(jarName);

      URL url = file.toURL();

      URLClassLoader loader = new URLClassLoader(new URL[]{url});

      Class aClass = loader.loadClass("C");



      IC ic = (IC)aClass.newInstance();

      ic.action();

    }catch (Exception e){

      e.printStackTrace();

    }

  }



  public static void main(String[] args){

    new Test();

  }

}

  

测试:
这里没有用到包,写完以上代码要这样测试

1.
在此目录编译C.java
ClassLoaderTest/1/
          IC.java
          C.java

javac C.java

把C.class封装到jar中

jar cvf C1.jar C.class

2.
在此目录编译C.java
ClassLoaderTest/2/
          IC.java
          C.java

javac C.java

把C.class封装到jar中

jar cvf C1.jar C.class


3.
编译Test.java
ClassLoaderTest/
        IC.java
        Test.java

javac Test.java


4. 把C1.jar拷贝到ClassLoaderTest/目录并改名为C.jar
cp C1.jar ../C.jar
执行java Test.
此时输出为:

Pls enter the input.

1. do one more time

0.exit the program

only accepted '1' or '0'

~>1 

**********************************

Do load().

Hi i am A class.

  


现在用C2.jar替换C.jar

cp C2.jar ../C.jar

输入1继续执行,此时输出应为:

Pls enter the input.

1. do one more time

0.exit the program

only accepted '1' or '0'

~>1 

**********************************

Do load().

Hi i am A NEW! class.

  

结论:
通过以上方式,就可以实现运行时动态加载类。如果你只是想实现运行时动态加载类,只需把上面的代码做相应修改即可。
--------------------------------------------------------------------------------
更深入的研究

用Class.forName()替代。
在Test.java中的load()方法替换成

private void load(){

  try{

    Class aClass = Class.forName("C");

    IC ic = (IC)aClass.newInstance();

    ic.action();

  }catch (Exception e){

    e.printStackTrace();

  }

}

1.
在此目录编译C.java
ClassLoaderTest/1/
          IC.java
          C.java

javac C.java

2.
在此目录编译C.java
ClassLoaderTest/2/
          IC.java
          C.java

javac C.java

3.
编译Test.java
ClassLoaderTest/
        IC.java
        Test.java

4. 把../1/C.class拷贝到ClassLoaderTest/中

cp C.class ../

执行java Test.
此时输出为:

Pls enter the input.

1. do one more time

0.exit the program

only accepted '1' or '0'

~>1 

**********************************

Do load().

Hi i am A class.

再把../2/C.class拷贝到ClassLoaderTest/中
cp C.class ../
执行java Test.
此时输出为:

Pls enter the input.

1. do one more time

0.exit the program

only accepted '1' or '0'

~>1 

**********************************

Do load().

Hi i am A class.

很惊讶的发现,同样是调用了Class.newInstance()方法来获取一个新的对象。但JVM并没有载入我们新修改的.class文件。所以Class.forName()不能达到我们所期望的结果。


更多测试
速度比较,这里采用三种不同的方式测试。
1.通过URLClassLoader从jar文件中加载类并创建实例
2.通过Class.forName()加载类并创建实例
3.通过普通方式new创建实例

把A.java中的action()改为空方法。并且在几个Test.java的run()方法中,作如下修改。

int MAX = 50000;

long start = System.currentTimeMillis();

for(int i=0;i<MAX;i++)

  load();

long end = System.currentTimeMillis();

System.out.println("time:"+ (end-start) );

 

MAX=5万次
输出
1.time:15795
2.time:39
3.time:20
MAX=10万次
输出
1.time:29735
2.time:24
3.time:19

这两组测试中,同等条件下多次测试1/2所用时间都差不多,而3在执行多次后竟然时间趋于0,这也不排除是我机器原因。令人感到很有趣的是Class.forName()与普通的new在首次执行创建实例时,耗时几乎所一致的,所以这也可以判断,这两种方式在首次创建实例时都是从磁盘中把.class文件载入再创建实例。

你可能感兴趣的:(java)