解析 Java 类和对象的初始化过程

上传人:M****1 文档编号:474016068 上传时间:2023-04-28 格式:DOCX 页数:10 大小:48.88KB
返回 下载 相关 举报
解析 Java 类和对象的初始化过程_第1页
第1页 / 共10页
解析 Java 类和对象的初始化过程_第2页
第2页 / 共10页
解析 Java 类和对象的初始化过程_第3页
第3页 / 共10页
解析 Java 类和对象的初始化过程_第4页
第4页 / 共10页
解析 Java 类和对象的初始化过程_第5页
第5页 / 共10页
点击查看更多>>
资源描述

《解析 Java 类和对象的初始化过程》由会员分享,可在线阅读,更多相关《解析 Java 类和对象的初始化过程(10页珍藏版)》请在金锄头文库上搜索。

1、解析Java类和对象的初始化过程由一个单态模式引出的问题谈起简介:类的初始化和对象初始化是JVM管理的类型生命周期中非常重要的两个环节, Google 了一遍网络,有关类装载机制的文章倒是不少,然而类初始化和对象初 始化的文章并不多,特别是从字节码和JVM层次来分析的文章更是鲜有所见。本文主要对类和对象初始化全过程进行分析,通过一个实际问题引入,将源代码 转换成JVM字节码后,对JVM执行过程的关键点进行全面解析,并在文中穿插 入了相关JVM规范和JVM的部分内部理论知识,以理论与实际结合的方式介绍 对象初始化和类初始化之间的协作以及可能存在的冲突问题。问题引入近日我在调试一个枚举类型的解析器

2、程序,该解析器是将数据库内一万多条枚举 代码装载到缓存中,为了实现快速定位枚举代码和具体枚举类别的所有枚举元素, 该类在装载枚举代码的同时对其采取两种策略建立内存索引。由于该类是一个公 共服务类,在程序各个层面都会使用到它,因此我将它实现为一个单例类。这个 类在我调整类实例化语句位置之前运行正常,但当我把该类实例化语句调整到静 态初始化语句之前时,我的程序不再为我工作了。下面是经过我简化后的示例代码:清单一package com.ccb.framework.enums;import java.util.Collections;import java.util.HashMap;import ja

3、va.util.Map;public class CachingEnumResolver /单态实例 一切问题皆由此行引起private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = newCachingEnumResolver();/*MSGCODE-Category 内存索引*/private static Map CODE_MAP_CACHE;static CODE_MAP_CACHE = new HashMap(); /为了说明问题,我在这里初始化一条数据CODE_MAP_CACHE.pu t( 0, 北京市);/pri

4、vate, for single instanceprivate CachingEnumResolver() /初始化加载数据 引起问题,该方法也要负点责任 initEnums(); /* 初始化所有的枚举类型*/public static void initEnums() / 问题从这里开始暴露 /if (null = CODE_MAP_CACHE) System.out.println(CODE_MAP_CACHE 为空,问题在 这里开始暴露.);CODE_MAP_CACHE = new HashMap();CODE_MAP_CACHE.put(1, 北京市);CODE_MAP_CACHE

5、.put(2, 云南省);/other code. public Map getCache() return Collections.unmodifiableMap(CODE_MAP_CACHE); /* 获取单态实例* return*/public static CachingEnumResolver getInstance() return SINGLE_ENUM_RESOLVER; public static void main(String args) System.out.println(CachingEnumResolver.getInstance().getCache ();想必

6、大家看了上面的代码后会感觉有些茫然,这个类看起来没有问题啊,这的确 属于典型的饿汉式单态模式啊,怎么会有问题呢?是的,他看起来的确没有问题,可是如果将他run起来时,其结果是他不会为 你正确work。运行该类,它的执行结果是:清单二CODE_MAP_CACHE为空,问题在这里开始暴露.0=北京市 我的程序怎么会这样?为什么在initEnum()方法里CODE_MAP_CACHE为空?为什么我输出的CODE_MAP_CACHE内容只有一个元素,其它两个元素呢?!看到这里,如果是你在调试该程序,你此刻一定觉得很奇怪,难道是我的Jvm有 问题吗?非也!如果不是,那我的程序是怎么了?这绝对不是我想要的

7、结果。可 事实上无论怎么修改initEnum()方法都无济于事,起码我最初是一定不会怀疑 到问题可能出在创建CachingEnumResolver实例这一环节上。正是因为我太相 信我创建CachingEnumResolver实例的方法,加之对Java类初始化与对象实 例化底层原理理解有所偏差,使我为此付出了三、四个小时一约半个工作日的大 好青春。那么问题究竟出在哪里呢?为什么会出现这样的怪事呢?在解决这个问题之前, 先让我们来了解一下JVM的类和对象初始化的底层机制。回页首 类的生命周期上图展示的是类生命周期流向;在本文里,我只打算谈谈类的初始化以及对 象实例化两个阶段。回页首类初始化类初始化

8、阶段,它是一个类或接口被首次使用的前阶段中的最后一项工作,本 阶段负责为类变量赋予正确的初始值。Java编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到 方法内,该方法只能被Jvm调用,专门承担初始化工作。除接口以外,初始化一个类之前必须保证其直接超类已被初始化,并且该初始化 过程是由Jvm保证线程安全的。另外,并非所有的类都会拥有一个() 方法,在以下条件中该类不会拥有()方法:该类既没有声明任何类变量,也没有静态初始化语句;该类声明了类变量,但没有明确使用类变量初始化语句或静态初始化语句 初始化;该类仅包含静态final变量的类变量初始化语句,并且类变量初始化语 句是编译时常量

9、表达式。回页首对象初始化 在类被装载、连接和初始化,这个类就随时都可能使用了。对象实例化和初始化 是就是对象生命的起始阶段的活动,在这里我们主要讨论对象的初始化工作的相 关特点。Java编译器在编译每个类时都会为该类至少生成一个实例初始化方法一即 ()方法。此方法与源代码中的每个构造方法相对应,如果类没有明确 地声明任何构造方法,编译器则为该类生成一个默认的无参构造方法,这个默认 的构造器仅仅调用父类的无参构造器,与此同时也会生成一个与默认构造方法对 应的()方法.通常来说,()方法内包括的代码内容大概为:调用另一个()方 法;对实例变量初始化;与其对应的构造方法内的代码。如果构造方法是明确地

10、从调用同一个类中的另一个构造方法开始,那它对应的 ()方法体内包括的内容为:一个对本类的()方法的调用;对应 用构造方法内的所有字节码。如果构造方法不是通过调用自身类的其它构造方法开始,并且该对象不是 Object对象,那()法内则包括的内容为:一个对父类()方法 的调用;对实例变量初始化方法的字节码;最后是对应构造子的方法体字节码。如果这个类是Object,那么它的()方法则不包括对父类()方 法的调用。回页首类的初始化时机本文到目前为止,我们已经大概有了解到了类生命周期中都经历了哪些阶段,但 这个类的生命周期的开始阶段一类装载又是在什么时候被触发呢?类又是何时 被初始化的呢?让我们带着这三

11、个疑问继续去寻找答案。Java虚拟机规范为类的初始化时机做了严格定义:initialize on first activeuse-在首次主动使用时初始化。这个规则直接影响着类装载、连接 和初始化类的机制一因为在类型被初始化之前它必须已经被连接,然而在连接之 前又必须保证它已经被装载了。在与初始化时机相关的类装载时机问题上,Java虚拟机规范并没有对其做严格 的定义,这就使得JVM在实现上可以根据自己的特点提供采用不同的装载策略。 我们可以思考一下JbossAOP框架的实现原理,它就是在对你的class文件装 载环节做了手脚一插入了 AOP的相关拦截字节码,这使得它可以对程序员做到 完全透明化,

12、哪怕你用new操作符创建出的对象实例也一样能被AOP框架拦截 与之相对应的Spring AOP,你必须通过他的BeanFactory获得被AOP代理 过的受管对象,当然JbossAOP的缺点也很明显一他是和JBOSS服务器绑定很 紧密的,你不能很轻松的移植到其它服务器上。嗯,说到这里有些跑题了, 要知道AOP实现策略足可以写一本厚厚的书了,嘿嘿,就此打住。说了这么多,类的初始化时机就是在在首次主动使用时,那么,哪些情形下才 符合首次主动使用的要求呢?首次主动使用的情形:创建某个类的新实例时-new、反射、克隆或反序列化;调用某个类的静态方法时;使用某个类或接口的静态字段或对该字段赋值时(fin

13、al字段除外);调用Java的某些反射方法时初始化某个类的子类时在虚拟机启动时某个含有main ()方法的那个启动类。除了以上几种情形以外,所有其它使用JAVA类型的方式都是被动使用的,他们 不会导致类的初始化。回页首我的问题究竟出在哪里好了,了解了 JVM的类初始化与对象初始化机制后,我们就有了理论基础,也就 可以理性的去分析问题了。下面让我们来看看前面清单一的JAVA源代码反组译出的字节码:清单三public class com.ccb.framework.enums.CachingEnumResolver extends java.lang.Objectstatic ;Code:0:ne

14、w #2; /class CachingEnumResolver3: dup4:invokespecial #14; /Method :()V 7:putstatic#16; /FieldSINGLE_ENUM_RESOLVER:Lcom/ccb/framework/enums/CachingEnumResolver;10: new #18; /class HashMap13:dup14: invokespecial #19; /Method java/util/HashMap.:()V17: putstatic#21; /Field CODE_MAP_CACHE:Ljava/util/Map

15、;20: getstatic#21; /Field CODE_MAP_CACHE:Ljava/util/Map;23: ldc#23; /String 025: ldc#25; /String 北京市27: invokeinterface #31, 3; /InterfaceMethodjava/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Ob ject; 32: pop33: returnprivate com.ccb.framework.enums.CachingEnumResolver();Code:0: aload_01: invokespecial

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号