要让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中,它的实际工作要略多一些,如果这个对象的类没有加载,就会加载相应的类。