Java更改变量名称会更改程序行为
我发现一个Java程序在重命名变量后行为不同的场景。 我知道这不是任何人都会使用的代码,但是如果有人知道发生了什么事情,那么有一个解释是很好的。 我在Eclipse Kepler上用java 1.6尝试了这一点。
package _test;
public class TestClass{
public static void main(String...args){
Object testClazz$1 = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(testClazz$1.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
这输出:
你好
线程“主”java.lang.NoSuchMethodError异常:_test.TestClass $ _doStuff()V在_test.TestClass.main(TestClass.java:13)
据我了解,编译器为testClazz $ 1对象创建一个TestClass $ 1.class文件,这会导致命名冲突。
但是在将对象重命名为testClass $ 1之后:
package _test;
public class TestClass{
public static void main(String...args){
Object testClass$1 = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(testClass$1.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
输出是:
_test.TestClass$1@2e6e1408
hello2
有什么想法发生在这里?
当类加载器遇到匿名的Object() {...}
类时,它将以名称TestClass$1
加载它。 这会与显式定义的class TestClass$1 {...}
产生冲突。
但是,类名冲突的处理过于不合情理。 这一点文件告诉我们
如果类c已经被链接,那么这个方法简单地返回。
这就是你的情况。 你只加载两个TestClass$1
类中的一个。
除了在编译器中重新编译和重新链接以外,“不同的变量名称”不负责其他任何内容。 在这一点上,类加载器可以自由选择两个TestClass$1
哪一个更好地使用它,并随处使用它。
如果你使用类似eclipse的东西(就像我),那么你的字节码将被缓存,直到对源文件进行新的touch
操作(并更新时间戳...)。 下面是我做的重现(在RedHat下运行openjdk 1.7,Eclipse Kepler):
把它放在一个源文件TestClass.java
:
package javaclasses.classes;
public class TestClass{
public static void main(String...args){
Object o = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(o.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
ctrl + F11输出:
javaclasses.classes.TestClass$1@42293b53
hello2
在控制台中打开并touch TestClass.java
回到eclipse中,现在输出ctrl + F11:
hello
Exception in thread "main" java.lang.NoSuchMethodError: javaclasses.classes.TestClass$1.doStuff()V
at javaclasses.classes.TestClass.main(TestClass.java:13)
结论:所有可以说明的是,默认的ClassLoader对于手动解析具有相同完全限定名的类是不可靠的。 更改变量名称并不重要,源文件上的更新时间戳记会执行此操作。
通过在封闭类的名称后附加$
符号和增加的数字来自动命名匿名类。
在你的第一个例子中,Anoymous类将被命名为TestClass$1
,它没有doStuff()
方法,你只能覆盖toString()
,这就是为什么你会得到NoSuchMethodError
错误。
在你的第二个例子中,你已经有了一个名为TestClass$1
的局部变量,所以编译器选择的自动生成的名称将是一个不同的名称,最有可能是TestClass$2
。 既然你实例化了TestClass$1
,它不是一个匿名类,而是一个你明确定义的类,它将被实例化,它具有一个正确打印"hello2"
并且不重写Object.toString()
的doStuff()
方法,由它的toString()
方法返回的值将按照java.lang.Ojbect
(这是类名附加@
符号,后跟默认哈希码,十六进制格式)中指定的值打印默认值。
结论:虽然这是一个有趣的例子,但不应该在类名和标识符名称中使用$
符号。
我修改了代码以从类名称中删除“$”,并将testClass $ 1重命名为t,并稍微更改了println,如下所示:
public class TestClass{
public static void main(String...args){
Object t = new Object (){
public String toString() {
return "t.toString()";
}
};
TestClass1 tc1 = new TestClass1();
System.out.println(t.toString());
tc1.doStuff();
}
}
class TestClass1{
public void doStuff(){
System.out.println("TestClass1.doStuff()");
}
}
输出现在是:
t.toString()
TestClass1.doStuff()
这是你期望的吗?
链接地址: http://www.djcxy.com/p/21713.html上一篇: Java changing variable name changes program behaviour
下一篇: could not find implicit value for evidence parameter of type scalaz.Applicative