`

ClassLoader-学习

阅读更多

相关文章:
https://www.ibm.com/developerworks/cn/java/j-lo-classloader/#download
http://blog.csdn.net/zhoudaxia/article/details/35897057
ClassLoader:类装载器是用来把类(class)装载进JVM的。

在java语言中,是个非常重要的概念。平时我们接触不太多。但是在以下领域我们需要了解ClassLoader的特性:
1、热部署。
2、在类加载阶段,修改字节码,增加特殊功能。(一般用的比较少)
3、jvm通过特定的类加载器实现安全性的要求

JAVA程序对类的使用方式可分为两种:
主动使用
被动使用

所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们
主动使用的情况:
1、创建类的实例
2、访问某个类或者接口的静态变量,或者对其进行赋值
3、调用类的静态方法
4、反射
5、初始化一个类的子类(接口不适用)
6、JAVA虚拟机启动时被标明类启动类的类


先了解下关于ClassLoader的基本知识:
1、已经加载的类,不能重复加载,否则会抛出重复加载的异常。为了保证类型安全,必须保证在任何时刻,只要使用相同class loader对象装载同名的类,那么得到的class实例都是相同的。

    为了做到这一点,就不能采用系统默认的类加载器委托规则,也就是说我们定制的类加载器在加载时,可以打破规则,自定义加载的顺序,当然这个是要以为功能的需要为目的。
     类的加载又分为显式加载和隐式加载。大家使用 new 关键字创建类实例时,其实就隐式地包含了类的加载过程。对于类的显式加载来说,比较常用的是 Class.forName,其实,它们都是通过调用 ClassLoader 类的 loadClass 方法来完成类的实际加载工作的。直接调用 ClassLoader 的 loadClass 方法是另外一种不常用的显式加载类的技术。
2、java是动态加载类的,并且是逐级别的,这样的话,可以节省内存,用到什么加载什么,就是这个道理,然而系统在运行的时候并不知道我们这个应用与需要加载些什么类,那么,就采用这种逐级加载的方式。
  (1)首先加载核心API,让系统最基本的运行起来
  (2)加载扩展类
  (3)加载用户自定义的类



3、一个Class 要想卸载,有一下要求:
1、该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
2、加载该类的ClassLoader已经被GC。
3、该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法

 

代码验证:

private static void testBasic(){
		/**
		 * ouput: null 启动类加载器(Bootstrap ClassLoader)
		 *  这个类加载器负责将<JAVA_HOME>\lib目录下的类库加载到虚拟机内存中
		 * ,用来加载java的核心库,此类加载器并不继承于java.lang
		 * .ClassLoader,不能被java程序直接调用,代码是使用C++编写的.是虚拟机自身的一部分.
		 */
		System.out.println(List.class.getClassLoader());

		/**
		 * ouput: sun.misc.Launcher$AppClassLoader@38da9246
		 * 这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,
		 * 这个类加载器是CLassLoader中的getSystemClassLoader
		 * ()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.
		 */
		System.out.println(ClassLoaderTest.class.getClassLoader());

		/**
		 * output:sun.misc.Launcher$ExtClassLoader@15b94ed3 扩展类加载器(ExtendsionClassLoader):     
		 * 这个类加载器负责加载<JAVA_HOME>\lib\ext目录下的类库,用来加载java的扩展库,
		 * 开发者可以直接使用这个类加载器.
		 */
		System.out.println(DNSNameService.class.getClassLoader());
	}

 

内部class loader:
1、bootstrap class loader --引导类加载器,它负责加载Java的核心类【java.* 】,不是 java.lang.ClassLoader的子类,这个类加载器负责将<JAVA_HOME>\lib目录下的类库加载到虚拟机内存中。

 

2、extension classloader -扩展类加载器(sun.misc.Launcher$ExtClassLoader@15b94ed3),它负责加载JRE的扩展目录【javax.* 】(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包。

 

3、system classloader -系统(也称为应用)类加载器(sun.misc.Launcher$AppClassLoader@38da9246

null),它负责加载系统或用户实现的类,在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径,如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。ClassLoader.getSystemClassLoader()获取。


4、User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。


ClassLoader加载过程:

classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器 (而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。



ClassLoader 类加载逻辑分析, 以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:

    
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 检查类是否已被装载过    
Class c = findLoadedClass(name);        
if (c == null ) {        
         // 指定类未被装载过        
         try {        
                 if (parent != null ) {        
                         // 如果父类加载器不为空, 则委派给父类加载        
                         c = parent.loadClass(name, false );        
                 } else {        
                         // 如果父类加载器为空, 则委派给启动类加载加载        
                         c = findBootstrapClass0(name);        
                 }        
         } catch (ClassNotFoundException e) {        
                 // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其        
                 // 捕获, 并通过findClass方法, 由自身加载        
                 c = findClass(name);        
         }        
 }
}



扩展ClassLoader方法:
1、复写方法   public    Class findClass(String name)
2、或者复写public     byte [] loadClassData(String name)



ClassLoader的方法说明:
1、findLoadedClass:每个类加载器都维护有自己的一份已加载类名字空间,其中不能出现两个同名的类。凡是通过该类加载器加载的类,无论是直接的还是间接的,都保存在自己的名字空间中,该方法就是在该名字空间中寻找指定的类是否已存在,如果存在就返回给类的引用,否则就返回 null。这里的直接是指,存在于该类加载器的加载路径上并由该加载器完成加载,间接是指,由该类加载器把类的加载工作委托给其他类加载器完成类的实际加载。
2、getSystemClassLoader:Java2 中新增的方法。该方法返回系统使用的 ClassLoader。可以在自己定制的类加载器中通过该方法把一部分工作转交给系统类加载器去处理。
3、defineClass:该方法是 ClassLoader 中非常重要的一个方法,它接收以字节数组表示的类字节码,并把它转换成 Class 实例,该方法转换一个类的同时,会先要求装载该类的父类以及实现的接口类。
4、loadClass:加载类的入口方法,调用该方法完成类的显式加载。通过对该方法的重新实现,我们可以完全控制和管理类的加载过程。
5、resolveClass:链接一个指定的类。这是一个在某些情况下确保类可用的必要方法,详见 Java 语言规范中“执行”一章对该方法的描述。

自定义类加载器初探:

package com.gym.backadmin.controller;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

/**
 * 自定义类加载器
 * @author xinchun.wang 
   @email: 532002108@qq.com
 * @createTime 2015-4-5 下午7:54:11
 */
public class MyClassLoader extends ClassLoader {
	/**
	 * just a test 
	 */
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] classData = loadClassData(name);
		return defineClass(classData, 0, classData.length);
	}
	
	private byte[] loadClassData(String urlString) {
		try {			
			//return byteOutput.toByteArray();
			FileInputStream in =new FileInputStream(urlString);			
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			FileChannel channel =in.getChannel();            
            WritableByteChannel outchannel = Channels.newChannel(out); 
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
            while (true) { 
                int i = channel.read(buffer); 
                if (i == 0 || i == -1) { 
                    break; 
                } 
                buffer.flip(); 
                outchannel.write(buffer); 
                buffer.clear(); 
            }             
			byte[] bytes =out.toByteArray();
			out.close();
			in.close();
			return bytes;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	
	public static void main(String[] args) throws Exception {
		MyClassLoader loader = new MyClassLoader();
		Class<?> userClass = loader.loadClass("D:\\gym\\haha\\com\\gym\\backadmin\\service\\user\\BasicUserService.class");
		Object userService = userClass.newInstance();
		Method m = userClass.getMethod("getUser", new Class[]{}); 
	    Object result = m.invoke(userService, new Object[]{}); 
	    System.out.println(result);
	}
}


运行结果:
{username=ooooooooooooo}
请注意:我把BasicUserService.class 文件放到了一个随意的目录。然后去加载二进制文件的。

/**
 * 
 * @author xinchun.wang 
   @email: 532002108@qq.com
 */
public class BasicUserService implements UserService {
	public Map<String, Object> getUser() {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("username", "ooooooooooooo");
		return model;
	}
}



另外:Object userService = userClass.newInstance();
如果改为:BasicUserService userService = (BasicUserService)userClass.newInstance();
会出现:


原因如下:
因为在 Java 中,即使是同一个类文件,如果是由不同的类加载器实例加载的,那么它们的类型是不相同的。在上面的例子中 userClass 是由 MyClassLoader 加载的,而 userService 变量类型声名和转型里的 BasicUserService 类却是由 run 方法所属的类的加载器(默认为 AppClassLoader)加载的,因此是完全不同的类型,所以会抛出转型异常。

 

特别注意:对于接口:
1、初始化一个类并不会先初始化它所实现的接口。
2、初始化一个接口时,并不会先初始化它的父接口。

/**
 * @author xinchun.wang 
   @email: 532002108@qq.com
 * @createTime 2015-4-7 上午10:58:57
 */
public class ImplementsTest {

	public static void main(String[] args) {
		A a2 = new A2();
		System.out.println(a2); // 注意A没有被初始化
		System.out.println(a2.a); //才会对A接口进行初始化,可以注释掉本行 做测试
	}

	static interface A {
		public static final A a = new A1();
	}

	static class A1 implements A {
		static {
			System.out.println("hello world in A");
		}
	}

	static class A2 implements A {

	}
}

  • 大小: 19.6 KB
  • 大小: 5.8 KB
  • 大小: 75.8 KB
  • 大小: 175.2 KB
1
2
分享到:
评论

相关推荐

    xwiki-commons-classloader-protocol-jar-5.4.zip

    《XWiki Commons Classloader Protocol Jar 5.4与LaZyWorker开源项目解析》 XWiki Commons Classloader Protocol Jar 5.4是一个重要的组件,它属于XWiki开源项目的一部分。XWiki是一个强大的、可扩展的、面向企业的...

    j-classloader-ltr

    ### Java ClassLoader 知识点解析 #### 一、引言 在《classloader教程 --- from IBM》这篇教程中,作者Greg Travis通过一系列详细的讲解帮助读者理解Java中的类加载器(ClassLoader)。作为Java运行时系统的一个核心...

    classloader-playground, 一个简单的java依赖隔离容器类.zip

    开源项目"classloader-playground"为我们提供了一个学习和实验Java类加载器的绝佳平台。 首先,我们来理解一下什么是类加载器。在Java中,类加载的过程包括加载、验证、准备、解析和初始化五个阶段。类加载器主要...

    restx-classloader-0.33.1.zip

    【标题】"restx-classloader-0.33.1.zip" 提供的是 Restx ClassLoader 的一个版本,这是 Restx 框架的一部分,主要...通过学习和使用这些开源项目,开发者可以提高他们的技能,并参与到更复杂、更高效的系统构建中去。

    【IT十八掌徐培成】Java基础第25天-04.classLoader-系统资源-不可见类访问.zip

    在Java编程语言中,ClassLoader是...总的来说,理解ClassLoader的工作机制和类的可见性对于深入学习Java和开发复杂的Java应用至关重要。这个课程将帮助开发者更好地掌握这些核心概念,并能够灵活运用到实际项目中去。

    ClassLoader 详解.doc

    《ClassLoader详解》 Java应用程序的运行离不开类的加载,而ClassLoader正是这个过程的...通过本文档的学习,读者可以深入理解Java虚拟机加载类的过程,掌握如何自定义和优化类加载机制,从而提升应用的稳定性和效率。

    classloader-example:博客帖子

    总的来说,这个“classloader-example”不仅是一个学习Java类加载器工作原理的好资源,也是一个实践自定义加载策略和运行时动态加载的实战案例。通过深入研究这个示例,开发者可以更好地掌握Java的高级特性,提高...

    ClassLoaderDemo.7z

    总之,`ClassLoaderDemo.7z`提供了一个实践性的学习平台,让我们有机会亲手操作并观察类加载过程,这对于理解Java的类加载机制及其在实际应用中的作用至关重要。无论是Java初学者还是经验丰富的开发者,这个示例都是...

    Java ClassLoader学习总结

    Java ClassLoader学习总结 Java 类加载机制是 Java 中一个非常重要的机制,它负责加载 Class 文件到 JVM,以供程序使用。ClassLoader 是 Java 中的一个抽象类,它的主要作用是加载 Class 文件到 JVM 中。...

    classloader

    学习ClassLoader的工作原理以及如何创建自己的ClassLoader是必要的。 首先,让我们深入了解ClassLoader的基本概念。在传统编程语言中,如C或C++,程序通常是一个单一的可执行文件,而Java则不同,它的编译结果是以...

    Java ClassLoader定制实例

    理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将深入探讨Java ClassLoader的内部工作,并通过一个具体的实例来展示如何定制自己的ClassLoader。 ...

    JVM ClassLoader简析

    在Java世界中,JVM(Java Virtual ...深入学习ClassLoader不仅有助于提升编程技巧,也有助于解决实际开发中遇到的类加载问题。阅读"ClassLoader简析.pdf"文档和分析"Sample.zip"中的代码,将有助于形成更全面的了解。

    ClassLoader

    ### ClassLoader概述 在Java中,`ClassLoader`是负责加载类到JVM的重要组件之一。它不仅能够确保每个被加载的类都具有唯一的标识符,还能处理...理解类加载器的工作原理对于深入学习Java虚拟机和其底层机制非常重要。

    java classloader

    Java ClassLoader是一个核心的Java运行时组件,负责加载类到Java虚拟机(JVM)中。它是Java平台的独特特性,因为它允许动态加载类,增强了软件的可扩展性和灵活性。这篇博文(虽然链接不可用)可能深入探讨了...

    ClassLoader 案例

    总的来说,这个案例提供了学习和实践Java ClassLoader、反射和注解的绝佳机会。通过这个案例,你可以了解到如何构建一个能够动态加载和更新类的系统,这对于理解和掌握Java的高级特性是非常有帮助的。在实际项目中,...

    ClassLoader原理

    ClassLoader原理的理解对于深入学习Java和进行系统优化至关重要。这篇博文将带你深入了解ClassLoader的工作机制。 首先,我们来理解ClassLoader的基本概念。ClassLoader是一个抽象的概念,它是Java中的一个接口,...

    Understanding the Java ClassLoader

    通过本教程的学习,你将能够理解Java ClassLoader的基本概念及其工作原理,并学会如何创建自定义的ClassLoader。自定义ClassLoader不仅能够扩展JVM的功能,还能够在实际项目中解决特定问题,如动态加载远程资源、...

    探究java的ClassLoader及类变量初始化顺序

    在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中。...通过学习这些知识点,开发者可以更好地优化代码、设计更健壮的系统,并解决与类加载和初始化相关的复杂问题。

    了解Java ClassLoader

    理解ClassLoader的工作原理对于深入学习Java以及进行JVM优化、插件开发等高级技术至关重要。下面将详细介绍Java ClassLoader的基本概念、工作流程、类加载机制以及自定义ClassLoader。 1. **ClassLoader基本概念** ...

    my_classloader_demo-master.7z Native层实现热修复

    在Android开发中,热修复是一种重要的技术,它允许开发者在应用无需用户手动更新的情况下修复程序中的错误或...通过学习和研究这个项目,开发者可以提升对Android系统和热修复技术的掌握,提高应用的质量和用户体验。

Global site tag (gtag.js) - Google Analytics