关于java类的动态装载

一、原理:


(1)   它是java语言能力动态性的根源;研究理解它是每一个java设计和代码人员不能忽视的特点,因为很多底层和问题和不理解的技术都来源于它;可以这么说它是热插拨(plug and pay)的根本,是IOC(Sping/PicoContant)的技术最有力的支持,能善用它为设计和实现一个很好的框架必不可少的技术。对于其他技术(C/C++)没有动态特性的语言来说,要运用底层的OS系统所提供的机制,设计人员必須多費一些功夫来编写额外的程序代码(例如Windows 平台上需要使用LoadLibrary()與GetProcAddress()兩個Win32 API 来完成动态的需求),這些额外编写的程序代码也会因为平台的不同而不同,毕竟這些额外的程序代码本身的运作逻辑很少关联,所以维护起来虽不算复杂,但仍有其难度。


(2)Java 是一种天生就具有动态连接能力的技术。Java 把每個class的宣告、interface的宣告,在编译器处理之后,全部变成一个个小的可执行(类型档, .class),一旦我們指定一个具有public static void main(String args[])方法的class作为起点开始运作之后,JVM会找出所有在运行時期需要的执行单位,并将他们载入内存之中,彼此互相交互运作。尽管本质上是一堆类型文件,但是在内存之中,变成成了一個“逻辑上”为一体的Java应用程序。所以,严格的来说,每个class对VM,都是一個独立的动态连接库,只不過它的扩展名不是.dll 或.so,而是.class 而已。因为这种特性,所以我们可以在不重新编译其他Java 代码的情況下,只修改有问题的运行单位并放入文件系统之中,等到下次JVM重新启用的时候,這個逻辑上的Java 应有程序就会因为载入了新修改的.class文件,自己的功能也做了更新。這是一個最基本的动态功能。所以,严格的来说,每个class对JVM来说都是一个动态连接库。


(3)Java除了有动态性还有一定灵活性,要让程序有弹性就必须利用java的动态性,java使用两种方法来实现动态性。一种似隐式的(implicit),另一种是显式的(explicit),这两种方式与OS的使用的机制是一样的,只是代码的实现不同而已。A.隐式就是我们经常用到的new这个关键字动态长生一个对象。『一般使用Java 程式语言来开发应用程序中,很少有机会觉察到java因为具备了动态性之后所带来的优点和特性,甚至根本不曾利用这个Java 先天就具有的特性。而是因为這个动态的本质被巧妙地隐藏起來,使得使用Java 的开发人员在不知不觉中用到了用到而不自知』
B.显式又分为两种:一就是由java.lang.Class的forname()的方法;第二就是java.lang.ClassLoader的loadclass的方法。

二、实例

文件:Assemblyjava

public   interface  Assembly
{
    
public void start() ;
}


文件:Office.java

 

public   class  Office
{
public static void main(String args[]) throws Exception
{
//采用Class.forName的方式
Class c = Class.forName(args[0]) ; 
//采用ClassLoader 方式
//ClassLoader cl = Thread.currentThread().getContextClassLoader();
//Class c = cl.loadClass(args[0]) ; 
Object o = c.newInstance() ;
Assembly a 
= (Assembly) o ;
a.start() ;
}

}

文件:Word.java

public   class  Word  implements  Assembly

{

public void start()

{

System.out.println(
"Word starts") ;

}


}


文件:Excel.java

public   class  Excel  implements  Assembly

{

public void start()

{
    System.out.println(
"Excel starts") ;
}

}


运行结果:
注意: 请仔细看加入–verbose:class 之后的屏幕输出,您会看到Assembly.class 也被系统载入了。
由此您可以发现,interface 如同class 一般,會由编译器产生一個独立的文档(.class),类载入器载入类別时,
如果发现该类别继承了其他类,或是实现了其他接口,就会先载入代表該接口的类,也会载入其父类,如果父类也有其父类,
也会一起先载入。换一种说法,类载入器會依继承体系最上层的类往下依序载入,直到所有的夫类別都载入了,才轮到自己载入。
举例來说,如果有个类C 继承了类B、实现了接口I,而B 类又继承自A 类,那么载入的順序如下:A--〉B--〉I--〉C

三、自定义的classload

使用接口类,新调用的class是对它的具体实现
1) 写一个接口类 newface.class
2) 写接口文件实现 testfacea.class 更名为 testfacea.file 或其它文件名全可以
3) 主程序中调入文件到byte[]中,可以在本地文件调用,也可用网络无论如何只要能将
   编译后的文件内容的类代码放到 byte[]当中就可以
4) 转换成一个Class并初始化
5) 实现拉口
实际上就是对一个接口类用调入的文件实现,当然可以用不同的文件进行不同的实现
也可以对一个文件进行加解密操作,
要注意的是对一个要调入的文件,一定要是一个已经存在的接口类的实现

主程序类文件:

// 使用的主程序
public   class  testnewface  {
    
public static void main(String[] args) throws java.lang.Exception
     
{

//  共用初使化参数,开始
    Class testc;
    Object testo;
    cloader cl
=new cloader();
//  共用初使化参数,结束

//方法 1 的例子代码,newface是本地接口类,newface.class本地已经存在 开始
    testc=cl.load("testfacea.class","testfacea");
    testo
=testc.newInstance();

     System.out.println(
"Instance has over!");
    ((newface)testo).out(
"方法1 第(1)种使用方法");
     System.out.println(
"outsize="+((newface)testo).outsize("1111","aaaa"));
    }

}

//接口

public   interface  newface  {

       
public void out(String xx);
       
public int outsize(String x1,String x2);

 }



 

//实现接口的类--编译后的.class文件

public   class  testfacea  implements  newface... {

    
public void out(String xx)  ...{
    System.out.println(xx
+" for testfacea ");
    }

    
public int outsize(String x1,String x2)  ...{
      
return x1.length()+x2.length();
    }


}


//自己的ClassLoad类Loader

/*
要想自己完成从一个 byte[] 转换到一个Class 必须要 extends ClassLoader
因为ClassLoader中的方法defineClass是 protected  要使用只有 extends ClassLoader
*/

// import com.newface;
class  cloader  extends  ClassLoader   {
     
static int maxsize=10000;
      
public Class load(String namefile,String classname) throws java.lang.Exception
       
{
           Class result
=null;
          
try {

           
//进行判断这个class是否已经调入,已经有就直接返回,不然就调入
         Class ctmp=this.findLoadedClass(classname);
           System.out.println(ctmp.getName()
+" is load");
           
return ctmp;
           }

           
catch (Exception e)  {
           System.out.println(
"memory is null");
             }

        System.out.println(
"start..");
        java.io.FileInputStream in
=new java.io.FileInputStream("d://"+namefile);
        
byte[] classbyte=new byte[maxsize];
//实际应用时完全可以对一个文件进行加解密处理,只要保证使用defineClass时classbyte中
//已经解密后的内容就可以
        int readsize;
        readsize
=in.read(classbyte);
        System.out.println(
"读文件长:"+readsize);
        in.close();
        System.out.println(
"the classname:"+classname);
        
return defineClass("com."+classname,classbyte,0,readsize);
              }

}


 

 三、附言

What is ClassLoader? 

与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。 

JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,Bootstrap ClassLoader是用本地代码实现的,它负责加载核心Java Class(即所有java.*开头的类)。另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由Bootstrap ClassLoader加载;其中Extension ClassLoader负责加载扩展的Java class(例如所有javax.*开头的类和存放在JRE的ext目录下的类),Application ClassLoader负责加载应用程序自身的类。 

When to load the class? 

什么时候JVM会使用ClassLoader加载一个类呢?当你使用java去执行一个类,JVM使用Application ClassLoader加载这个类;然后如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。 

Why use your own ClassLoader? 

似乎JVM自身的ClassLoader已经足够了,为什么我们还需要创建自己的ClassLoader呢? 

因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,如果编写你自己的ClassLoader,你可以做到: 
1)在执行非置信代码之前,自动验证数字签名 
2)动态地创建符合用户特定需要的定制化构建类 
3)从特定的场所取得java class,例如数据库中 
4) 等等 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值