转载:http://blog.csdn.net/moreevan/article/details/6968718

我们知道一个类(class)要被使用必须经过装载,连接,初始化这样的过程。下面先对这三阶段做一个简单的描述,之后会结合一个简单的例子来说明Java中类的初始化过程。

在装载阶段,类装载器(Bootstrap ClassLoader 或者用户自定义的ClassLoader) 把编译形成的class文件载入内存,创建类相关的Class对象,这个Class对象封装了我们要使用的类的类型信息。

连接阶段又可以分为三个子步骤:验证、准备和解析。

验证就是要确保java类型数据格式 的正确性,并适于JVM使用。

准备阶段,JVM为静态变量分配内存空间,并设置默认值,注意,这里是设置默认值,比如说int型的变量会被赋予默认值0 。在这个阶段,JVM可能还会为一些数据结构分配内存,目的 是提高运行程序的性能,比如说方法表。

解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。

类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。而我们这里所说的主动使用 包括:

1.        创建类的实例

2.        调用类的静态方法

3.        使用类的非常量静态字段

4.        调用JavaAPI中的某些反射方法

5.        初始化某个类的子类

6.        含有main()方法的类启动时

初始化一个类包括两个步骤:

1、  如果类存在直接父类的话,且直接父类还没有被初始化,则先初始化其直接父类

2、  如果类存在一个初始化方法,就执行此方法

注:初始化接口并不需要初始化它的父接口。

好,下面我们通过一些简单的例子来重点看一下初始化的过程 。

好,先上代码(超级简单的代码)。

  1. package kevin.demo;
  2. class Base {
  3. int a;
  4. static int b;
  5. static int c = 1;
  6. static {
  7. b=2;
  8. c = 3;
  9. System.out.println("Base:static init block invoked..");
  10. }
  11. void display() {
  12. System.out.println("Base:a=" + a + ",b=" + b + ",c=" + c);
  13. }
  14. }
  15. class Derived extends Base {
  16. int d;
  17. static int e = 4;
  18. static {
  19. e= 5;
  20. System.out.println("Derived:static init block invoked...");
  21. }
  22. void display() {
  23. super.display();
  24. System.out.println("Derived:d=" + d + ",e=" + e);
  25. }
  26. }

好,这里我们定义了一个简单的基类Base,然后定义了一个类Derived继承它。这个已经简单到我不知道说什么了,直接上main方法吧。

  1. public class Demo {
  2. public static void main(String[] args) {
  3. new Derived().display();
  4. }
  5. }

好,我们执行这个方法,看会输出什么。。上个图看一下:

如图所示,虽然我们调用的子类的构造函数创建对象 ,但是父类中的有关初始化的语句和静态初始化块也会被执行或调用,这正与前面所说的:如果一个类的直接父类还没有被初始化,那么 先初始化它的父类。而为什么这些初始化的动作会执行,就是因为我们主动使用了Derived这个类。而我们上面列出了好几个主动使用的情况,好下面我们修改一下main方法,看上面所言是否正确。

注:因为我们在这里谈论的主题是类的初始化,所以关于对象的初始化过程我们这里暂不讨论。

好,看下修改后的Demo:

  1. public class Demo {
  2. static int f;
  3. static{
  4. f=100;
  5. System.out.println("Demo:static init block invoked..and f="+f);
  6. }
  7. public static void main(String[] args) {
  8. System.out.println("maininvoked..");
  9. }}

好,看执行结果:

好,我们可以看到,含有main函数的类会首先执行初始化。

  1. public class Demo {
  2. static int f;
  3. static{
  4. f=100;
  5. System.out.println("Demo:static init block invoked..and f="+f);
  6. }
  7. public static void main(String[] args) {
  8. System.out.println(Derived.e);
  9. }
  10. }

下面再看一下,使用Derived的静态变量会不会引起初始化过程。。

果然,这也属于主动使用的一种,所以类执行了初始化,并且这里看的更加清楚 ,含有main方法的类首先被初始化,因为它是第一个被使用的。但要注意,这里的静态就是不能是final的,否则初始化不会执行。

我们在main方法中添加一个语句,如下

  1. public static void main(String[] args) {
  2. System.out.println(Derived.e);
  3. new Derived().display();
  4. }

好, 这下看一下这个执行结果,能过这个,我主要 是想证明,类的初始化是只执行一次呢还是每次主动使用类时都执行。。

通过这个截图我们可以看到,我们添加了一条语句 ,但输出结果只添加了两行,调用Derived类的实例的display()方法只是执行了方法中的语句 ,并没有再执行初始化语句,因此我们可以总结出,一个类只有会被初始化一次,除非是它被重新装载。

Last but not least,让我们看一下静态嵌套类的初始化情况,它是要被主动使用的时候才初始化呢,还是它的外围类被主动使用的时候 它就会被初始化呢?

为了方便,我们就在Demo类中添加一个静态嵌套类Inner,如下

  1. public class Demo {
  2. static int f;
  3. static {
  4. f = 100;
  5. System.out.println("Demo:static init block invoked..and f=" + f);
  6. }
  7. static class Inner{
  8. static int g ;
  9. static{
  10. g=666;
  11. System.out.println("Inner:static init block invoked..and g="+g);
  12. }
  13. }
  14. public static void main(String[] args) {
  15. }
  16. }

看下main执行的结果:

我们可以看到,Inner类的静态初始化块并没有被执行,也就是说它并没有被初始化。好,现在在main函数中加上下面这句再看下执行结果:

  1. System.out.println(Inner.g);

看截图:

这下,很明显,Inner的初始化进行了,所以说,它与外围类没有必然的联系,外围类的初始化并不能导致它被初始化, 要初始化静态嵌套类必须主动使用它,而主动使用其外围类并不等同于主动使用Static Nested Class.

java类的初始化的更多相关文章

  1. 图示Java类的初始化顺序

    Java类的初始化顺序   在开发中,知道Java类的初始化顺序才能让我们更加清楚地掌握程序的执行流程.先把结论贴出来,Java里,从图里的1~6,分别按顺序执行.   以下为代码验证阶段,一共三个类 ...

  2. java类的初始化和构造函数

    本人小白一枚,看java类的初始化的时候好晕的说,我觉着书上尽管说的对.但总认为有些信息没说出来,没说清楚,看了好多文章博客的,如今有些感悟,来小写下总结,也算是为以后再次复习种个好果子. 先摘一下书 ...

  3. java类的初始化顺序

    在java中,当我们new一个对象时,对象中的成员,初始化块以及构造方法的加载是有一定的顺序的,看下面一副图: 一.单类(无基类)下的初始化顺序: public class Parent { stat ...

  4. java类的初始化和对象的创建顺序

    学习java编程思想--类的初始化p146 类的加载顺序* 1加载器启动找到 xxx.class文件,通过extends关键字寻找基类,先加载基类* 2类初始化先初始化static成员变量和stati ...

  5. [Java] 类的初始化步骤

    前言 类的初始化过程,可在多线程环境下进行的,为了简化,本文介绍的单线程情况下的类初始化步骤. 此外,继承情况下的顺序不是本文焦点,本文重点在于一个类的内部字段的初始化顺序.想了解继承情况下的初始化情 ...

  6. 分析java类的初始化契机

    分析java类的静态成员变量初始化先于非静态成员变量   依上图中当class字节码文件被jvm虚拟机加载到内存中依次经过 连接 验证:对字节码进行验证 准备:给静态变量分配内存并赋予变量类型各自的默 ...

  7. Java类的初始化与实例对象的初始化

    Java对象初始化详解 2013/04/10 · 开发 · 1 评论· java 分享到:43 与<YII框架>不得不说的故事—扩展篇 sass进阶篇 Spring事务管理 Android ...

  8. java类的初始化块/执行顺序,实例化对象数据赋值

    java里初始化一个类的对象,通过初始化快或者构造方法进行数据赋值.与其相关的执行代码有这么几种: 静态初始化块 初始化块 构造方法 静态初始化块 静态初始化块只在类加载时执行一次,同时静态初始化块只 ...

  9. Java类的初始化过程及清理

    一.类的数据成员初始化 Java中类的数据成员初试化可能有两种形式. 在定义类成员变量的地方直接提供初始化值(这是C++中不允许的) 在构造器中初试化.(Java中不存在类似C++中的初始化列表) 两 ...

随机推荐

  1. SaltStack Job管理

    Job基本管理 Jid: job id,格式为%Y%m%d%H%M%S%f 在master在下发指令消息时,会附带上产生的jid.minion在接收到指令开始执行时,会 在本地的cachedir(默认 ...

  2. AVAudioPlayer的锁屏播放控制和锁屏播放信息显示

    在设置这个锁屏之前,首先得设置应用支持后台音乐播放,TAGETS->Info->Required background modes->App plays audio or strea ...

  3. mac关机快捷键

    1.Ctrl + 关机:弹出关机提示 2.Ctrl + Option + 关机 : 正常关机快捷键 3.Command + Option + 关机 :进入休眠状态 4.Ctrl + Command + ...

  4. HDU 5868 Different Circle Permutation(burnside 引理)

    HDU 5868 Different Circle Permutation(burnside 引理) 题目链接http://acm.hdu.edu.cn/showproblem.php?pid=586 ...

  5. C++虚函数继承的bug

    闲来无事想测试一下:如果在派生类中重写基类的虚函数,那么允不允许改变虚函数的访问权限,结果颠覆了三观..... 基类Base,拥有public方法test(),test()为虚函数 派生类Derive ...

  6. 通过mvn archetype:generate创建Maven项目模板慢的问题

    通过mvn archetype:generate这种交互方式来创建Maven项目模板的时候,经常会长时间卡在Generating project in Interactive mode这一行提示(图1 ...

  7. Scala并发编程

    Scala的actor提供了一种基于事件的轻量级线程.只要使用scala.actors.Actor伴生对象的actor方法,就可以创建一个actor.它接受一个函数值/闭包做参数,一创建好就开始运行. ...

  8. Windows下NexusPHP搭建PT站过程

    搭建环境:Win7+XAMPP(5.5) 网站根目录: D:\IT\XAMPP5.5\htdocs\ 1 下载源码程序http://sourceforge.net/projects/nexusphp/ ...

  9. 在Ubuntu 14.04安装 Let’s Encrypt并配置ssl

    1.下载安装 Let's Encrypt客户端 cd /usr/local/sbin sudo wget https://dl.eff.org/certbot-auto 2.添加执行权限 sudo c ...

  10. Struts2第二天

    Struts2第二天 昨天: 1.Action的编写方式:实现Action接口.继承ActionSupport.自定义pojo作为action 2.action调用方法:默认的execute.meth ...