我已阅读here,在Java中,两个具有相同名称但不同类型的变量可能在同一范围内共存。我的意思是这个
class test
{
private int x;
private double x;
}
但是所有java IDE都不允许这样的代码。我想知道这样的代码是否真的在语法上是正确的,或者只是IDE不允许这样的代码来防止歧义。
无论如何,这里是website
的摘录“如果你很幸运,你可以重新编译来自Jad的输出。但是,Java VM对变量命名的规则比Java语言本身更宽松。例如,有效的类文件可以有几个名为'a'的变量,只要它们有不同的类型。如果你反编译这样的类,你获得的源代码将无效。
JAD通常会重命名违规字段,并制作可重新编译的文件......唯一的问题是重新编译的文件与原始类不兼容。“
答案 0 :(得分:5)
正如其他人所说,在Java中是非法的,但在字节码中是合法的。
javac断言
assert
是一个Java示例,它在Oracle JDK 1.8.0_45中生成多个具有相同名称但不同类型的字段。 E.g:
public class Assert {
// We can't use a primitive like int here or it would get inlined.
static final int[] $assertionsDisabled = new int[0];
public static void main(String[] args) {
System.out.println($assertionsDisabled.length);
// currentTimeMillis so it won't get optimized away.
assert System.currentTimeMillis() == 0L;
}
}
assert
的存在会生成bool $assertionsDisable
合成字段以缓存方法调用,有关详细信息,请参阅:https://stackoverflow.com/a/29439538/895245。
然后:
javac Assert.java
javap -c -constants -private -verbose Assert.class
包含以下行:
#3 = Fieldref #9.#28 // Assert.$assertionsDisabled:[I
#5 = Fieldref #9.#31 // Assert.$assertionsDisabled:Z
#12 = Utf8 $assertionsDisabled
#28 = NameAndType #12:#13 // $assertionsDisabled:[I
#31 = NameAndType #12:#14 // $assertionsDisabled:Z
public static void main(java.lang.String[]);
3: getstatic #3 // Field $assertionsDisabled:[I
10: getstatic #5 // Field $assertionsDisabled:Z
注意常量表甚至重用#12
作为变量名。
如果我们已经声明了另一个布尔值,那么它将无法编译:
static final boolean $assertionsDisabled = false;
有错误:
the symbol $assertionsDisabled conflicts with a compile synthesized symbol
这也是使用带有美元符号的字段名称的一个非常糟糕的主意:When should I use the dollar symbol ($) in a variable name?
<强>茉莉强>
当然,我们也可以和Jasmin一起试试:
.class public FieldOverload
.super java/lang/Object
.field static f I
.field static f F
.method public static main([Ljava/lang/String;)V
.limit stack 2
ldc 1
putstatic FieldOverload/f I
ldc 1.5
putstatic FieldOverload/f F
getstatic java/lang/System/out Ljava/io/PrintStream;
getstatic FieldOverload/f I
invokevirtual java/io/PrintStream/println(I)V
getstatic java/lang/System/out Ljava/io/PrintStream;
getstatic FieldOverload/f F
invokevirtual java/io/PrintStream/println(F)V
return
.end method
其中包含两个静态字段,一个int
(I
)和一个float
(F
),并且输出:
1
1.5
如果有效,因为:
getstatic
指向常量表Fieldref
结构
Fieldref
指向NameAndType
NameAndType
指向该类型,显然是因此,为了区分它们,Jasmin只使用两种不同类型的Fieldref
。
答案 1 :(得分:3)
答案 2 :(得分:1)
当然,您不能在同一个类中使用int x和long x字段,就像您不能拥有两个具有相同名称和参数列表的方法一样。但这是在源级别。 JVM和字节码有不同的规则。考虑一下:
包裹测试;
public class Test {
static int x;
@Override
protected Test clone() throws CloneNotSupportedException {
return this;
}
public static void main(String[] args) {
int y = x;
}
}
如果您使用字节码大纲工具(我已经使用了Andrey Loskutov的Eclipse插件),您将在此行中看到int Test.main()
GETSTATIC test/Test.x : I
这是JVM从字段x加载值的方式,其全名是“test / Test.x:I”。这会向您提示字段值是否存在于字段全名中。
javac不是创建类的唯一方法,这已经不是什么秘密了,有工具/库可以直接创建字节码,并且它们可以自由地创建具有相同名称但不同类型的字段的类。 JNI也是如此。
很难展示我想要证明的实例。但让我们考虑方法,类似的问题。字节码分析表明,在Test类中有两种方法具有相同的名称和参数,这是JLS不允许的:
protected clone()Ltest/Test; throws java/lang/CloneNotSupportedException
protected volatile bridge clone()Ljava/lang/Object; throws java/lang/CloneNotSupportedException
javac添加了“bridge”方法,因为Test.clone()返回Test,这意味着它不会覆盖返回Object的Object.clone(),这是因为JVM认为这两种方法不同
1 clone()Ltest/Test;
2 clone()Ljava/lang/Object;
答案 3 :(得分:1)
您不能在同一范围内存在具有相同名称(但不同类型)的变量。 考虑一下,如果有可能,那么java编译器将如何确定你的意思。
请考虑此代码段
class test
{
private int x;
private double x;
test() //constructor
{
System.out.println(x); //Error cannot determine which x you meant
}
}
java编译器无法理解您实际引用的是哪个x。所以这样的代码在语法上并不正确,也不可编译。
但是存在诸如ClassEditor之类的工具,它们可以在创建后修改生成的类文件。在那里可以将两个变量的名称更改为相同。
但是这样的类不一定是java jvm可以运行的。
您引用的软件即JAD
可以在类文件中重命名此类重复的命名变量,以便您获得的源代码实际上在语法上是正确的