有关Java重载和动态绑定的问题

| 在下面的代码中,第一和第二个打印语句如何打印出SubObj? top和sub指向同一个Sub类吗?
class Top {
    public String f(Object o) {return \"Top\";}
}

class Sub extends Top {
    public String f(String s) {return \"Sub\";}
    public String f(Object o) {return \"SubObj\";}
}

public class Test {
    public static void main(String[] args) {  
        Sub sub = new Sub();
        Top top = sub;
        String str = \"Something\";
        Object obj = str;


        System.out.println(top.f(obj));
        System.out.println(top.f(str));
        System.out.println(sub.f(obj));
        System.out.println(sub.f(str));
    }
}
上面的代码返回下面的结果。
SubObj
SubObj
SubObj
Sub
    
已邀请:
既然您已经了解了案例1、3和4,那么我们来解决案例2。 (请注意-我绝不是JVM或编译器内部工作的专家,但这是我的理解。如果阅读此书的人是JVM专家,请随时编辑此答案,以查找可能存在的任何差异) 子类中具有相同名称但签名不同的方法称为方法重载。方法重载使用静态绑定,这基本上意味着在编译时将强制“选择”(即绑定)适当的方法。编译器不了解对象的运行时类型(也称为实际类型)。所以当你写:
                         // Reference Type  // Actual Type
    Sub sub = new Sub(); // Sub                Sub
    Top top = sub;       // Top                Sub
编译器仅“知道” top的类型为Top(又称引用类型)。因此,当您稍后编写时:
    System.out.println(top.f(str)); // Prints \"subobj\"
编译器“看到”调用\'top.f \'指的是Top类的f方法。它“知道” str是扩展对象的String类型。因此,由于1)调用\'top.f \'是指Top类的sf方法,2)Top类中没有使用String参数的f方法,以及3)由于str是Object的子类,因此顶级类的f方法是编译时唯一有效的选择。因此,编译器将str隐式转换为其父类型Object,因此可以将其传递给Top的f方法。 (这与动态绑定相反,在动态绑定中,上述代码行的类型解析将推迟到运行时,然后由JVM而非编译器解决。) 然后在运行时,在上面的代码行中,JVM将top转换为它的实际类型sub。但是,编译器已将参数str转换为Object类型。因此,现在JVM必须在类sub中调用f方法,该方法采用Object类型的参数。 因此,上面的代码行打印\“ subobj \”而不是\“ sub \”。 对于另一个非常相似的示例,请参见:Java动态绑定和方法覆盖 更新:在JVM的内部工作中找到了这篇详细的文章: http://www.artima.com/underthehood/invocationP.html 我对您的代码进行了注释,以使事情更加清楚:
class Top {
    public String f(Object o) {return \"Top\";}
}

class Sub extends Top {
    public String f(String s) {return \"Sub\";} // Overloading = No dynamic binding
    public String f(Object o) {return \"SubObj\";} // Overriding = Dynamic binding
}

public class Test {
    public static void main(String[] args) {  

                                  // Reference Type     Actual Type
        Sub sub = new Sub();      // Sub                Sub
        Top top = sub;            // Top                Sub
        String str = \"Something\"; // String             String
        Object obj = str;         // Object             String

                                        // At Compile-Time:      At Run-Time:
        // Dynamic Binding
        System.out.println(top.f(obj)); // Top.f (Object)   -->  Sub.f (Object)

        // Dynamic Binding
        System.out.println(top.f(str)); // Top.f (Object)   -->  Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(obj)); // Sub.f (Object)        Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(str)); // Sub.f (String)        Sub.f (String)
    }
}
    
这是因为Java中的所有方法调用都是虚拟的(默认情况下)。 也就是说,解析从实际对象(而不是表达式的类型)开始,并“继承”继承链(针对实际对象类型),直到找到第一个匹配方法为止。非虚拟方法将从表达式的类型开始。 (将方法标记为“ 5”会使其成为非虚拟方法。) 但是,确切的方法签名是在编译时确定的(Java不支持多调度,单调度仅在运行时根据接收者对象的不同而有所不同),这解释了为什么
Sub.f(String)
导致\“ Sub \”,例如,即使对子类型Top调用
Top.f(String)
\“ binds \”到匹配
Top.f(Object)
的方法。 (这是在编译时确定的最佳合格签名)。虚拟调度本身是相同的。 快乐的编码。     
这与对象的外观类型有关。 Java在编译时根据您声明对象为的类型而不是实例化的特定类型进行类型检查。 您有一个类型为Top的方法f(Object)。所以当你说:
 System.out.println(top.f(obj));
Java编译器只关心对象top是Top类型,并且唯一可用的方法将Object作为参数。然后在运行时,它将调用实际实例化对象的f(Object)方法。 下一个调用的解释方式相同。 接下来的两个调用将按照您的预期进行解释。     
是的,他们俩都指向ѭ10级。 问题是ѭ11只知道
f(Object o)
并且只能调用该签名。 但是
sub
既知道两个签名,也必须按参数类型进行选择。     
在继承中,基类对象可以引用派生类的实例。
Top top = sub;
就是这样运作。 对于
System.out.println(top.f(obj));
top
对象尝试使用
Sub
类的
f()
方法。现在在
Sub
类中有两个
f()
方法,对传递的参数进行类型检查。由于类型是
Object
,因此会调用
Sub
类的第二个
f()
方法。 对于
System.out.println(top.f(str));
: 您可以将其解释为与(1)相同,即类型为
String
,以便调用第一个
f()
函数。 对于
System.out.println(sub.f(obj));
: 这很简单,因为您正在调用
Sub
类本身的方法。现在,由于
Sub
类中有两个重载方法,因此在这里还要对传递的参数进行类型检查。由于传递的参数的类型为
Object
,因此将调用第二个
f()
方法。 对于
System.out.println(sub.f(str));
: 与3.相似,这里传递的类型是
String
,因此调用
Sub
类的第一个
f()
函数。 希望这可以帮助。     
Sub sub = new Sub();
Top top = sub;
您创建了sub的实例,然后将其强制转换为顶部,这使得它仅了解top中存在的方法。 最上面存在的方法是公共
String f(Object o) {return \"Top\";}
现在该方法也被sub重载,因此当您创建sub实例并将其向上转换到顶部时,它将被调用。 另一种表达方式是 sub类型是外观类型,而top类型是实际类型,因为您将sub指定为top。 如果重载实际类型,您将以明显的类型调用方法,但是您将无法调用实际类型中不存在的任何方法     

要回复问题请先登录注册