无限递归的构造器和javap使用指南
public class ConstructorRecursion {
ConstructorRecursion rc;
{
rc = newConstructorRecursion();
}
public ConstructorRecursion(){
System.out.println("noparameter constructor");
}
public static void main(String[] args){
ConstructorRecursion rc = new ConstructorRecursion();
}
}
Exceptionin thread "main" java.lang.StackOverflowError
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
at ConstructorRecursion.<init>(ConstructorRecursion.java:5)
表面上看,上面程序没有任何问题,ConstructorRecursion类的构造器中没有任何代码,只是单纯一句输出。但不要忘记了,不管是定义实例变量时指定的初始值,还是在非静态初始化块中执行的初始化操作,最终都将提取到构造器中执行。因为上面代码递归调用了ConstructorRecursion类的狗仔去哦,所以抛出了StackOverflowError错误。
这个程序给出的教训是:无论如何不要导致构造器产生递归调用。也就是说,应该:
尽量不要在定义实例变量时指定实例变量的值为当前类的实例;
尽量不要初始化块中创建当前类的实例;
尽量不要在构造器内调用本构造器创建Java对象;
C:Documents andSettingsmz50947workspaceTestProjectsrc>javap ConstructorRe
ursion
Compiled from"ConstructorRecursion.java"
public classConstructorRecursion extends java.lang.Object{
ConstructorRecursion rc;
public ConstructorRecursion();
public static voidmain(java.lang.String[]);
}
使用JAVAP可以查看java编译器生成的字节码,通过比较字节码和源代码,可以发现很多的问题。
C:Documents andSettingsmz50947workspaceTestProjectsrc>javac -g Constructor
Recursion.java
08/08/2013 02:50 PM 645 ConstructorRecursion.class
08/08/2013 02:34 PM 304 ConstructorRecursion.java
不带参数的javap将打印类的public信息,包括成员和方法。
C:Documents andSettingsmz50947workspaceTestProjectsrc>javap -c Constructor
Recursion
Compiled from"ConstructorRecursion.java"
public classConstructorRecursion extends java.lang.Object{
ConstructorRecursionrc;
publicConstructorRecursion();
Code:
0: aload_0
1: invokespecial #1; //Methodjava/lang/Object."<init>":()V
4: aload_0
5: new #2; //classConstructorRecursion
8: dup
9: invokespecial #3; //Method"<init>":()V
12: putfield #4; //Fieldrc:LConstructorRecursion;
15: getstatic #5; //Fieldjava/lang/System.out:Ljava/io/PrintStream;
18: ldc #6; //String no parameterconstructor
20: invokevirtual #7; //Methodjava/io/PrintStream.println:(Ljava/lang/Str
ing;)V
23: return
public static voidmain(java.lang.String[]);
Code:
0: new #2; //classConstructorRecursion
3: dup
4: invokespecial #3; //Method "<init>":()V
7: astore_1
8: return
}
C:Documents andSettingsmz50947workspaceTestProjectsrc>javap -l Constructor
Recursion
Compiled from"ConstructorRecursion.java"
public classConstructorRecursion extends java.lang.Object{
ConstructorRecursionrc;
publicConstructorRecursion();
LineNumberTable:
line 7: 0
line 5: 4
line 8: 15
line 9: 23
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 this LConstructorRecursion;
public static voidmain(java.lang.String[]);
LineNumberTable:
line 11: 0
line 12: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
8 1 1 rc LConstructorRecursion;
}
C:Documents andSettingsmz50947workspaceTestProjectsrc>javap -c Constructor
Recursion
Compiled from"ConstructorRecursion.java"
public classConstructorRecursion extends java.lang.Object{
ConstructorRecursionrc;
public ConstructorRecursion();
Code:
0: aload_0
1: invokespecial #1; //Methodjava/lang/Object."<init>":()V
4: aload_0
5: new #2; //classConstructorRecursion
8: dup
9: invokespecial #3; //Method"<init>":()V
12: putfield #4; //Fieldrc:LConstructorRecursion;
15: getstatic #5; //Fieldjava/lang/System.out:Ljava/io/PrintStream;
18: ldc #6; //String no parameterconstructor
20: invokevirtual #7; //Methodjava/io/PrintStream.println:(Ljava/lang/Str
ing;)V
23: return
public static voidmain(java.lang.String[]);
Code:
0: new #2; //classConstructorRecursion
3: dup
4: invokespecial #3; //Method"<init>":()V
7: astore_1
8: return
}
The javap command iscalled the Java “disassembler”because it takes apart class files andtells you what’s inside them. You won’t use this command often, but using it tofind out how a particular Java statement works is fun, sometimes. You can alsouse it to find out what methods are available for a class if you don’t have thesource code that was used to create the class.
Here is the general format:
javapfilename [options]
The following is typical of the information you getwhen you run the javap command:
C:javasamples>javapHelloApp
Compiledfrom "HelloApp.java"
publicclass HelloApp extends java.lang.Object{
public HelloApp();
public static voidmain(java.lang.String[]);
}
As you can see, the javap command indicates that the HelloApp class was compiled from the HelloApp.java file and that it consists of a HelloApppublic class and a mainpublic method.
You may want to use two options with the javap command. If you use the -c option, the javap command displays the actual Java bytecodes createdby the compiler for the class. (Java bytecode is the executable programcompiled from your Java source file.)
And if you use the -verbose option, the bytecodes — plus a ton of otherfascinating information about the innards of the class — are displayed. Here’sthe -c output for aclass named HelloApp:
C:javasamples>javapHelloApp -c
Compiledfrom "HelloApp.java"
publicclass HelloApp extends java.lang.Object{
publicHelloApp();
Code:
0: aload_0
1: invokespecial #1; //Method
java/lang/Object."<init>":()V
4: return
publicstatic void main(java.lang.String[]);
Code:
0: getstatic #2; //Field
java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Hello, World!
5: invokevirtual #4; //Method
java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}