• 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文件载入再创建实例。

  • 相关阅读:
    【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)
    php monolog 的写日志到unix domain socket 测试终于成功
    dhcp 过程
    【转】nginx 和 php-fpm 通信使用unix socket还是TCP,及其配置
    php 获取TZ时间格式
    React.Fragment 包裹标签
    git 关联远程分支
    select * from (select user())a 为什么是查询user()的意思?
    ant Form 常用 api
    antd-design LocaleProvider国际化
  • 原文地址:https://www.cnblogs.com/localhost/p/2626092.html
Copyright © 2020-2023  润新知