设为首页
加入收藏
站内地图
旧版入口

深入理解Java初始化的含义

作者:佚名 出处:bea 时间:08-25 点击:

内容载入中...

下面我们再来看看实例初始化方法""

  ""用于对象创建时对对象进行初始化,当在HEAP中创建对象时,一旦在HEAP分配了空间。最先就会调用""方法。这个方法包括实例变量的赋值(声明不在其中)和初始化块,以及构造方法调用。如果有多个重载的构造方法,每个构造方法都会有一个对应的""方法。构造方法隐式或显示调用父类的构造方法前,总是先执行实例变量初始化和初始化块.同样,实例变量和初始化块的顺序也是按源文件的原文顺序执行,构造方法中的代码在最后执行:

package debug;
public class Test {
 int x = 0;
 String s = "123";
 {
  String s1 = "456";
  //if(1==1)
  //throw new RuntimeException();
 }
 public Test(){
  String ss = "789";
 }
 public static void main(String[] args) {
  new Test();
 }
}
javap -c debug.Test的结果:
 Compiled from "Test.java"
 public class debug.Test extends java.lang.Object{
  int x;
  java.lang.String s;
  public debug.Test();
  Code:
  0: aload_0
  1: invokespecial #1; //Method java/lang/Object."":()V
  4: aload_0
  5: iconst_0
  6: putfield #2; //Field x:I
  9: aload_0
  10: ldc #3; //String 123
  12: putfield #4; //Field s:Ljava/lang/String;
  15: ldc #5; //String 456
  17: astore_1
  18: ldc #6; //String 789
  20: astore_1
  21: return
  public static void main(java.lang.String[]);
  Code:
  0: new #7; //class debug/Test
  3: dup
  4: invokespecial #8; //Method "":()V
  7: pop
  8: return
 }


  如果在同一个类中,一个构造方法调用了另一个构造方法,那么对应的""方法就会调用另一个"",但是实例变量和初始化块会被忽略,否则它们就会被多次执行。

package debug;
public class Test {
 String s1 = rt("s1");
 String s2 = "s2";
 public Test(){
  s1 = "s1";
 }
public Test(String s){
 this();
 if(1==1) throw new Runtime();
}
String rt(String s){
 return s;
}
public static void main(String[] args) {
 new Test("");
}
}

  反编译的结果:

Compiled from "Test.java"
public class debug.Test extends java.lang.Object{
 java.lang.String s1;
 java.lang.String s2;
 public debug.Test();
 Code:
 0: aload_0
 1: invokespecial #1; //Method java/lang/Object."":()V
 4: aload_0
 5: aload_0
 6: ldc #2; //String s1
 8: invokevirtual #3; //Method rt:(Ljava/lang/String;)Ljava/lang/String;
 11: putfield #4; //Field s1:Ljava/lang/String;
 14: aload_0
 15: ldc #5; //String s2
 17: putfield #6; //Field s2:Ljava/lang/String;
 20: aload_0
 21: ldc #2; //String s1
 23: putfield #4; //Field s1:Ljava/lang/String;
 26: return
 public debug.Test(java.lang.String);
 Code:
  0: aload_0
  1: invokespecial #7; //Method "":()V
  4: new #8; //class java/lang/RuntimeException
  7: dup
  8: invokespecial #9; //Method java/lang/RuntimeException."":()V
  11: athrow
  java.lang.String rt(java.lang.String);
 Code:
  0: aload_1
  1: areturn
  public static void main(java.lang.String[]);
 Code:
  0: new #10; //class debug/Test
  3: dup
  4: ldc #11; //String
  6: invokespecial #12; //Method "":(Ljava/lang/String;)V
  9: pop
  10: return
 }

  我们看到,由于Test(String s)调用了Test();所以"":(Ljava/lang/String;)V不再对实例变量和初始化块进次初始化:

public debug.Test(java.lang.String);
Code:
 0: aload_0
 1: invokespecial #7; //Method "":()V
 4: new #8; //class java/lang/RuntimeException
 7: dup
 8: invokespecial #9; //Method java/lang/RuntimeException."":()V
 11: athrow

  而如果两个构造方法是相互独立的,则每个构造方法调用前都会执行实例变量和初始化块的调用:

package debug;
public class Test {
 String s1 = rt("s1");
 String s2 = "s2";
 {
  String s3 = "s3";
 }
public Test() {
 s1 = "s1";
}
public Test(String s) {
 if (1 == 1)
  throw new RuntimeException();
}
String rt(String s) {
 return s;
}
public static void main(String[] args) {
 new Test("");
}
}

  反编译的结果:

Compiled from "Test.java"
 public class debug.Test extends java.lang.Object{
  java.lang.String s1;
  java.lang.String s2;
  public debug.Test();
  Code:
  0: aload_0
  1: invokespecial #1; //Method java/lang/Object."":()V
  4: aload_0
  5: aload_0
  6: ldc #2; //String s1
  8: invokevirtual #3; //Method rt:(Ljava/lang/String;)Ljava/lang/String;
  11: putfield #4; //Field s1:Ljava/lang/String;
  14: aload_0
  15: ldc #5; //String s2
  17: putfield #6; //Field s2:Ljava/lang/String;
  20: ldc #7; //String s3
  22: astore_1
  23: aload_0
  24: ldc #2; //String s1
  26: putfield #4; //Field s1:Ljava/lang/String;
  29: return
  public debug.Test(java.lang.String);
  Code:
  0: aload_0
  1: invokespecial #1; //Method java/lang/Object."":()V
  4: aload_0
  5: aload_0
  6: ldc #2; //String s1
  8: invokevirtual #3; //Method rt:(Ljava/lang/String;)Ljava/lang/String;
  11: putfield #4; //Field s1:Ljava/lang/String;
  14: aload_0
  15: ldc #5; //String s2
  17: putfield #6; //Field s2:Ljava/lang/String;
  20: ldc #7; //String s3
  22: astore_2
  23: new #8; //class java/lang/RuntimeException
  26: dup
  27: invokespecial #9; //Method java/lang/RuntimeException."":()V
  30: athrow
  java.lang.String rt(java.lang.String);
  Code:
  0: aload_1
  1: areturn
  public static void main(java.lang.String[]);
  Code:
  0: new #10; //class debug/Test
  3: dup
  4: ldc #11; //String
  6: invokespecial #12; //Method "":(Ljava/lang/String;)V
  9: pop
  10: return
 }

  明白了上面这些知识,我们来做一个小测试吧:

public class Test2 extends Test1
{
 System.out.print("1");
}
Test2(){
 System.out.print("2");
}
static{
 System.out.print("3");
}
{
 System.out.print("4");
}
public static void main(String[] args) {
 new Test2();
}
}
class Test1 {
 Test1(){
 System.out.print("5");
}
static{
 System.out.print("6");
}
}

  试试看能清楚打印的顺序吗?如果没有new Test2()将打印什么?

1 2 [3]
收藏本文:
】【打印页面】【推荐给朋友】【关闭窗口

站长学院

推荐信息