关于Java反射机制

最近在对以后平台进行优化,原先每次部署新模块,或者更新模块都需要重起进程,准备改成增加新模块自动部署,更新模块也能重新加载。

原先系统是在每次启动时从一张配置表里读取每个模块的类路径,然后通过反射机制实例化模块,之后就再不管那张配置表了。优化过程中,在系统增加了一个线程,专门用于检测配置表是否有模块更新需求或新增加的模块。如有的话则重新加载模块或新增模块。新增模块的工作很顺利,但在更新模块时却遇到一些问题。

刚开始时重新加载一个模块,我认为只要重新执行一次Class.forName(classpath),然后在newInstance()一下就万事大吉了,没想到重新执行一次Class.forName(classpath).newInstance()后的模块还是老的,压根就没有更新,试了多次依然如此。于是到网上google一下,查阅了相关资料,发现了一些问题:

在Java 1.2后,java class的加载采用所谓的委托模式(Delegation Modle),当调用一个ClassLoader.loadClass()加载一个类的时候,将遵循以下的步骤:
1)检查这个类是否已经被加载进来了?
2)如果还没有加载,调用父对象加载该类
3)如果父对象无法加载,调用本对象的findClass()取得这个类。

也就是说,如果这个类已经被加载了,JVM就不会在重新加载一遍。这个问题有点郁闷了。java有没有卸载类的方法呢:

当一个java class被加载到JVM之后,它有没有可能被卸载呢?我们知道Win32有FreeLibrary()函数,Posix有dlclose()函数可以被调用来卸载指定的动态连接库,但是Java并没有提供一个UnloadClass()的方法来卸载指定的类。在Java中,java class的卸载仅仅是一种对系统的优化,有助于减少应用对内存的占用。既然是一种优化方法,那么就完全是JVM自行决定如何实现,对Java开发人员来说是完全透明的。在什么时候一个java class/interface会被卸载呢?Sun公司的原话是这么说的:"class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded."

卸载类的方法是没有的,不过我们有一些变通的方法可以实现卸载,动态反射创建的类是可以被JVM收回的,跟对象一样,只要打断所有指向该类的引用就ok。下面是从《深入java虚拟机第二版》一书中学得的方法:

##############   首先创建一个自己的类加载器 #####################

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

/**
 *
 * @author dingwy
 *
 */
public class MyClassLoader extends ClassLoader {
 private String basepath;

 public MyClassLoader(String basepath) {
  this.basepath = basepath;
 }

 public MyClassLoader(ClassLoader parent, String basepath) {
  super(parent);
  this.basepath = basepath;
 }

 public Class findClass(String className) throws ClassNotFoundException {
  byte classData[];
  classData = getTypeFromBasePath(className);
  if (classData == null) {

  }
  return defineClass(className, classData, 0, classData.length);
 }

 public byte[] getTypeFromBasePath(String typeName) {
  FileInputStream fis = null;
  String fileName = basepath + File.separatorChar + typeName.replace('.', File.separatorChar)
    + ".class";
  try {
   fis = new FileInputStream(fileName);
  } catch (Exception e) {
   e.printStackTrace();
   return null;
  }
  BufferedInputStream bis = new BufferedInputStream(fis);
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  try {
   int c = bis.read();
   while (c != -1) {
    out.write(c);
    c = bis.read();
   }
  } catch (Exception e) {
   e.printStackTrace();
   return null;
  }
  return out.toByteArray();
 }
}

##############   创建一个接口 #####################

public interface TestInterface {
      public void test();         }

##############   扩展接口 #####################

public class TestA implements TestInterface {

     public void test() {

         System.out.println("1");

     }

}

###############  测试 #######################

public class test {

   public static void main(String[] args) {

while(true) {

   try {

    MyClassLoader myClassLoader = new MyClassLoader(
      "D://");

    Class c = myClassLoader
      .findClass("TestA");
    Object obj = c.newInstance();
    Test t = (Test) obj;

    System.out.print("1  ");
    t.test();

    myClassLoader = null;
    c = null;
    obj = null;
    t = null;

           Thread.sleep(2000);
         } catch (Exception e) {
            e.printStackTrace();
        }

     }

}

}

注:在test类运行过程中,可以改变TestA 中输出的值观察输出结果,如果是使用Class.ForName()进行类加载的话,输出内容是不会变的,而使用自定义的加载器加载则可以。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值