【Java基础】java类加载过程与反射机制

1.类的加载、连接和初始化

当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对类进行初始化。如果没有意外,jvm将会连续完成这三个步骤,有时也把这三个步骤统称为类的加载和类初始化。

1.1 类的加载

类加载指的是将类的class文件读入内存,并且为之创建一个java.lang.Class对象,也就是说当程序中使用任何类时,都会为之创建一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建类加载器。

1.2 类的连接

当类被加载之后,系统会为之生成一个Class对象,接着将进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。连接阶段分为三个步骤:
a.验证:验证阶段用于验证被加载的类是否有正确的内部结构,并和其他类协调一致。
b.准备:准备阶段负责为类变量分配内存,并且设置默认的初始值。
c.解析:解析阶段会让JVM检查类文件中所引用的类型是否都是已知的类型,如果有运行时未知的类,那么它们也会被加载进来。

1.3 类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化,在java类中对类变量进行初始化有两种方式:1.声明类变量时指定初始值。2.使用静态初始化块对类变量指定初始值。
类变量和静态初始化块的执行优先级是同级的,例如:

public class Test {
    static{
        n=10;
    }
    static int n=9;
    public static void main(String[] args) {
        System.out.println("n="+n);//9
    }
}

这里的输出值是9,并非10,因为静态初始化块和类变量是同级的。当程序加载、连接完成以后,进入准备初始化阶段,这个时候n为0,程序按照先后循序首先使用10为n赋值,然后再使用9为n赋值。
经过上面的介绍,我们已经知道一个对象的建立需要经历加载、链接、初始化。在类加载的时候,需要使用类加载器,接下来介绍类加载器的作用。

2.类加载器

类加载器负责将.class文件加载到内存中,并为之生成java.lang.Class对象,这是后面讲到的java反射机制的基础。

当JVM启动时,会形成由三个类加载器组成的类加载器结构。
Bootstrap ClassLoader:根类加载器
Extension ClassLoader:扩展类加载器
System ClassLoader:系统类加载器
如果用户自定义了类加载器,那么还有用户类加载器。除了根类加载器外所有的类加载器都是java.lang.ClassLoader的子类,并且这些类加载器是具有层次结构的。

如图:

根类加载器:负责加载java的核心类。
扩展类加载器:负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext或者java.ext.dirs指定的目录)中的jar包。
系统类加载器:负责在JVM启动时,加载来自java命令的-classpath选项,或是java.class.path系统属性,或是CLASSPATH环境变量所指定的JAR包和类路径。

public static void main(String[] args) throws IOException {
        System.out.println("//系统类加载器");
        ClassLoader systemLoader=ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器:"+systemLoader);
        /* 获得系统类加载器的加载路径
         * 通常为CLASSPATH环境变量指定的值。
         * 如果没有指定CLASSPATH环境变量,那么默认以当前路径作为系统类的加载路径。
         */
        Enumeration<URL> em1=systemLoader.getResources(".");
        while(em1.hasMoreElements()){
            System.out.println(em1.nextElement());
        }

        System.out.println("//扩展类加载器");
        ClassLoader extensionLoader=systemLoader.getParent();//系统类加载器的父类就是扩展类加载器
        System.out.println("扩展类加载器:"+extensionLoader);
        System.out.println("扩展类加载器的加载路径:"+System.getProperty("java.ext.dirs"));
        System.out.println("扩展类加载器的父加载器:"+extensionLoader.getParent());//获得根加载器

        System.out.println("//根类加载器");
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for(int i=0;i<urls.length;i++){
            System.out.println(urls[i].toExternalForm());
        }
    }

打印结果为:

//系统类加载器
系统类加载器:sun.misc.Launcher$AppClassLoader@addbf1
file:/D:/workspace/pc141/Test/bin/
//扩展类加载器
扩展类加载器:sun.misc.Launcher$ExtClassLoader@42e816
扩展类加载器的加载路径:D:\App\jdk\jre1.6\lib\ext;C:\Windows\Sun\Java\lib\ext
扩展类加载器的父加载器:null
//根类加载器
file:/D:/App/jdk/jre1.6/lib/resources.jar
file:/D:/App/jdk/jre1.6/lib/rt.jar
file:/D:/App/jdk/jre1.6/lib/sunrsasign.jar
file:/D:/App/jdk/jre1.6/lib/jsse.jar
file:/D:/App/jdk/jre1.6/lib/jce.jar
file:/D:/App/jdk/jre1.6/lib/charsets.jar
file:/D:/App/jdk/jre1.6/lib/modules/jdk.boot.jar
file:/D:/App/jdk/jre1.6/classes

从上面的打印结果可以看出,系统类加载器的父加载器是扩展类加载器,扩展类类加载器的父加载器是根加载器,但是为什么调用扩展类加载器的getParent()却返回null呢?这是因为根加载器比较特殊,它不是ClassLoader的子类。

原文地址:https://www.cnblogs.com/leiblog/p/10447230.html

时间: 2024-08-24 13:29:33

【Java基础】java类加载过程与反射机制的相关文章

Java基础 -- 深入理解Java类型信息(Class对象)与反射机制

一 RTTI概念 认识Claa对象之前,先来了解一个概念,RTTI(Run-Time Type Identification)运行时类型识别,对于这个词一直是 C++ 中的概念,至于Java中出现RTTI的说法则是源于<Thinking in Java>一书,其作用是在运行时识别一个对象的类型和类的信息,这里分两种: 传统的”RTTI”:它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象时该类必须已定义好): 反射机制,它允许我们在运

复习java基础第七天(反射)

一:目标 Ø理解 Class 类 Ø理解 Java 的类加载机制 Ø学会使用 ClassLoader 进行类加载 Ø理解反射的机制 Ø掌握 Constructor.Method.Field 类的用法 Ø理解并掌握动态代理 1.Class类 –对象照镜子后可以得到的信息:某个类的数据成员名.方法和构造器.某个类到底实现了哪些接口. 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象. 一个 Class 对象包含了特定某个类的有关信息. –  Class 对象只能由系统建立对象.

第十二章 类加载器和反射机制

12 类加载器和反射机制 12.1 类加载器 负责将.class文件加载到内存中,并为之生成对应的Class对象. 1.类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载.连接.初始化三个步骤来实现对这个类的初始化. 加载 就是指将calss文件读入到内存,并为之穿件一个Class对象. 任何类被使用时系统都会建立一个Class对象. 连接 验证    是否有正确的内部结构,并和其他类协调一致 准备    负责为类的静态成员分配内存,并设置默认初始化值 解析    将类

[Java基础] Java对象内存结构

转载地址:http://www.importnew.com/1305.html 原文于2008年11月13日 发表, 2008年12月18日更新:这里还有一篇关于Java的Sizeof运算符的实用库的文章. 学C/C++出身的我,对Java有一点非常困惑,那就是缺乏计算对象占用内存大小的机制.而在C++中就可以通过sizeof运算符来获得基本类型以及类实例的大小.C和C++中的这个操作符对于指针运算.内存拷贝和IO操作都非常有用. Java中并没有一个类似的运算符.事实上,Java也不需要这种运

[Java基础] Java线程复习笔记

先说说线程和进程,现代操作系统几乎无一例外地采用进程的概念,进程之间基本上可以认为是相互独立的,共享的资源非常少.线程可以认为是轻量级的进 程,充分地利用线程可以使得同一个进程中执行多种任务.Java是第一个在语言层面就支持线程操作的主流编程语言.和进程类似,线程也是各自独立的,有自 己的栈,自己的局部变量,自己的程序执行并行路径,但线程的独立性又没有进程那么强,它们共享内存,文件资源,以及其他进程层面的状态等.同一个进程内的 多个线程共享同样的内存空间,这也就意味着这些线程可以访问同样的变量和

Java基础--Java入门

IsCoder 标记: Java基础,Java环境配置 一.Java环境配置 Java Develop Kit(JDK安装) 系统环境变量设置(JAVA_HOME) Java源程序编辑工具 Java编译运行命令 运行经典的HelloWorld程序 1.1 JDK安装 JDK,就是甲骨文公司提供给我们的Java开发工具包,包括最常用的Javac.exe编译工具和Java.exe运行工具.需要指出的是,JDK中已经包含了JER(Java Runtime Environment,Java运行时环境),

程序猿二三事之Java基础--Java SE 5增加的特性--语法篇(一)

程序猿二三事之Java基础–Java SE 5增加的特性–语法篇(一) [ TOC ] 为什么是Java SE 5? 目前已经到了JDK-8u74了,JDK7的主版本已经于2015年4月停止公开更新. 那为什么还要来说Java/JDK5呢? Java SE在1.4(2002)趋于成熟,随着越来越多应用于开发企业应用,许多框架诞生于这个时期或走向成熟. Java SE 5.0的发布(2004)在语法层面增加了很多特性,让开发更高效,代码更整洁. 自动装箱/拆箱.泛型.注解.for循环增强.枚举.可

[Java基础] java的守护线程与非守护线程

最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以前忽略了). 估计学过Unix开发但是没有细致学习Java的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构 建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说

Java基础--Java编程规范

IsCoder 标记: Java基础,Java编程规范 摘要:Java虽然没有强制性的编程规范,但是为了便于统一,Java有不成文的编程规范,为了形成良好的编程习惯,建议熟悉并遵守Java编程规范,提高代码的阅读性. 一.Java标识符 在任何语言中,都要自己的一套标识符规则.Java的标识符包括:Java关键字.Java特殊功能的标识符.合法的用户自定义标识符.其中,我们能改变的只有自定义的标识符,和大部分计算机编程语言一样,Java标识符原始只支持ASCII的编码,但是随着Java在世界的影