`
annan211
  • 浏览: 461056 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

所谓高手程序员犯下的错误

 
阅读更多
看下面一段代码,这是一个网上程序员写下的一段代码,摘录而来,你能根据

http://annan211.iteye.com/blog/2118115

来分析其出错的原因吗?

package com.ccb.framework.enums;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class CachingEnumResolver {
	//单态实例 一切问题皆由此行引起
	private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = new  
	CachingEnumResolver(); //[1]
	/*MSGCODE->Category内存索引*/
	private static Map CODE_MAP_CACHE;//[2]
	static {
		CODE_MAP_CACHE = new HashMap();
		//为了说明问题,我在这里初始化一条数据
		CODE_MAP_CACHE.put("0","北京市");
	}
	
	//private, for single instance
	private 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.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());
	}
}


写下这段代码的作者的本意是希望 输出一个包含 3个键值对的map,但是结果却只输出了
CODE_MAP_CACHE为空,问题在这里开始暴露.
{0=北京市}

要想发现问题的根源 和 解决问题,我们还需要从基础做起,从类的加载说起。

Java 文件被jvm编译器 变异成字节码 也就是class 文件,class 文件中包含了这个类所有的基本信息。字节码被加载进入内存,或者主动或被动的被初始化,这里的主动和被动 如下解释:

主动引用:

创建某个类的新实例--新实例可通过new 关键字,反射,克隆,反序列化创建
调用某个类的静态方法
调用某个类或接口的静态字段(final修饰的除外)

使用Java的某些反射方法
初始化某个类的子类
虚拟机启动时包含有main的启动类


以上6种情形会造成类的初始化。

这里有必要把类的加载和初始化区分开,加载是指把class字节码载入内存,一个类的初始化过程包含以下几个步骤
  1 加载
 
加载 :
     这里的加载是指将class文件载入内存,由类加载器执行,并且创建一个Class 对象放入堆中。
  
   
  2 连接 
 
连接:
     这里的连接 是指 将已经加载的Java 二进制代码 组合到jvm运行时内存当中。
     连接又包含以下几步骤:
        验证阶段:
            验证即是检验有无恶意代码,检查final 类有没有被继承等不符合Java规范的代码快存在。
        准备阶段:
            准备阶段主要是创建静态域,分配空间。将基本类型的变量0,boolean类型的变量设置为false,引用类型设置为 null.
        解析阶段:
            解析的过程就是对类中的接口、类、方法、变量的符号引用进行解析并定位,解析成直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址),并保证这些类被正确的找到。解析的过程可能导致其它的类被加载。需要注意的是,根据不同的解析策略,这一步不一定是必须的,有些解析策略在解析时递归的把所有引用解析,这是early resolution,要求所有引用都必须存在;还有一种策略是late resolution,这也是Oracle 的JDK所采取的策略,即在类只是被引用了,还没有被真正用到时,并不进行解析,只有当真正用到了,才去加载和解析这个类。
       
  

  3 初始化
 
初始化
          首先执行静态代码块static{},接着是静态变量,接着是静态方法。
          当执行到 private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = new CachingEnumResolver(); 时,会出现jvm对Java对象的初始化,会按照 类内部静态块 > 类静态属性 > 类内部属性 > 类构造函数  将类的属性进行初始化。
  


  根据以上的理基础  我们来分析原作者的这段代码
  第一步 字节码被加载
  第二部 被加载的字节码需要经过连接阶段,在连接阶段 先后经过 检查 准备 解析三个小阶段。 在准备阶段  将 静态变量 SINGLE_ENUM_RESOLVER   CODE_MAP_CACHE  设置为null.
  接着解析,分析是否还有其他需要被载入初始化的类。
  第三步 初始化阶段 按照  类内部静态块 > 类静态属性 > 类内部属性 > 类构造函数优先级顺序一次初始化


类初始化顺序


对于普通的Java程序,一般都不需要显式的声明来动态加载Java类,只需要用import关键字将相关联的类引入,类被第一次调用的时候,就会被加载初始化。那对于一个类对象,其内部各组成部分的初始化顺序又是如何的呢?
         一个Java类对象在初始化的时候必定是按照一定顺序初始化其静态块、静态属性、类内部属性、构造方法。这里我们讨论的初始化分别针对两个对象,一个是类本身还有一个是类实例化的对象。
         类本身的初始化会在类被加载完毕、链接完成之后,由Java虚拟机负责调用<clinit>方法完成。在这个方法中依次完成了堆类内部静态块的调用和类内部静态属性的初始化(如果存在父类,父类会优先进行初始化)。不论创建多少个实例化的对象,一个类只会被初始化一次。
         类实例化的对象通过new操作创建,Java虚拟机保证一个类在new操作实例化其对象之前已经完成了类的加载、链接和初始化。之后Java虚拟机会调用<init>方法完成类实例化对象的初始化。这个方法会优先按照代码中顺序完成对类内部个属性的初始化,之后再调用类的构造函数(如果有父类,则优先调用父类的构造函数)。
  PS:需要注意的是上述提到的<init>和<clinit>方法都是非法的Java方法名,是由编译器命名的,并不能由编码实现。
  综上所述,我们大致可以得出以下结论,对于一个类,在实例化一个这个类的对象时,我们可以保证以下这样的优先级进行初始化:类内部静态块 > 类静态属性 > 类内部属性 > 类构造函数[
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    C++程序员容易犯的十个C#错误

    C++程序员容易犯的十个C#错误 C++程序员在转换到C#时,经常会遇到一些错误。本文将讨论C++程序员最容易犯的十个错误,帮助C++程序员更好地理解C#语言和.NET Framework。 错误1:析构函数上的差异 C++程序员在使用...

    论全世界所有程序员都会犯的错误

    论全世界所有程序员都会犯的错误

    程序员不应该再犯的五大编程错误

    作为一名程序员,犯错误是不可避免的,尤其是在学习编程的初期。然而,重要的是要学会从错误中吸取教训,避免重复犯同样的错误。以下就是程序员应该避免的五大编程错误: 1. 不理解就复制代码:这种做法可能导致...

    程序员犯的非技术错误(Top 5)[1].doc

    本文主要探讨了程序员们经常犯的五个非技术错误,这些错误往往会影响团队协作和项目成功。 首先,缺乏团队纪律是一个常见问题。正如Jim Rohn所说,纪律是实现目标与成就之间的桥梁。在软件开发中,团队合作至关重要...

    DSP C2000程序员高手进阶 PDF 版

    介绍 DSP C2000 的开发方法,适合DSP 程序员 高手进阶

    DSP C2000程序员高手进阶

    DSP C2000程序员高手进阶,对初入门想提高的同学是个不错的东西

    DSP_C2000_程序员高手进阶.pdf

    DSP_C2000_程序员高手进阶.pdf

    Python-中国程序员容易发音错误的单词

    中国程序员容易发音错误的单词 (以美式发音为准, 非音标为字母发音)

    程序员高手是怎样炼成的

    程序员高手是怎样炼成的

    程序员最常犯的五大非技术性错误

    程序员最常犯的五大非技术性错误

    程序员高手进阶经验

    本文将深入探讨“程序员高手进阶经验”,旨在帮助那些希望通过学习和掌握C语言来提升自身技能的开发者。 C语言是系统级编程的首选语言,它简洁、高效,能够直接操作硬件资源,这使得C语言在操作系统、嵌入式系统...

    android开发高手进阶中国程序员47131.epub

    android开发高手进阶中国程序员

    程序员专用 编程输入法

    总的来说,程序员专用的输入法如精灵输入法,是通过提供高效便捷的编码环境,帮助程序员提升工作效率,减少错误,从而更好地投入到软件开发工作中。这样的工具对于经常进行编程工作的人员来说是非常有价值的。

    2018程序员上午题

    试题中的错误分类,例如段落标题编号错误、语句不通顺、错别字、格式问题等,都是软件开发中需要注意的细节,尤其是文档规范性,对于专业程序员来说非常重要。 【知识点二】:统计学基础 试题中的调查问题涉及到了...

    程序员系列丛书《程序员接单宝典━资深高手谈接外包项目》作者:韦刃

    《程序员接单宝典━资深高手谈接外包项目》是由韦刃所著的一本专为程序员设计的指导书籍,旨在帮助那些希望扩展职业发展、尝试接单或创业的程序员提供宝贵的实战经验和策略。这本书深入探讨了程序员如何在接外包项目...

    2005年下半年程序员级试题及答案

    3. "2005下半年程序员级试题答案.doc" - 这是关键的参考资料,它提供了所有试题的标准答案,考生可以通过比对自己的解答来评估学习效果,找出错误并进行针对性的复习。 这些试题和答案的组合,为学习者提供了一个...

    《Java程序员》

    本书中既有在公司中的生存技巧,又有高手达人的进阶策略,既有求职攻略的按图索骥,又有入职后生产环境的破解揭秘。 本书中浓缩了程序员求职与工作、生存与发展的点滴经验,希望本书能够成为你的朋友。望本书能让...

Global site tag (gtag.js) - Google Analytics