偶然的机会发现在一个好用的IDEA插件--Bytecode Editor,可以用来快速的修改Java的字节码。推荐大家试试!记得点左上角的重编译按钮哦。
Tag: bytecode
Class.cast() vs. cast operator
A co-worker told me Class.cast() is better than cast opertor as it’s safer to handle null reference. Really?
Let’s take a look at the following code snippet. As you can see, basically, there is no difference. Both test cases can pass.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@Test public void testCastOpertor() { Object obj = null; Integer a = (Integer)obj; assertNull(a); obj = new Integer(10); a = (Integer)obj; assertEquals(new Integer(10), a); obj = new Double(20.0); try { a = (Integer) obj; assertTrue(false); } catch (ClassCastException e) { assertTrue(true); } } @Test public void testCastFunction() { Object obj = null; Integer a = Integer.class.cast(obj); assertNull(a); obj = new Integer(10); a = Integer.class.cast(obj); assertEquals(new Integer(10), a); obj = new Double(20.0); try { a = Integer.class.cast(obj); assertTrue(false); } catch (ClassCastException e) { assertTrue(true); } } |
In the function of cast(), it does null and type check but still uses cast operator to cast object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Casts an object to the class or interface represented * by this {@code Class} object. * * @param obj the object to be cast * @return the object after casting, or null if obj is null * * @throws ClassCastException if the object is not * null and is not assignable to the type T. * * @since 1.5 */ public T cast(Object obj) { if (obj != null && !isInstance(obj)) throw new ClassCastException(cannotCastMsg(obj)); return (T) obj; } |
What’s the difference in byte code? See the source code and byte code (compiled by Java 7) of a simple test case.
1 2 3 4 5 6 |
@Test public void testComparison() { Object obj = new Integer(10); Integer b = (Integer)obj; Integer c = Integer.class.cast(obj); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// access flags 0x1 public testComparison()V @Lorg/junit/Test;() L0 LINENUMBER 16 L0 NEW java/lang/Integer DUP BIPUSH 10 INVOKESPECIAL java/lang/Integer.<init> (I)V ASTORE 1 L1 LINENUMBER 17 L1 ALOAD 1 CHECKCAST java/lang/Integer ASTORE 2 L2 LINENUMBER 18 L2 LDC Ljava/lang/Integer;.class ALOAD 1 INVOKEVIRTUAL java/lang/Class.cast (Ljava/lang/Object;)Ljava/lang/Object; CHECKCAST java/lang/Integer ASTORE 3 L3 LINENUMBER 19 L3 RETURN L4 LOCALVARIABLE this Lorg/yli/algo/TreeTest; L0 L4 0 LOCALVARIABLE obj Ljava/lang/Object; L1 L4 1 LOCALVARIABLE b Ljava/lang/Integer; L2 L4 2 LOCALVARIABLE c Ljava/lang/Integer; L3 L4 3 MAXSTACK = 3 MAXLOCALS = 4 |
Note: Internally, line index starts from 0.
For L1: Instruction of “CHECKCAST” will be invoked to do type casting. In JVM specification, you can find all details about it. Basically, it behaves the same as we expects. See more details here.
For L2: Function cast() will be invoke first and later “CHECKCAST” still needed. Why? I think Java compiler plays a trick. cast() is a generic function, which should return an object in T type. But in runtime, information of type will be erased. So compiler need to put an extra instruction here to do type casting, which should be the same for other generic functions. Actually, in cast(), “CHECKCAST” is not really invoked.
In that case, way to use case() has worse performance.
See a simple performance test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Test public void testPerformance() { Object obj = new Integer(10); long startTime = System.nanoTime(); for (int i = 0; i < 100000; ++i) { Integer b = (Integer) obj; } System.out.println( String.format("Elapsed time for cast operator: %fms", (System.nanoTime() - startTime) / 1000000.0)); startTime = System.nanoTime(); for (int i = 0; i < 100000; ++i) { Integer c = Integer.class.cast(obj); } System.out.println( String.format("Elapsed time for cast(): %fms", (System.nanoTime() - startTime) / 1000000.0)); } |
And the difference is BIG.
1 2 |
Elapsed time for cast operator: 1.639244ms Elapsed time for cast(): 6.964643ms |
Conclusion
I think in most cases, cast operator is safe and simple. But in some cases, when you only have Class object, such as some generic function/class, cast() is also a good choice.
Java技术学习之Class文件
Java Class文件是由Java Compiler编译源文件之后产生的。Class文件里保存的就是大名鼎鼎的ByteCode(字节码)。其实在JVM Specification有对它格式的详细描述,我也因此用Python写了一个解析器PyJavap。昨天这个小工具也到了第一个Milestone,支持1.5以前的规范。我心里还是很有成就感的,同时对ByteCode也有了更深了理解,在我的GitHub上可以找到。
现在对Java技术的兴趣越来越深,继续研究吧! 🙂