han*_*szt 71 java reflection string-pool
我正在学习 Java 中的反射。偶然间,我发现了以下对我来说意想不到的行为。
下面写的两个测试都成功。
class NewInstanceUsingReflection {
@Test
void testClassNewInstance()
throws NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException
{
final var input = "A string";
final var theClass = input.getClass();
final var constructor = theClass.getConstructor();
final String newString = constructor.newInstance();
assertEquals("", newString);
}
@Test
void testClassNewInstanceWithVarOnly()
throws NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException
{
final var input = "A string";
final var theClass = input.getClass();
final var constructor = theClass.getConstructor();
final var newString = constructor.newInstance();
assertEquals("A string", newString);
}
}
Run Code Online (Sandbox Code Playgroud)
除了断言之外的唯一区别是变量类型在第一个测试中是显式的,并在第二个测试中newString
声明。var
我正在使用 java 17 和 junit5 测试框架。
为什么第一个测试中的值为newString
空字符串,而input
第二个测试中的值为字符串值?
它与字符串池有关系吗?
还是有其他事情发生?
rzw*_*oot 43
Java17,同样的问题。解释很清楚:bug。
反编译它,相关部分:
20: anewarray #2 // class java/lang/Object
23: invokevirtual #35 // Method java/lang/reflect/Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object;
26: checkcast #41 // class java/lang/String
29: astore 4
31: ldc #23 // String A string
33: ldc #23 // String A string
35: invokevirtual #43 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
Run Code Online (Sandbox Code Playgroud)
astore 4
是结果所在的位置,但无处可去:插槽 4 不再使用。相反,相同的字符串常量被加载两次,实际上很容易导致 ,"A string".equals("A string")
这当然是true
。
替换var
为 String,重新编译并重新运行javap
:
20: anewarray #2 // class java/lang/Object
23: invokevirtual #35 // Method java/lang/reflect/Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object;
26: checkcast #41 // class java/lang/String
29: astore 4
31: ldc #23 // String A string
33: aload 4
35: invokevirtual #43 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
Run Code Online (Sandbox Code Playgroud)
ldc
除了第二个是正确的之外,其他方面都相同aload 4
。
我很难弄清楚这里发生了什么。感觉更像是以var
某种方式导致ldc
重复(与错误地认为值保证相同的分析相反;javac 故意很少进行此类优化)。
我真的很难弄清楚这在2 个LTS 版本中是如何发生的。令人印象深刻的发现。
下一步是验证最新的 JDK (18),然后提交错误。我快速浏览了一下是否已被报道,但我不确定要使用什么搜索词。不过,我在搜索中没有找到任何报告。
注意:反编译痕迹是使用javap -c -v NewInstanceUsingReflection
.
编辑:刚刚尝试了 ecj ( Eclipse Compiler for Java(TM) v20210223-0522, 3.25.0, Copyright IBM Corp 2000, 2020. All rights reserved.
) - 那里没有发生错误。