5

我在这里读到,在 Java 中,两个具有相同名称但不同类型的变量可以在同一范围内共存。我的意思是这个

class test
{
    private int x;
    private double x;
}

但是所有的 java IDE 都不允许这样的代码。我想知道这样的代码在语法上是否真的正确,或者只是IDE不允许这样的代码来防止歧义。

不管怎样,这里是网站的摘录

“如果幸运的话,你也许可以重新编译 Jad 的输出。但是,Java VM 对变量命名的规则比 Java 语言本身更宽松。例如,一个有效的类文件可以有多个名为 'a ',只要它们的类型不同。如果你反编译这样的一个类,你得到的源代码将是无效的。

JAD 通常会重命名有问题的字段,并制作一个可重新编译的文件……唯一的问题是重新编译的文件与原始类不兼容。”

4

4 回答 4

5

正如其他人所说,在 Java 中是非法的,但在字节码中是合法的。

javac 断言

assert是一个 Java 示例,它在 Oracle JDK 1.8.0_45 中生成多个名称相同但类型不同的字段。例如:

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

这也是为什么使用带有美元符号的字段名称是一个非常糟糕的主意:我什么时候应该在变量名中使用美元符号 ($)?

茉莉

当然,我们也可以用 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的类型。

于 2015-07-11T09:20:40.403 回答
3

根据语言规范(JLS 8.3):

类声明的主体声明两个具有相同名称的字段是编译时错误。

您引用的语句是关于类文件(即编译文件,而不是源代码)。

于 2012-12-01T06:29:36.270 回答
3

您不能让具有相同名称(但类型不同)的变量存在于同一范围内。考虑是否有可能,那么 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可以在类文件中重命名此类重复的命名变量,以便您将获得的源代码实际上在语法上是正确的

于 2012-12-04T05:27:36.183 回答
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;
于 2012-12-01T09:25:18.140 回答