要讓Java這個面向“對象”的世界正常運作,創建對象就是一項不可或缺的操作。
public class NewMain {
public static void main(String[] args) {
new Object();
}
}
用javap反編譯上面的代碼,我們可以得到下面的指令,這里省去了javac暗中創建的構造函數。
public class NewMain extends java.lang.Object{
...
public static void main(java.lang.String[]);
Code:
0: new #3; //class java/lang/Object
3: invokespecial #8; //Method java/lang/Object."<init>":()V
6: return
}
從這段代碼中,我們可以清晰的看出創建對象(new)和調用構造函數(invokespecial)兩個過程。關于這個問題,我在《
對象的生命》中曾經進行過討論。
既然javac將一個new的動作被解釋為兩條指令,那在JVM的層面上,我們當然就可以將它們分開。下面是一段沒什么實際用途的代碼,只是證明這個觀點可行性。
public class NewGenerator {
public static void main(String[] args) throws Exception {
String className = "New";
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);
Method m = Method.getMethod("void main (String[])");
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, m, null, null, cw);
mg.newInstance(Type.getType(Object.class));
Label label = mg.newLabel();
mg.ifNonNull(label);
mg.mark(label);
mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
mg.push("new object is not null");
mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println(java.lang.String)"));
mg.pop();
mg.returnValue();
mg.endMethod();
cw.visitEnd();
OutputStream os = null;
try {
os = new FileOutputStream(className + ".class");
os.write(cw.toByteArray());
} finally {
if (os != null) {
os.close();
}
}
}
}
這段代碼生成的類是能夠運行的,有興趣的可以自己試一下。這段代碼的作用是new出一個對象之后,如果這個對象非空的話,就會產生一個輸出:
new object is not null
當然,如果嘗試用這個對象做一些其它的操作,會有錯誤等待著我們,因為這個對象并不是一個完整的對象。在
JVM規范中有相關的解釋:
new指令并不能完整創建出一個新的對象,直到對未初始化的對象調用了實例初始化方法才會完成實例的創建。這段代碼也正好符合《
對象的生命》中的解釋,它只是負責做出申請內存。當然,在JVM中,它的實際工作要略多一些,如果這個對象的類沒有加載,就會加載相應的類。