A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张百振 高级黑马   /  2014-5-15 23:24  /  1617 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  1. <font size="3">class ClassA{
  2. static { //静态代码块
  3. System.out.println("In ClassA Static");
  4. }
  5. public ClassA(){
  6. System.out.println("ClassA()");
  7. }
  8. }
  9. class ClassB{
  10. static { //静态代码块
  11. System.out.println("In ClassB Static");
  12. }
  13. public ClassB(){
  14. System.out.println("ClassB()");
  15. }
  16. }
  17. class ClassC extends ClassB{
  18. static{
  19. System.out.println("In ClassC Static");
  20. }
  21. public ClassC(){
  22. System.out.println("ClassC()");

  23. }
  24. }
  25. class MyClass {
  26. static ClassA ca = new ClassA();
  27. ClassC cc = new ClassC();
  28. static{
  29. System.out.println("In MyClass Static");
  30. }
  31. public MyClass(){
  32. System.out.println("MyClass()");
  33. }
  34. }
  35. public class TestMain{
  36. public static void main(String args[]){
  37. MyClass mc1 = new MyClass(); //
  38. MyClass mc2 = new MyClass();
  39. System.out.println(mc1.cc == mc2.cc);
  40. System.out.println(mc1.ca == mc2.ca);
  41. }
  42. } </font>
复制代码

写出这个程序运行的结果。





该题需要创建两个MyClass 对象。
一般而言,创建对象的过程如下:
1) 分配空间
2) 递归构造父类对象
3) 初始化本类属性
4) 调用本类构造方法

如果某个类是JVM 运行中第一次遇到,则会进行类加载的动作。类加载会初始化该类
的静态属性,并执行该类的静态初始化代码块。
1) 加载MyClass 类,并初始化其静态属性
2) 为MyClass 分配空间
3) 递归构造MyClass 的父类对象。
4) 初始化MyClass 属性
5) 调用MyClass 的构造方法。


6) 为MyClass 分配空间
7) 递归构造MyClass 的父类对象
8) 初始化MyClass 属性
9) 调用MyClass 的构造方法。
经过9 个步骤,两个对象创建完毕。
其中,在第1 步时,类加载时会初始化其静态属性,之后会执行静态初始化代码块,因
此对第1 步进行细分:
1.1 初始化ca 属性
1.2 执行MyClass 的静态初始化代码块。
在1.1 执行时,初始化ca 属性会创建ClassA 对象。由于这是第一次在程序中用到ClassA
对象,因此会执行对ClassA 对象的类加载。即:1.1 步可以细分为以下步骤:
1.1.1 加载ClassA 类
1.1.2 创建ClassA 对象
在初始化MyClass 属性时,需要创建ClassC 对象。而程序执行到第4 步时是第一次遇
到ClassC 类型的对象,因此会执行ClassC 的类加载。因此,对第4 步和第8 步进行细化:
第4 步:初始化MyClass 属性:
4.1 加载ClassC
4.2 为ClassC 分配空间
4.3 递归构造ClassC 的父类对象
4.4 初始化ClassC 属性
4.5 调用ClassC 的构造方法
第8 步,初始化MyClass 属性:
8.1 为ClassC 分配空间
8.2 递归构造ClassC 的父类对象
8.3 初始化ClassC 属性
8.4 调用ClassC 的构造方法
对于4.1 而言,为了创建ClassC 对象,必须获取ClassC 类的信息。而获得ClassC 类完
整信息的前提,是获得ClassB 类的信息。由于是第一次遇到ClassB 和ClassC,因此会先加
载ClassB,之后加载ClassC。细分之后,4.1 分为以下两步:
4.1.1 加载ClassB
4.1.2 加载ClassC
完整列出所有步骤如下:
1.1.1 加载ClassA 类    输出In ClassA Static
1.1.2 创建ClassA 对象  输出ClassA()
1.2 执行MyClass 的静态初始化代码块  输出In Static MyClass
2 分配MyClass 的空间  无输出
3 递归构造MyClass 的父类对象  无输出
4.1.1 加载ClassB  输出 In ClassB Static
4.1.2 加载ClassC  输出In ClassC Static
4.2 分配ClassC 的空间  无输出
4.3 构造ClassC 的父类对象(ClassB)  输出ClassB()
4.4 初始化ClassC 属性  无输出
4.5 调用ClassC 的构造方法  输出ClassC()
5 调用MyClass 的构造方法  输出MyClass()
6 为MyClass 分配空间  无输出
7 递归构造MyClass 的父类对象  无输出
8.1 为ClassC 分配空间  无输出
8.2 递归构造ClassC 的父类对象  ClassB()
8.3 初始化ClassC 的属性  无输出
8.4 调用ClassC 的构造方法  ClassC()
9 调用MyClass 的构造方法  MyClass()


以上是我的分析可是有一下几点不是太明白:
1.当一个类和他的父类中中同时出现静态代码块,的时候是怎么进行创建对象的?
2.静态的成员是不是所有的都只是加载一次?


我分析的输出结果是:


In ClassA Static
ClassA()
In Static MyClass
In ClassB Static
In ClassC Static
ClassB()
ClassC()
MyClass()
ClassB()
ClassC()
MyClass()


追加提问:
如果将ClassA添加一个父类ClassObject,再将ClassA中的静态代码块
static { //静态代码块
System.out.println("In ClassA Static");
} 改为

static { //静态代码块
Object o;
System.out.println("In ClassA Static");
}
流程是怎么走的呢?

类Object如下
Class Object {
     static {
System.out.println("static int Objict");
}
Object (){
Systen.out.println("Object  ()");
}
}


就是想加强一下对继承和代码块以及有继承关系的创建对象过程的了解
同时也希望对大家的知识有所巩固

3 个回复

正序浏览
我喜欢用向上转型,向下转型的思维来看这类问题。。。
回复 使用道具 举报
张盼 发表于 2014-5-15 23:46
当一个类和他的父类中中同时出现静态代码块,应该是先执行父类的静态代码块,因为静态代码块用于给类进行初 ...

那如果没有加载子类,JVM怎么知道这个子类有父类呢?是不?
回复 使用道具 举报
当一个类和他的父类中中同时出现静态代码块,应该是先执行父类的静态代码块,因为静态代码块用于给类进行初始化,父类加载进内存的时间早于子类,所以先执行父类的静态代码块。个人是这么认为的,不知道对不对
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马