`
草帽当家的
  • 浏览: 24376 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
文章分类
社区版块
存档分类
最新评论

从JVM到RTTI

 
阅读更多
        这一切还要从CLASS文件说起。当我们在虚拟机上编写java文件时,虚拟机会动态地将java文件转换为以.class为后缀名的CLASS文件。那么这个ClASS文件又有什么作用呢?为什么我们删除java文件保留ClASS文件,在命令行中执行java filename程序仍能正常运行呢?

一、CLASS文件
        1、概述   
        Java编译器将Java源文件指令翻译成虚拟机能识别的字节码,这些机器码被储存在以.class为扩展名的CLASS文件中。CLASS文件是可以运行在任何支持Java虚拟机的硬件平台和操作系统上的二进制文件。每个CLASS文件都包含一个类或接口的定义和实现。CLASS文件是一个虚拟机的指令集,所有的这些指令都会被程序翻译成目标机器的机器语言。
        2、作用
        平台无关性:为Java程序提供独立于底层主机平台的二进制形式的服务。
        网络移动性: CLASS文件设计得紧凑,因此它们可以快速地在网络上传送。其次,由于Java程序是动态连接和动态扩展的,CLASS文件可以在需要的时候才下载。这个特点使得Java应用程序能够安排从网络上下载CLASS文件的时间,从而可以最大限度地减少终端用户的等待时间。
        3、链接
        所有的类都是在对其第一次使用,通过类加载器动态地加载到虚拟机中的。然而,类加载器加载的不是Java源文件,而是编译后的CLASS文件,从而可以看出——Java文件只是用户和虚拟机的交互,真正对虚拟机有意义的是CLASS文件,它参与了虚拟机与底层的交互

二、类加载器
        1、概述
        类加载机制不只是使用一个单一的类加载器,每个Java程序至少有三个类加载器,它们都是系统类加载器。用户也可以通过实现java.lang.ClassLoader实现自定义类加载器。
        2、JVM自带类加载器
        根类加载器(Bootstrap):使用c++编写,该加载器没有父加载器。它负责加载虚拟机的核心类库(典型地,来自rt.jar包下的类库)。根类加载器依赖于底层操作系统,属于虚拟机实现的一部分,它并没有实现java.lang.ClassLoader类。
        扩展类加载器(Extension):纯java代码编写,ClassLoader的子类,它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre\lib\ext子目录下加载类库。你可以将你要引进的jar文件放在该目录下,加载器不需要任何class path就可以找到它们的类库。
        系统类加载器或应用加载器(System):纯java代码编写,ClassLoader的子类,它的父加载器为扩展类加载器,它从环境变量classpath或系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器
        3、类的加载过程
        类的加载采用父类委托机制,这种机制能够更好的保障Java平台的安全性。在此委托机制中,除Java自带的根加载器外,其余的加载器都有且只有一个父加载器,如下层次图所示,当Java程序请求(your custom)loader加载(your custom)class时,loader首先委托自己的父加载器去加载class,若父加载器能完成,则由父加载器加载,否则由loader加载。(这种关系并非是继承关系,而是一种包装机制)。
       
        4、命名空间
        在Java中一个类通过认证的类全名来唯一的标识。认证的类全名包括包名和类名两部分组成。但是一个被加载到JVM中的类则通过类的全名和加载这个类的类记载器来唯一的标识。每个类加载器都有自己的命名空间,命名空间由该加载器和所有的父加载器所加载的类组成。在同一个命名空间不会出现类的完整名字(包括包名)相同的两个类;在不同的命名空间有可能出现完整的名字包括包名相同的两个类。
        5、运行时包
        由同一个类加载器加载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同,只有属于同一个运行时包的类才能互相访问包可见的类和类成员,这样的限制能避免用户自定义的类冒充核心类库的类,去访问包可见成员。
        6、自定义类加载器
        如上所述,系统类加载器是自定义类加载器的父加载器。若想实现自定义类加载器,需实现java.lang.ClassLoader类。如下示例。
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader{
	private String name;//类加载器的名字
	private String path = "F:\\";//加载类的路径
	private static final String fileType = ".class";//文件的扩展名
	public MyClassLoader(String name){
		super();//显式地调用父类构造器,将该加载器的父加载器设置为默认的系统加载器
		this.name = name;
	}
	public MyClassLoader(ClassLoader parent,String name){
		super(parent);//设置该加载器的父加载器
		this.name = name;
	}
	//将java文件中的数据存储到字节数组中
	private byte[] loadClassData(String name){
		byte[] data = null;//存储字节码
		InputStream ips = null;
		ByteArrayOutputStream baos = null;
		try{
			String cname = name.replace(".", "\\");//获取正确的路径格式
			ips = new FileInputStream(new File(path+cname+fileType));
			baos = new ByteArrayOutputStream();
			int ch = 0;
			while(-1 != (ch = ips.read()) ){
				baos.write(ch);//从文件输入流中读取数据,将读到的数据写到字节数组输出流中
			}
			data = baos.toByteArray();
			ips.close();
			baos.close();//关闭输入输出流
		}catch(Exception ep){
			ep.printStackTrace();
		}
		return data;
	}
	//获取Class对象
	public Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] data = this.loadClassData(name);
		return this.defineClass(name, data, 0, data.length);
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
	public String toString(){
		return this.name;
	}
	//测试方法
	public static void test(ClassLoader loader) throws Exception{
		Class<?> c = loader.loadClass("First");
		c.newInstance();
	}
	public static void main(String[] args) throws Exception {
		MyClassLoader loader1 = new MyClassLoader("loader1");
		loader1.setPath("F:\\myapp\\loader1\\");
		MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
		loader2.setPath("F:\\myapp\\loader2\\");
		test(loader1);
		test(loader2);
	}
}

public class First {
	public First(){
		System.out.println("First的类加载器是:"+this.getClass().getClassLoader());
		new Second();
	}
}

public class Second {
	public Second(){
		System.out.println("Second的类加载器是:"+this.getClass().getClassLoader());
	}
}

        只需要将要加载的类放到类加载器的路径下,即可在命令行完成测试。当然,只需将程序略改一二,就能完成对父委托机制的测试,此处不再讨论。
        7、链接
        随着Class对象的产生,我们已经越来越接近RTTI(运行时类型信息)了。之所以这么说,是因为Class对象构成了RTTI的核心,它能使程序在运行时确定类的确切类型,而这正是强大的多态机制避及的问题。一旦我们获得类的确切类型,就能进行一些特定的操作,而不是基于基类通用的操作,丰富了程序的行为。不仅如此,Class对象还让我们在运行时打开和检查CLASS文件成为可能,从而实现了反射机制。还有诸如注册工厂、动态代理等设计模式的实现。这一切都在酝酿之中~~...
  • 大小: 18.8 KB
3
11
分享到:
评论
5 楼 夜CT 2012-08-12  
4 楼 草帽当家的 2011-09-12  
wenshen22 写道
很好很强大!!


言重了,还差的远呢!
3 楼 草帽当家的 2011-09-12  
cry615 写道
,很不错!

谢谢!
2 楼 cry615 2011-09-12  
,很不错!
1 楼 wenshen22 2011-09-11  
很好很强大!!

相关推荐

Global site tag (gtag.js) - Google Analytics