Java中的类装载器

简介:

Java的类装载器是JRE的一部分,它动态的将类装载到JVM中。通常当类有需求的时候才会装载,属于延迟加载。由于存在类装载器,Java Runtime不需要知道文件和文件系统。

Java的类型系统包含4种类型,原始类型(primitive),数组类型(array),null类型,和class类型,class即描述对象类型的类型。

其中最重要的类型是Class。类装载器负责定位类库,读取类信息,在类库中装载类。这个装载行为是按需装载,也就是说只有当这个类要被程序使用的时候才会装载。同一个装载器只装载同一个类一次。每一个Java类必须由类装载器装载,此外,Java程序可能用到外部类库。当JVM启动的时候,会用到三个类装载器。 Bootstrap 类装载器,Extensions 类装载器,System 类装载器。

bootstrap 类加载器加载<JAVA_HOME>/jre/lib 目录中的Java核心代码,这个类加载器是JVM核心中的一部分,是原生在JVM中的。

Extensions 类加载器加载扩展目录(JAVA_HOME>/jre/lib/ext)中指定的代码,由sun.misc.Launcher$ExtClassLoader实现。

System 类装载器加载java.class.path中的代码,即映射在system CLASSPATH 变量中的,由sun.misc.Launcher$AppClassLoaderclass实现。

Java中Class是一个申明了Final了的类,因此无法被继承。它的声明如下:

public final class Class extends Object

该类的实例表示了在Java应用程序中运行着的类型,类似C#中的Type。该类有一个静态方法。

static Class forName(String className)

返回的是名字为className的类或者接口。类或者接口的名字必须是完整的,包含包的名字。如果名字给错的话,会出现异常。

Class.forname和ClassLoader.loadClass()这2个方法都是根据类名动态的装载java类,但是他们行为的不同点在于使用哪个类装载器(java.lang.ClassLoader)和是否初始化类对象。

Class.forName()的最常见的形式,就是输入一个类名的字符串作为参数,总是使用调用者的classloader。这个classloader就是装载执行forName代码的类装载器。相对的ClassLoader.loadClass()是一个实例方法,要求你指定一个classloader,这个classloader可能不是装载当前调用代码的那个装载器。如果你的代码中需要使用一个指定的装载器来装载类,那么应该使用ClassLoader.loadClass(),或者是forName带有三个参数的那个重载版本。

此外,Class.forName()的常见形式初始化了要装载的类。直接表现在执行了类的静态初始化器,这个和ClassLoader.loadClass()的行为不同。ClassLoader.loadClass()只用在这个类首次使用的时候才会运行初始化器。所谓初始化器,即类中static那部分代码。

如果要装载一个已知的类,这个类有一个非常耗资源的静态初始化器,你可以选择先加载它,以保证这个类在classpath中存在,同时又希望它的初始化器只有在调用这个类的字段或者方法的时候才运行。那么方法Class.forName(String, boolean, ClassLoader)是最好的选择。

你可以设置第二个参数为false来延迟初始化器,使用第三个参数指定选用哪一个类加载器。推荐总是使用这个方法,因为这个方法最为灵活。

成功装载一个类不代表就没有问题了,如果静态初始化器中抛出一个异常,该异常会被包裹在java.lang.ExceptionInInitializerError中,那么这个类变为不能使用的。这样,如果要在代码的中处理这样异常错误,应该使用Class.forName() 方法来执行初始化器。

 
  1. public class Main  
  2. {  
  3.     public static void main (String [] args) throws Exception  
  4.     {  
  5.         for (int repeat = 0; repeat < 3; ++ repeat)  
  6.         {  
  7.             try 
  8.             {  
  9.                 // "Real" name for X is outer class name+$+nested class name:  
  10.                 Class.forName ("Main$X");  
  11.             }  
  12.             catch (Throwable t)  
  13.             {  
  14.                 System.out.println ("load attempt #" + repeat + ":");  
  15.                 t.printStackTrace (System.out);  
  16.             }  
  17.         }  
  18.     }  
  19.     private static class X  
  20.     {  
  21.         static 
  22.         {  
  23.             if (++ s_count == 1)  
  24.                 throw new RuntimeException ("failing static initializer...");  
  25.         }  
  26.     } // End of nested class  
  27.     private static int s_count;  
  28. // End of class  

这段代码企图装载一个类X三次,尽管X的静态初始化器只在第一次运行的时候失败,但每次调用Class.forName的时候,都会提示失败。(这样充分表明了,类的静态初始化构造器只运行一 本人住)

后续的装载失败的异常是java.lang.NoClassDefFoundError。这是因为JVM已经标记X已经被转载(在初始化器运行之前)。这个类也不会被卸载,除非当前的类装载器被垃圾回收。所以,后续的调用Class.forName()。JVM不会调用初始化器,而是误以为类没有找到。而抛出NoClassDefFoundError.

java中使用x.class语法获得一个已知类的类对象。这一细节的实现也许随着编译器的不同而不同,但是所有的这些都是会使用Class.forName()方法。















本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/1131558 ,如需转载请自行联系原作者


相关文章
|
6天前
|
Java
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、&quot;+&quot;操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
|
6天前
|
存储 Java 编译器
课时11:综合实战:简单Java类
本次分享的主题是综合实战:简单 Java 类。主要分为两个部分: 1.简单 Java 类的含义 2.简单 Java 类的开发
|
7天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
28 5
|
6天前
|
Oracle Java 关系型数据库
课时37:综合实战:数据表与简单Java类映射转换
今天我分享的是数据表与简单 Java 类映射转换,主要分为以下四部分。 1. 映射关系基础 2. 映射步骤方法 3. 项目对象配置 4. 数据获取与调试
|
1月前
|
安全 Java 编译器
JAVA泛型类的使用(二)
接上一篇继续介绍Java泛型的高级特性。3. **编译时类型检查**:尽管运行时发生类型擦除,编译器会在编译阶段进行严格类型检查,并允许通过`extends`关键字对类型参数进行约束,确保类型安全。4. **桥方法**:为保证多态性,编译器会生成桥方法以处理类型擦除带来的问题。5. **运行时获取泛型信息**:虽然泛型信息在运行时被擦除,但可通过反射机制部分恢复这些信息,例如使用`ParameterizedType`来获取泛型参数的实际类型。
|
1月前
|
安全 Java 编译器
JAVA泛型类的使用(一)
Java 泛型类是 JDK 5.0 引入的重要特性,提供编译时类型安全检测,增强代码可读性和可维护性。通过定义泛型类如 `Box&lt;T&gt;`,允许使用类型参数。其核心原理是类型擦除,即编译时将泛型类型替换为边界类型(通常是 Object),确保与旧版本兼容并优化性能。例如,`Box&lt;T&gt;` 编译后变为 `Box&lt;Object&gt;`,从而实现无缝交互和减少内存开销。
|
4月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
223 58
|
3月前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
4月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
127 8
|
4月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
126 17

热门文章

最新文章