上篇文章主要分析了类加载器的种类以及加载规则。下面,将主要分析下与加载器有关的
java程序动态扩展。
这篇文章只挑了类加载器的一个问题来分析,如果要想了解类加载器的一些常见问题以及自定义类加载要注意的一些细节,可以参考我的上篇文章:
http://xtu-tja-163-com.iteye.com/blog/770401
Java的连接模型允许用户运行时扩展应用程序,既可以通过当前虚拟机中预定义的加载器加载编译时已知的类或者接口,又允许用户自行定义类装载器,在运行时动态扩展用户的程序,这就是Java程序动态扩展(运行时加载类)。java动态扩展有两种方式:
1.采
用Class.forName方法。动态扩展Java程序最直接的方式就是使用Class的forName()方法,它有两中重载形式:
1)Class.forName(String className)
2)Class.forName(String className,boolean initialize)
它们之间的关系是:
Class.forName(string className) == Class.forName(string className,true)。
initialize参数用来指定加载className对应的类时,是否还进行初始化。有些场景下,需要将initialize设置为true来强制加载,同时完成初始化,例如典型的就是利用DriverManager进行JDBC驱动程序类注册的问题,因为每一个JDBC驱动程序类的静态初始化方法都用DriverManager注册驱动程序,这样才能被应用程序使用,这就要求驱动程序类必须被初始化,而不单单被加载.
2.
采用类加载器的loadClass方法来动态扩展java应用程序。它的加载过程已经在上面分析过了,采用的是委托双亲加载机制。这里注意,它虽然也有个
loadClass(String className,boolean resolve)方法,
resolve是加载指定名称的类以后,是否进行连接的标志,但是如果resolve为false,也不能保证被装载的类没被连接(例如指定的类在这之前已经被装载过了)。还有,用loadClass加载的类一般不进行初始化。
最后,建议使用
Class.forName()方法,因为,它在加载指定的类时,一定都会对加载的类进行初始化,而初始化在一些操作中是非常重要的,如果JDBC驱动的初始化。注意:
类的初始化不等于new一个对象,如果对类的初始化不熟悉,可以参考我以前的一篇文章:
http://xtu-tja-163-com.iteye.com/admin/blogs/769226
在类加载器中,比较容易弄错的一个问题就是:
由不同的类加载器加载的指定类型还是相同的类型吗?
答案: 在Java中,一个类用其完全匹配类名(fully qualified class name)作为标识,这里指的完全匹配类名包括包名和类名。
但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识,不同类加载器加载的类将被置于不同的命名空间.我们可以用两个自定义类加载器去加载某自定义类型(注意,不要将自定义类型的字节码放置到系统路径或者扩展路径中,否则会被系统类加载器或扩展类加载器抢先加载),然后用获取到的两个Class实例进行java.lang.Object.equals(…)判断,将会得到不相等的结果。
首先,在eclipse中部署好下面代码块:
package com.newTest.test3;
public class TestClass {
static {
System.out.println("我被加载了,加载我的加载器是:"+TestClass.class.getClassLoader());
}
public void print() {
System.out.println("我被加载了,加载我的加载器是:"+TestClass.class.getClassLoader());
}
}
/**
* 自定义加载器工厂
*/
public class ClassLoaderFactory {
private static ClassLoaderFactory instatnce = new ClassLoaderFactory();
static String s ;
public static ClassLoaderFactory getInstance() {
return instatnce;
}
//根据制定的路径生成不同的加载器
public ClassLoader getClassLoader(String path) {
File file = new File(path);
if(file.exists()) {
file.mkdirs();
}
URL url = null;
try {
String urlPath = file.getCanonicalPath()+File.separator;
/*
* 路径前一定要有'/',不然不对,我也不知道原因,知道的说下,谢谢
*/
urlPath = urlPath.charAt(1)=='/'?urlPath:'/'+urlPath;
String reposity = new URL("file",null,file.getCanonicalPath()+File.separator).toString();
URLStreamHandler handler = null;
url = new URL(null,reposity,handler);
} catch (Exception e) {
e.printStackTrace();
//记录日志 抛出异常
}
//生成类加载器,记住:如果你要调试,一定要把它的父加载器设置为null
return new URLClassLoader(new URL[]{url},null);
}
@SuppressWarnings({ "unchecked" })
public static void main(String[] args) throws Exception {
String path1 = "D:/classReposity";
String path2 = "D:/classReposityCopy";
//---------生成两个自定义类加载器--------------------
ClassLoader loader1 = ClassLoaderFactory.getInstance().getClassLoader(path1);
ClassLoader loader2 = ClassLoaderFactory.getInstance().getClassLoader(path2);
//-----------------采用同一个加载器加载同一个类型实例------------
Class c = loader1.loadClass("com.newTest.test3.TestClass");
Class c2 = loader1.loadClass("com.newTest.test3.TestClass");
//-----------------采用另外一个加载器加载同一个类型实例----------------
//注意,采用loadClass加载的类时,可能不会被初始化,也就是说,暂时还不会执行TestClass中静态块内容
Class c3 = loader2.loadClass("com.newTest.test3.TestClass");
System.out.println("使用同一个类加载器加载的类实例,是否相等?结果是:"+c.equals(c2));
System.out.println("使用不同的类加载器加载的类实例,是否相等?结果是:"+c.equals(c3));
//执行加载出的c的一个实例的print方法
c.getMethod("print", new Class[]{}).invoke(c.newInstance(), new Object[]{});
}
}
经过编译后,把生成的Test.class文件分别放在path1和path2目录下,注意:如
果有包,要放在path1或path2的包目录下,如:path1/com/netTest/test3/下。
最后,执行上面程序,得出如下结果:
使用同一个类加载器加载的类实例,是否相等?结果是:true
使用不同的类加载器加载的类实例,是否相等?结果是:false
我被加载了,加载我的加载器是:java.net.URLClassLoader@c17164
我被加载了,加载我的加载器是:java.net.URLClassLoader@c17164
分享到:
相关推荐
【JVM】类的奇幻漂流——类加载机制探秘 Java虚拟机(JVM)是运行Java程序的核心组件,它负责将我们编写的类加载到内存中并执行。类加载机制是JVM的一个重要组成部分,它确保了程序的正常运行。本文将带你深入理解...
在Java编程中,类加载器(ClassLoader)扮演着至关重要的角色,它负责将类的.class文件加载到Java虚拟机(JVM)中,使程序得以运行。从JDK 2.0开始,Java引入了一种特殊的类加载策略——父亲委托机制,这一机制保证...
传统的Java应用程序在启动时,由JVM(Java虚拟机)通过类加载器将类加载到内存中,一旦加载完成,除非程序退出,否则这些类通常不会被重新加载。然而,在开发过程中,我们可能希望在不重启应用的情况下,对已加载的...
【深入Java虚拟机(二)——类的生命周期(上)类的加载和连接】 Java虚拟机(JVM)是Java程序的核心,它负责解释和执行Java字节码。类的生命周期在JVM中是一个关键的概念,它涵盖了从类的加载到卸载的整个过程。...
本教程——“深入JVM内核—原理、诊断与优化视频教程”,将重点讲解这些关键点,帮助开发者提升技术水平,更好地解决实际问题。 首先,我们来探讨JVM的内核原理。JVM主要由类加载子系统、运行时数据区、执行引擎、...
1. **Bootstrap ClassLoader**:这是JVM启动时的第一个类加载器,负责加载JDK核心库(rt.jar)中的类。 2. **Extension ClassLoader**:加载JRE扩展目录(java.ext.dirs)下的JAR文件。 3. **System ClassLoader**...
- **加载**:这是类加载的第一步,JVM会通过类加载器找到.class文件,并将其二进制数据读入内存。这些数据会被存储在方法区,这是一个用于存储已加载类元数据的区域。同时,在堆区创建一个`java.lang.Class`对象,...
书中可能讲解了双亲委派模型、自定义类加载器以及类加载器之间的关系。 8. **性能优化实践**:除了理论知识,书中可能还提供了大量实战案例,如通过代码优化减少对象创建、利用String池提高效率、避免过多反射操作...
《Java实现的JVM——深入理解与实践》 在计算机科学领域,Java虚拟机(JVM)是Java语言的关键组成部分,它使得Java程序能够在不同平台上运行而无需重新编译。本项目“jvmjava”是一个开源项目,由Java语言实现,...
总之,在Java开发过程中,深入了解JVM的类加载机制以及相关的类加载器类型对于提高代码质量、优化程序性能具有重要意义。同时,掌握深拷贝与浅拷贝的概念有助于更好地管理对象间的引用关系,避免不必要的错误和内存...
双亲委派模型是JVM中类加载器之间的关系,每个类加载器都有一个父加载器,除了启动类加载器外,其他类加载器都有自己的父类加载器。当某个特定的类加载器接收到加载请求时,首先将加载任务委托给父类加载器,只有...
在深入理解Java虚拟机(JVM)如何加载Class文件之前,我们需要明确一点:Java的所有类都需要通过类加载器加载到JVM中才能被执行。这个过程对开发者来说通常是透明的,但在一些特殊情况下,例如使用反射时,了解类加载...
《深入JVM内核——原理、诊断与优化》是一份深度探讨Java虚拟机核心机制、问题诊断和性能优化的专业资料。这份资料涵盖了JVM的各个方面,对于Java开发者来说,理解和掌握这些知识至关重要。 首先,我们要了解JVM...
在类加载机制方面,作者解释了JVM是如何在运行时动态加载和链接Java类的,包括双亲委派模型、类加载器的种类以及自定义类加载器的设计与实现。理解这些机制对于构建灵活的应用程序架构至关重要,尤其是在处理热部署...
本节介绍了一种特殊的类加载器——编译时类加载器。这类加载器能够在加载类之前自动编译源代码。这对于动态语言或需要即时编译的场景非常有用,比如当开发者希望在运行时修改某些行为而不需要重新编译整个项目时。 ...
而JVM代理(JVM Agent)和类加载器(ClassLoader)则是两个关键的概念,它们对于深入理解Java应用程序的运行机制至关重要。 **JVM代理** JVM代理是一种在JVM启动时或者运行时插入额外功能的方式,它允许开发者在不...
此外,还有一个特殊类型的类加载器——线程上下文类加载器,稍后会单独讨论。 类加载器之间遵循双亲委派机制。这意味着当一个类加载器收到加载类的请求时,它会先委托给其父类加载器尝试加载。只有当父类加载器无法...
双亲委派模型是指类加载器在加载类之前,先委托给父类加载器尝试加载,如果父类加载器加载失败,则由当前类加载器尝试加载。这一机制的优点是可以避免重复加载相同的类,同时保证了核心API类的安全性。 #### 三、...
总的来说,理解和掌握Class文件结构、JVM的工作原理以及类加载器的功能对于Java开发者,尤其是系统架构师和性能调优专家而言,是必不可少的知识。通过深入学习这些内容,我们可以更好地理解Java程序的运行机制,从而...
### JVM之走进类加载 #### 一、Java及JVM简介 Java自1995年由Sun Microsystems发布以来,便迅速成为了最受欢迎的编程语言之一。根据TIOBE语言热度排行榜,Java持续保持在前列,这证明了其在编程领域的持久影响力。...