最近在阅读 《Inside the JVM》 这本书,结合一些日常工作学习中的感想,随便写一些东西,蜻蜓点水,不必有章法。
类的初始化工作,主要是将静态变量、常量初始化为“正确”的值(也就是程序员希望设定的特定值而非其类型的默认值),以及其它一些需要在初始化类的时候需要做的工作(如读取配置文件等)。通常我们可以这样做:
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)使用类中声明的非常量静态字段。考虑下面的例子:
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常量),它在编译的时候已经在使用它的所有地方用这个常量值直接替换了,也就是说,经过编译,实际上主运行类变成了这样:
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!");
}
}
自然地,它打印结果的时候就无需初始化甚至无需加载相关的类了。
实际中我的项目也有很多活生生的例子。比如,很多时候我们想要用一个类来管理项目中通用的常量,避免“魔数”的代码臭味,比如:
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甚至从来都没有机会被应用服务器加载。
分享到:
- 2009-11-08 22:22
- 浏览 1763
- 评论(9)
- 论坛回复 / 浏览 (8 / 5735)
- 查看更多
相关推荐
【Java学习笔记——全面解析】 Java作为一种广泛应用的高级编程语言,是软件开发领域的核心力量。这份"学习笔记——资料"涵盖了Java学习的各个方面,旨在帮助初学者和有经验的开发者巩固基础,提升技能。以下是对这...
总结来说,类的加载机制是JVM中至关重要的部分,它确保了类的正确加载、验证和初始化,使得程序能够正常运行。理解类加载机制有助于优化程序性能,解决类加载相关的错误,并实现更灵活的代码管理。
Java JDK 6学习笔记——PPT简体版是针对初学者和有经验的开发者们的一份宝贵资源,它深入浅出地介绍了Java编程语言的核心概念和技术。这份笔记以PPT的形式呈现,使得学习过程更加直观易懂,适合课堂教学或自我学习。...
JVM的类加载机制包括加载、验证、准备、解析和初始化五个阶段。其中,加载是找到类的.class文件并读入内存;验证确保字节码的正确性;准备为类的静态变量分配内存并初始化为默认值;解析将符号引用转换为直接引用;...
### JVM学习笔记核心知识点整理 #### 一、引言与背景 随着软件开发技术的不断发展,Java作为一种广泛应用的编程语言,其背后的核心技术——Java虚拟机(JVM)的重要性日益凸显。掌握JVM不仅可以帮助开发者更好地理解...
【描述】"ImagesForJVM——JVM笔记图片" 暗示这些图片可能是教学或学习笔记的一部分,旨在通过视觉化的方式解释JVM的关键概念,如内存模型、类加载机制、垃圾收集以及性能优化等方面。 【标签】"java" 明确了这些...
类加载分为加载、验证、准备、解析和初始化五个阶段,每个阶段都有其特定的任务和目的,如确保类的正确性和内存分配。 2. **运行时数据区**:JVM在运行时会创建几个不同的数据区域,包括程序计数器、虚拟机栈、本地...
《Java JDK 7学习笔记》详细介绍了JVM、JRE、Java SE API、JDK与IDE之间的对应关系。必须要时从Java SE API的源代码分析,了解各种语法在Java SE API中如何应用。 《Java JDK 7学习笔记》将IDE操作纳为教学内容...
数组的定义方式有三种:动态初始化、静态初始化和结合初始化。冒泡排序是常见的排序算法,对于 n 个元素,进行 n-1 轮排序,每轮排序进行 n-1 次比较。 二维数组可以理解为数组的数组,定义方式有三种:完全指定...
### CoreJava学习笔记 #### 一、JAVA特点与运行原理 **JAVA特点:** 1. **简单性**:Java的设计者们将C++语言中许多不易理解和容易混淆的部分去除,使得Java更容易理解与掌握。 2. **面向对象**:Java几乎一切都...
### Java学习笔记——基础知识详解 #### 一、Java开发环境(JDK)与运行环境(JRE) Java技术的核心在于其强大的跨平台能力,这主要得益于Java的两大环境:JDK(Java Development Kit)和JRE(Java Runtime ...
**Java学习笔记——Java SE基本知识** Java是一种广泛使用的面向对象的编程语言,以其跨平台、安全性高和可移植性而闻名。Java Standard Edition(Java SE)是Java平台的基础,它提供了开发和运行桌面应用程序、...
匿名内部类可以简化单个接口实现的情况,而var关键字(在Java 10才正式引入,但这里提到可能是早期的概念讨论)则允许编译器根据初始化表达式推断变量的类型,使得代码更加简洁。 此外,Java 5还引入了注解...
### Java学习笔记——设计模式与基础概念解析 #### 一、设计模式概述 设计模式是在软件开发过程中,针对常见的问题提出的经过验证的解决方案。这些模式是开发者们在长期实践中不断提炼和完善的结果,能够帮助...
### Java学习笔记——Java基础知识整理 #### 一、Java技术入门 Java是一种广泛使用的编程语言,因其跨平台性、安全性和高效性而受到青睐。Java最初由Sun Microsystems开发,并于1995年首次发布。 ##### 1. Sun ...
变量是存储数据的容器,理解它们的声明、初始化和使用是编写任何Java程序的基础。 "JAVA面向对象编程(孙卫琴)_3java操作符1.doc"和"JAVA面向对象编程(孙卫琴)_3java操作符.doc"讲解了Java中的运算符,包括算术...
构造器是用于初始化新创建对象的特殊方法,Java中的构造器名与类名相同。 四、异常处理 Java的异常处理机制使得程序能够优雅地处理错误,通过try-catch-finally语句块捕获并处理异常。异常类继承自java.lang....
3. **初始化阶段**:在类加载完成后,JVM会根据初始化指令为类的静态变量赋予初始值,并执行静态块中的代码。 #### 三、类加载机制 类加载过程是Java程序动态性的重要体现,它包括以下几个步骤: 1. **加载**:...
- **变量与常量**:讲解变量的定义、初始化及作用域,以及常量的特点与使用。 - **运算符与表达式**:覆盖各种运算符(算术运算符、比较运算符等)及其使用方法。 - **流程控制语句**:包括条件判断语句(if-else、...