关于构造器的一些思考
Java编译器会给没有构造器的Class添加一个无参的构造器,而JVM规范并没有要求Class必须有一个构造器。
类似的还有构造器里调用其他/父类构造器必须写在第一行,不得不说这个要求有些愚蠢,他好像只是为了告诉你对象的父类字段应该优先初始化,而这个要求又并不是十分严格。
比如我要在调用父类构造器之前做一些事情,那我就必须写成这样:
class A{
int val;
A(int i) {
val = i;
}
}
class B extends A{
B(int i) {
super(fun(i));
}
static int fun(int i) {
System.out.println("hehe");
return i;
}
}
回到正题上来,一个没有构造器的Class 字节码是可以被JVM接受的,我们用ASM(jdk自带)来创建一个简单的类,就是下面这个:
public class Hehe {
public String hehe() {
return "hehe";
}
}
代码:
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import java.io.IOException;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
public class Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, IOException, InvocationTargetException, NoSuchMethodException {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "Hehe", null, "java/lang/Object", null);
cw.visitSource("Hehe.java", null);
mv = cw.visitMethod(ACC_PUBLIC, "hehe", "()Ljava/lang/String;", null, null);
mv.visitCode();
mv.visitLdcInsn("hehe");
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
cw.visitEnd();
byte[] bytes = cw.toByteArray();
}
}
这样就完成了。
因为ClassLoader
的defineClass
是protected
的,所以只能自己创建一个加载器来加载他。
static class Loader extends ClassLoader {
Class<?> define(String name, byte[] b, int off, int length) {
return defineClass(name, b, off, length);
}
}
加载之后通过Unsafe.allocateInstance
来实例化,(因为它并没有构造器)。
然后通过反射调用hehe
方法,完成了。
接下来创建一个构造器第一行不是调用父类构造器的类,就直接贴一起了。现在有构造器就不用Unsafe来初始化,而且类加载器继承了当前的类加载器,所以子类实例可以直接赋值给Test变量。
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
public class Test {
public Test() {
System.out.println("Test");
}
public static void main(String[] args) {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(52, ACC_PUBLIC | ACC_SUPER, "Son1", null, "Test", null);
cw.visitSource("Son1.java", null);
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("Son");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "Test", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
cw.visitEnd();
byte[] bytes = cw.toByteArray();
ClassLoader loader = new ClassLoader(Thread.currentThread().getContextClassLoader()) {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return name.equals("Son1") ? super.defineClass(name, bytes, 0, bytes.length) : super.loadClass(name);
}
};
Class<? extends Test> son = null;
try {
son = (Class<? extends Test>) loader.loadClass("Son1");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
Test test = son.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
子类的构造器打印Son1,父类构造器打印Test。
输出:Son (手动换行)Test。
网友评论