java 类加载原理_理解Java类加载原理

第六部分. 源代码

CompilingClassLoader.java

以下是文件CompilingClassLoader.java内容

import java.io.*;

/*

CompilingClassLoader动态的编译Java源文件。它检查.class文件是否存在,.class文件是

否比源文件陈旧。

*/

public class CompilingClassLoader extends ClassLoader

{

// 指定一个文件名,从磁盘读取整个文件内容,返回字节数组。

private byte[] getBytes( String filename ) throws IOException {

// 获得文件大小。

File file = new File( filename );

long len = file.length();

//创建一个数组刚好可以存放文件的内容。

byte raw[] = new byte[(int)len];

// 打开文件

FileInputStream fin = new FileInputStream( file );

// 读取所有内容,如果没法读取,表示发生了一个错误。

int r = fin.read( raw );

if (r != len)

throw new IOException( "Can't read all, "+r+" != "+len );

// 别忘了关闭文件。

fin.close();

// 返回这个数组。

return raw;

}

// 产生一个进程来编译指定的Java源文件,制定文件参数.如果编译成功返回true,否者,

// 返回false。

private boolean compile( String javaFile ) throws IOException {

// 显示当前进度

System.out.println( "CCL: Compiling "+javaFile+"..." );

// 启动编译器

Process p = Runtime.getRuntime().exec( "javac "+javaFile );

// 等待编译结束

try {

p.waitFor();

} catch( InterruptedException ie ) { System.out.println( ie ); }

// 检查返回码,看编译是否出错。

int ret = p.exitValue();

// 返回编译是否成功。

return ret==0;

}

// 类加载器的核心代码 -加载类在需要的时候自动编译源文件。

public Class loadClass( String name, boolean resolve )

throws ClassNotFoundException {

//我们的目的是获得一个类对象。

Class clas = null;

// 首先,检查是否已经出理过这个类。

clas = findLoadedClass( name );

//System.out.println( "findLoadedClass: "+clas );

// 通过类名获得路径名 比如:java.lang.Object => java/lang/Object

String fileStub = name.replace( '.', '/' );

// 构建指向源文件和类文件的对象。

String javaFilename = fileStub+".java";

String classFilename = fileStub+".class";

File javaFile = new File( javaFilename );

File classFile = new File( classFilename );

//System.out.println( "j "+javaFile.lastModified()+" c "

//+classFile.lastModified() );

// 首先,判断是否需要编译。如果源文件存在而类文件不存在,或者都存在,但是源文件

// 较新,说明需要编译。

if (javaFile.exists() &&(!classFile.exists() ||

javaFile.lastModified() > classFile.lastModified())) {

try {

// 编译,如果编译失败,我们必须声明失败原因(仅仅使用陈旧的类是不够的)。

if (!compile( javaFilename ) || !classFile.exists()) {

throw new ClassNotFoundException( "Compile failed: "+javaFilename );

}

} catch( IOException ie ) {

// 可能编译时出现IO错误。

throw new ClassNotFoundException( ie.toString() );

}

}

// 确保已经正确编译或者不需要编译,我们开始加载原始字节。

try {

// 读取字节。

byte raw[] = getBytes( classFilename );

// 转化为类对象

clas = defineClass( name, raw, 0, raw.length );

} catch( IOException ie ) {

// 这里并不表示失败,可能我们处理的类在本地类库中,如java.lang.Object。

}

//System.out.println( "defineClass: "+clas );

//可能在类库中,以默认的方式加载。

if (clas==null) {

clas = findSystemClass( name );

}

//System.out.println( "findSystemClass: "+clas );

// 如果参数resolve为true,根据需要解释类。

if (resolve && clas != null)

resolveClass( clas );

// 如果还没有获得类,说明出错了。

if (clas == null)

throw new ClassNotFoundException( name );

// 否则,返回这个类对象。

return clas;

}

}

CCRun.java

一下是CCRun.java文件

import java.lang.reflect.*;

/*

CCLRun通过CompilingClassLoader加载类来运行程序。

*/

public class CCLRun

{

static public void main( String args[] ) throws Exception {

// 第一个参数指定用户要运行的主函数类。

String progClass = args[0];

// 接下来的参数是传给这个主函数类的参数。

String progArgs[] = new String[args.length-1];

System.arraycopy( args, 1, progArgs, 0, progArgs.length );

// 创建CompilingClassLoader

CompilingClassLoader ccl = new CompilingClassLoader();

// 通过CCL加载主函数类。

Class clas = ccl.loadClass( progClass );

// 利用反射调用它的主函数和传递参数。

// 产生一个代表主函数的参数类型的类对象。

Class mainArgType[] = { (new String[0]).getClass() };

// 在类中找到标准的主函数。

Method main = clas.getMethod( "main", mainArgType );

// 创建参数列表 -在这里,是一个字符串数组。

Object argsArray[] = { progArgs };

// 调用主函数。

main.invoke( null, argsArray );

}

}

Foo.java

以下是文件Foo.java内容

public class Foo

{

static public void main( String args[] ) throws Exception {

System.out.println( "foo! "+args[0]+" "+args[1] );

new Bar( args[0], args[1] );

}

}

Bar.java

以下是文件Bar.java内容

import baz.*;

public class Bar

{

public Bar( String a, String b ) {

System.out.println( "bar! "+a+" "+b );

new Baz( a, b );

try {

Class booClass = Class.forName( "Boo" );

Object boo = booClass.newInstance();

} catch( Exception e ) {

e.printStackTrace();

}

}

}

baz/Baz.java

以下是文件baz/Baz.java内容

package baz;

public class Baz

{

public Baz( String a, String b ) {

System.out.println( "baz! "+a+" "+b );

}

}

Boo.java

以下是文件Boo.java内容

public class Boo

{

public Boo() {

System.out.println( "Boo!" );

}

}第七部分. 总结

总结

通过本文你是否意识到,创建自定义类加载器可以让你深入到Java虚拟机的内部。你可以从

任何资源加载类文件,或者动态的生成它,这样你就可以通过扩展这些功能做很多你感兴趣

的事,还能完成一些强大的功能。

关于ClassLoader的其它话题

就像本文开头说的,自定义类加载器在Java嵌入式浏览器和小应用程序浏览器中起着重要的

作用。下面给出类加载器的其它功能。

* 安全: 自定义的类加载器可以在把这个类交给Java虚拟机之前检查它是否有正确的数字

签名。你也可以自己创建一个"沙箱"来阻止对某些方法的调用,这是通过检查源代码,阻止

该类对沙箱之外的操作来实现的。

* 加密:通过自定义类加载器可以动态的解码,所有你的类文件就无法通过反编译被查看到

代码。用户需要密码才能运行程序,这个密码用来对代码解密。

* 存档:你是否需要将你的代码以某种格式或者压缩形式发布吗?自定义ClassLoader可以

从你想要的任何资源中生成字节码文件。

* 自提取程序:可以把整个应用程序编译到一个可执行的类文件中,这个文件包括压缩过或

者加密过的数据,有了内部类加载器,当程序运行的时候,他把自己解包到内存-不需要事

前安装。

* 动态生成:可以动态的生成那些被引用的类-整个程序需要用的类都可以动态的生成然后

交给Java虚拟机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值