当我们在命令行输入java Xxx(某个类)时候,java内部会做些什么动作呢?
类装载器:java虚拟机使用每一个类的第一件事情就是将该类的字节码装载进来,装载类字节码的功能是有类装载器完成的,类装载器负责根据一个类的名称来定位和生成类的字节码数据后返回给java虚拟机。最常见的类加载器是将要加载的类名转换成一个.class文件名,然后从文件系统中找到该文件并读取其中的内容,这种类装载器也不是直接将.class文件中的内容原封不动地返回给java虚拟机,它需要将.class文件中的内容转换成Java虚拟机使用的类字节码,譬如,Java程序中的字符串编译成.class文件后是以UTF-8编码存在的,而装载进java
虚拟机后要被转换成Unicode编码。类装载器本身也是一个Java类,允许开发人员自己编写自己的类装载器,以便通过其他各种特殊方式来产生类字节码。我们可以对一些.class文件进行加密处理来防止反编译,但需要使用特殊的类装载器从已被加密处理的.class文件中还原出正常的字节码即可。不管类装载器采用什么方式,只要能够在内存制造出Java虚拟机调用类字节码即可。所以,把类装载器描述为类字节码制造器更容易让人理解。
当一个类被加载后,Java虚拟机将其编译为可执行代码存储在内存中,并将索引信息存储进一个HashTable中,其索引关键字在HashTable中查找相应的信息,如果该代码已经存在,虚拟机直接从内存里调用该可执行代码,反之则调用类装载器并进行加载和编译。
一个Java类用来描述现实中的事物,Java程序也是一种事物,它可以用一个Java类描述,这个特殊的类名就叫Class。Class类用于描述Java程序语言中使用的一个类的有关信息。可以认为,类装载器装载某个类的字节码的过程实际上就是在创建Class类的一个实例对象,这个Class类的实例对象封装的内容正好是当前加载的类的字节码数据,也就是Java虚拟机对当前加载类编译后存储在内存中的可执行代码!可以采用下面三种方式获得:
1。类名.class ,例如。System.class
2。对象.getClass(),例如。new Date().getClass()
3。Class.forName("类名"),例如,Class.forName("java.util.Date");
类装载器本身也是一个java类,java类库中提供了一个java.lang.ClassLoader来作为类装载器的基类,java虚拟机和程序都调用ClassLoader的子类。Class类中定义了一个getClassLoader方法,用于返回它所描述的类的类装载器对象,这个返回对象的类型就是ClassLoader.
双亲委派模型:
下面是类装载器使用示例:
package cn.itcast;
import java.io.*;
public class MyClassLoader extends ClassLoader
{
private String path = null;
public MyClassLoader(String path)
{
//错误检查省略
this.path = path;
}
protected class findClass(String name)
{
try
{
File f = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e)
{
e.printStrackTrace() ;
}
return null;
}
public static void cypher(InputStream ips , OutputStream ops)
{
try
{
int b = 0;
while((b=ips.read()) != -1)
{
ops.write(((byte)b) ^ 0xff);
}
}catch(Exception e){}
}
public static void main(String [] args) throws Exception
{
if(!args[0].endsWith("class"))
{
System.out.println(
MyClassLoader.class.getClassLoader().getClass().getName());
ClassLoader loader = new MyClassLoader(args[1]);
ClassLoader ld = loader.getParent();
while(ld != null)
{
System.out.println(" " + ld.getClass().getName());
ld = ld.getParent();
}
Class cls = loader.loadClass(args[0]);
System.out.println(cls.getClassLoader().getClass().getName());
java.util.Date d = (java.util.Date)cls.newInstance();
System.out.println(d.toString());
return;
}
FileInputStream fis = new FileInputStream(args[0]);
File f = new File(args[1], new File(args[0]).getName());//不用检查目录最后是否有目录分割符
FileOutputStream fos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}