可以转储原始字节码,javap -v
但要以您可以编辑的格式获取类文件中的所有信息,我使用 ASM。您不使用它进行编辑的原因是;
- 它不允许你做很多你在 Java 中做不到的事情。
- 它很难使用。
- 如果你打破它,你不会得到太多反馈。
字节码只是 Java 代码的编译版本,这毫无价值,这就是为什么它们做同样的事情的原因。JVM 可以自由地以多种方式对其进行优化,如果您想了解 JVM 的工作方式或代码的运行方式,那么理解字节码并不是很有用。
感谢@Antimony 建议使用手动编写的字节码。虽然其中一些是有效的,但它们要么相当先进,要么晦涩难懂,要么具有实现相同目标的替代方法。
一些想法
抛出已检查的异常而不声明它们,
Thread.currentThread().stop(checkedException);
捕获检查的异常而不声明它们,
if (false) throw new CheckedException();
} catch(CheckedException ce) {
在 Java 中使用无效的标识符
有这么多有效但不是那么有用的字符可以使用我会使用其中一个
if( ⁀ ‿ ⁀ == ⁀ ⁔ ⁀ || ¢ + ¢== ₡)
和
for (char ch = 0; ch < Character.MAX_VALUE; ch++)
if (Character.isJavaIdentifierPart(ch) && !Character.isJavaIdentifierStart(ch))
System.out.printf("%04x <%s>%n", (int) ch, "" + ch);
由于标识符中的不可见字符导致文本向后打印,因此是有效代码。
http://vanillajava.blogspot.co.uk/2012/09/hidden-code.html
http://vanillajava.blogspot.co.uk/2012/08/uses-for-special-characters-in-java-code.html
在构造函数调用之前访问字段
我很确定这会导致VerifyError(或者在我尝试时确实如此)。相反,您可以使用
Object o = Unsafe.allocateInstance(clazz); // create without calling a constructor.
有条件地调用不同的构造函数
同样,我已经能够在对象创建和实例调用之间放置代码,但可能有一种方法可以让它工作。在Java中你会写
MyClass mc = condition ? new MyClass(a) : new MyClass(a, b);
对构造函数进行异常处理。
不确定您在这里的意思,因为您可以在构造函数中捕获或抛出异常。
多次分配最终字段或根本不分配
您可以改用反射并为最终字段提供默认值。即一个字段不可能没有价值。
有一个带有非strictfp 方法的strictfp 构造函数
这是一个有趣的,但不是我曾经需要的。
使用更宽松的类型检查
字节码确实允许不同的类型检查规则,但它们很容易与不太有用的 VerifyErrors 发生冲突。你可以做到这一点,但它很难得到正确的恕我直言。
从静态初始化程序抛出异常
您已经可以抛出未检查的异常,并且要抛出已检查的异常,您可以使用上面的技巧,但是我通常用 AssertionError 或类似的方法包装。
使用调用动态
您可以使用 MethodHandles,但它们在 Java 7 中相当笨重。我希望在 Java 8 中它们会更自然地使用。
使用子程序
有趣的是,我还没有尝试过,但我不确定它们比方法调用有什么优势。
使用异常控制流
没错,但这更有可能混淆 JIT 优化器,因此代码可能会变慢,因此可能不会给您带来您希望的优势。
使用旧的超级调用语义
你可以,但我不确定这有多大用处。
使用线程监视器
您可以使用
Unsafe.enterMonitor();
Unsafe.exitMonitor();
Unsafe.tryMonitorEnter();
字段的初始值。
我同意编译器并没有像我预期的那样使用这些,但是我再次不确定如果这样做会有什么不同。