【问题标题】:What does Java object assignment mean?Java 对象赋值是什么意思?
【发布时间】:2015-07-07 22:00:59
【问题描述】:

我有以下两个课程:

class Animal {
    public static void staticMethod(int i) {
        System.out.println("Animal : static -- " + i);
    }

    public void instanceMethod(int i) {
        System.out.println("Animal : instance -- " + i);
    }
}

class Cat extends Animal {
    public static void staticMethod(int i) {
        System.out.println("Cat : static -- " + i);
    }

    public void instanceMethod(int i) {
        System.out.println("Cat : instance -- " + i);
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        myCat.staticMethod(1);                       // Cat : static -- 1
        myCat.instanceMethod(2);                     // Cat : instance -- 2
        System.out.println("");

        Animal myAnimal = myCat;
        Animal.staticMethod(3);                      // Animal : static -- 3 
        myAnimal.staticMethod(4);                    // Animal : static -- 4 [ ? ]
        System.out.println("");

        myAnimal.instanceMethod(5);                  // Cat : instance -- 5
    }
} 

当我运行 Cat 时,我得到了以下结果:

Cat : static -- 1
Cat : instance -- 2

Animal : static -- 3
Animal : static -- 4

Cat : instance -- 5

我可以理解 1,2,3 和 5,但为什么 #4 不是:“Cat : static -- 4”? 我的理解是这样的:

myAnimal=myCat 表示“myAnimal”现在与“myCat”完全相同,因此在任何出现“myAnimal”的地方,您都可以将其替换为“myCat”并获得相同的结果,因为 myAnimal 中的所有内容都与所有内容相同在 myCat 中,因此“myAnimal.staticMethod(4)”应与“myCat.staticMethod(4)”相同,输出应为:“Cat : static -- 4”,类似于“myCat.staticMethod(1)”以上。

但好像不是这样,为什么呢?

【问题讨论】:

  • 这是 Eclipse 告诉我的:The static method staticMethod(int) from the type Animal should be accessed in a static way,因为它是一个静态方法,所以当你调用它的 obj 时,它只绑定到父对象 Animal
  • 静态在编译时链接。这就是阻止它们被覆盖的原因
  • 这是一个重复的问题,被问了很多次

标签: java object variable-assignment


【解决方案1】:

来自Oracle docs

8.4.8.2。隐藏(通过类方法)

如果一个类 C 声明或继承了一个静态方法 m,那么 m 被称为 隐藏任何方法 m',其中 m 的签名是子签名 (§8.4.2) m'的签名,在超类和 C 的超接口,否则 C 中的代码可以访问这些超接口。

示例 8.4.8.2-1。隐藏类方法的调用

隐藏的类(静态)方法可以通过使用 类型是实际包含的类的引用 方法的声明。在这方面,隐藏静态方法 不同于覆盖实例方法。例子:

class Super {
            static String greeting() { return "Goodnight"; }
            String name() { return "Richard"; }
        }
        class Sub extends Super {
            static String greeting() { return "Hello"; }
            String name() { return "Dick"; }
        }
        class Test {
            public static void main(String[] args) {
                Super s = new Sub();
                System.out.println(s.greeting() + ", " + s.name());
            }
        }

产生输出:

晚安,迪克

因为greeting的调用使用了s的类型,即Super, 在编译时找出调用哪个类方法,而 name 的调用使用 s 的类,即 Sub,来计算, 在运行时,调用哪个实例方法。

【讨论】:

    【解决方案2】:

    当您设置 myAnimal = myCat 时,指针 myAnimal 指向一个猫对象,但当您尝试通过 myAnimal 指针访问静态方法时,它会从声明 myAnimal 的类访问静态方法。

    【讨论】:

      【解决方案3】:

      Static 确切的意思是:调用是静态解析的(在您的情况下,它是根据 variable 的声明类型解析的,并且该变量是 compile -时间实体)。

      您期望的结果需要动态(多态地,基于引用实例的实际类型,并且该实例是运行时实体)解析调用。

      【讨论】:

        【解决方案4】:

        您将myAnimal 声明为Animal。因此,该类也调用了一个静态方法。

        您不应该从实例中调用静态方法(或访问静态字段)以防止这种混淆。

        【讨论】:

          【解决方案5】:

          原因是 Java 根据引用变量本身的类型解析静态方法,而不是像实例方法那样在运行时多态。

          稍微扩展一下,当您执行Animal myAnimal = myCat 时,您正在将一个 Cat 引用分配给一个 Animal 引用。这是可以接受的,因为Cat 也是Animal,所以Animal 可以做的任何事情,Cat 也可以做。

          此外,如果您通过myAnimal 引用调用实例(即非静态)方法,并且该方法在Cat 中被覆盖,则调用该方法的Cat 版本,因为这就是为什么该方法首先被覆盖。另一方面,静态方法永远不会被覆盖。这就是为什么它们是“静态的”,就像“非动态”一样。这意味着静态方法可以由编译器解析,而不必依赖运行时环境。

          【讨论】:

            猜你喜欢
            • 2017-08-13
            • 2011-06-12
            相关资源
            最近更新 更多