类的初始化工作,主要是将静态变量、常量初始化为“正确”的值(也就是程序员希望设定的特定值而非其类型的默认值),以及其它一些需要在初始化类的时候需要做的工作(如读取配置文件等)。通常我们可以这样做:
Java代码
class A extends B {
public static int intVal = 30;
public static String strVal;
static {
strVal = readConfig("ItemA");
}
private static String readConfig(String key) {
....
}
}
当一个类被加载,它将顺序经历四个过程:验证、准备、解析、初始化(一个类被加载以前,如果其父类尚未初始化,那么JVM会先去用以上四个过程对这个父类进行初始化)。验证只是检查class文件是否符合java语义并且不会损害JVM的完整性,这里不多赘述。准备阶段是为类成员分配内存,同时根据该成员的类型赋给它相应的默认值,对于上面的示例类A,经过准备阶段后状态是这样的:
intVal = 0;
strVal = null;
解析是把符号引用转为直接引用的过程,比如,原来JVM只知道类A有一个成员叫"intVal",通过解析则知道了该成员的地址是0xBBEDF,以后再使用这个成员的时候,就直接去这个内存地址去找了。同时在这个阶段,类的方法比如上面的readConfig()也会被映射到某个内存地址以待调用。
初始化则是利用类定义的JAVA代码确定成员变量的初值(如果对某个成员没有相应的java代码对其进行初始赋值操作,那么它就沿用在准备阶段获得的该类型默认值)。在这个阶段,所有的静态代码都会被执行,事实上,类在编译时编译器是会把这些静态代码封装到一个<clinit>方法中去的,在使用这个类的时候,初始化过程就会调用这个<clinit>方法。这个方法程序员是不能调用的,它只能被JVM调用。
以上对JVM初始化一个类的过程做了一些讲解,但是JVM究竟什么时候才会初始化一个类呢?总的来说,JVM会在“主动”使用一个类的时候将该类初始化。所谓“主动”,大致有6种已知的行为,对我们比较常见的是:1)试图创建该类的一个新实例;2)调用该类声明的一个静态方法;3)使用类中声明的非常量静态字段。考虑下面的例子:
Java代码
public interface Angry {
String greeting = "Grrrr!";
int angerLevel = Dog.getAngerLevel();
}
public class Dog implements Angry {
public static final String greeting = "Wong, Wong, Wong!";
static {
System.out.println("Dog was initialized.");
}
public static int getAngerLevel() {
System.out.println("Angry was initialized.");
return 1;
}
}
public class Main {
public static void main(String[] args) throws Exception {
testClassInit();
}
public static void testClassInit() throws Exception {
//passive use of Angry
System.out.println(Angry.greeting);
//passive use of Dog
System.out.println(Dog.greeting);
}
}
如果可以的话,看官可以先猜一猜输出的结果是什么。
其实注释里已经提示得很清楚了,两个输出语句都是用被动方式调用的Angry和Dog的成员,因此无论是"Dog was initialized."还是"Angry was initialized."都不会被打印。换句话说,Angry和Dog都没有被初始化。
为什么类没有被初始化但它的静态final成员还是可以正确打印呢?实际上,像声明为“public static final String”的静态常量(注意,在接口里声明String效果是一样的,它实际上也是个final常量),它在编译的时候已经在使用它的所有地方用这个常量值直接替换了,也就是说,经过编译,实际上主运行类变成了这样:
Java代码
public class Main {
public static void main(String[] args) throws Exception {
testClassInit();
}
public static void testClassInit() throws Exception {
//passive use of Angry
System.out.println("Grrrr!");
//passive use of Dog
System.out.println("Wong, Wong, Wong!");
}
}
自然地,它打印结果的时候就无需初始化甚至无需加载相关的类了。
实际中我的项目也有很多活生生的例子。比如,很多时候我们想要用一个类来管理项目中通用的常量,避免“魔数”的代码臭味,比如:
Java代码
class ConstManager {
public static final String SIGN_DASH = "-";
public static final String SIGN_SLASH = "/";
public static final String SIGN_COMMA = ",";
......
}
然后我们会在项目的某处会以ConstManager.SIGN_COMMA 来代替逗号。这对我们管理项目是有益的,当需要修改一个符号的时候,只需要在一处修改再编译就行了。但实际上,编译后的class文件,在用到这些常量的代码块的位置,这些符号早就被替换成真实的","之类的值了。在项目运行的过程中,ConstManager甚至从来都没有机会被应用服务器加载。
原文:http://www.iteye.com/topic/512550
分享到:
相关推荐
本篇文章将深入探讨“Java起航——类的初始化历程”,并结合JVM(Java虚拟机)的工作原理,帮助你更好地理解这个过程。 首先,我们需要了解Java类的生命周期,它包括加载、验证、准备、解析和初始化五个阶段。当一...
### JVM底层原理——类加载子系统详解 #### 一、引言 Java虚拟机(JVM)作为Java程序运行的基础环境,其内部结构复杂且功能强大。本文将基于“JVM底层原理课件PPT”中关于类加载子系统的介绍进行深入解析。类加载子...
其中,类加载子系统负责加载、验证、准备和初始化类文件;运行时数据区包括堆、栈、方法区、程序计数器和本地方法栈,它们各自承担着不同的职责,如存储对象实例、管理方法执行上下文等;执行引擎是JVM的心脏,它...
本文将重点探讨类加载器的角色,以及类的加载、连接和初始化这三个阶段。 1. **类加载**: 类加载是JVM获取Java类字节码文件并将其转换为内存中`java.lang.Class`对象的过程。这个过程通常由类加载器完成。Java源...
总结来说,类的加载机制是JVM中至关重要的部分,它确保了类的正确加载、验证和初始化,使得程序能够正常运行。理解类加载机制有助于优化程序性能,解决类加载相关的错误,并实现更灵活的代码管理。
5. **初始化**:这是类加载的最后一步,在之前的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码...
- **数组**:学习如何声明、初始化和操作一维或多维数组。 - **集合框架**:ArrayList、LinkedList、HashSet、HashMap等容器的理解和使用,以及它们之间的区别和应用场景。 4. **异常处理** - **异常分类**:...
5. **初始化**:为类的静态变量赋初始值。 **2.2 加载.class文件的方式** 加载`.class`文件的方式多样,常见的有: 1. **从本地系统直接加载**:最常见的加载方式,从本地文件系统加载。 2. **通过网络下载**:从...
JVM的类加载机制包括加载、验证、准备、解析和初始化五个阶段。其中,加载是找到类的.class文件并读入内存;验证确保字节码的正确性;准备为类的静态变量分配内存并初始化为默认值;解析将符号引用转换为直接引用;...
其中,类的初始化阶段是执行类构造器`()`的地方,负责对类变量进行初始值设置。 #### 字节码解释与JIT编译 Java程序最初以字节码形式运行,由解释器解释执行。为了提高性能,JIT(Just-In-Time)编译器会在运行时将...
`main()`函数通常包含初始化参数`String[] args`,这部分的局部变量会在栈帧中分配空间。 ##### Min()函数的调用 当从`main()`函数中调用`Min()`函数时,JVM会创建一个新的栈帧来保存局部变量和参数,并跳转到相应...
- **初始化(Initialization)**:执行类构造器`()`方法,对类变量进行初始化。 #### 二、类加载器概述 **1. 定义** 类加载器是Java中用于加载类文件的组件。当Java程序运行时,类加载器负责从文件系统、网络等来源...
在这个过程中,mini-jvm需要处理类的依赖关系,确保正确地初始化和加载类。 七、异常处理与垃圾回收 尽管mini-jvm简化了许多JVM的功能,但依然需要实现基本的异常处理框架。同时,虽然可能没有完全实现垃圾回收,...
- **初始化**:执行类初始化代码,即执行类的静态初始化器和静态初始化块。 每个步骤都有其特定的作用,保证了类能够被正确且安全地加载和使用。 ##### 装载的实现 在JVM中,类的装载是由`ClassLoader`及其子类...
这样,开发者可以直接利用这个模板进行新库的初始化,无需从零开始设置各种环境,极大地提高了开发效率。 在深入探讨这个模板之前,我们先了解一下Gradle,它是项目构建的首选工具,尤其是在Java和Kotlin生态系统中...
【描述】"ImagesForJVM——JVM笔记图片" 暗示这些图片可能是教学或学习笔记的一部分,旨在通过视觉化的方式解释JVM的关键概念,如内存模型、类加载机制、垃圾收集以及性能优化等方面。 【标签】"java" 明确了这些...
JVM的类加载机制包括加载、验证、准备、解析和初始化五个阶段。"jvm-demo"可能包含相关的类加载示例,帮助我们理解类如何被动态地加载到内存中,并进行初始化。 3. **运行时数据区** JVM内部划分了不同的区域来...
了解构造函数的作用,它是初始化新创建的对象时调用的方法。 6. **包与访问修饰符**:Java使用包来组织类,提供命名空间和访问控制。访问修饰符有public、private、protected和默认(包级私有),它们决定了类、...
其中,类加载子系统负责加载、验证、准备和初始化类;运行时数据区包括堆、栈、方法区、程序计数器和本地方法栈,用于存储程序运行时的数据。 2. **类加载机制** 类加载分为加载、验证、准备、解析和初始化五个...
《JVM参数调优——深度解析与实践指南》 在Java开发中,JVM(Java Virtual Machine)扮演着至关重要的...通过对"jvmSample-master"这样的实战案例学习,我们可以更好地理解JVM的工作原理,提高我们的Java应用性能。