虚函数如何在 C# 和 Java 中工作?
它是使用类似于 C++ 的相同 vtable 和 vpointer 概念还是完全不同的东西?
编码面试官喜欢这个问题。是的。尽管 Java 没有 virtual 关键字,但 Java 有虚函数,您可以编写它们。
在面向对象的编程中,虚函数或虚方法是一种函数或方法,其行为可以在继承类中被具有相同签名的函数覆盖。这个概念是面向对象编程 (OOP) 的多态部分的一个非常重要的部分。
像这样询问有关特定语言的架构问题需要出色的沟通技巧和深入掌握 Java 编译器的基本原理,特别是接口、抽象类以及继承的工作原理。
引导面试官了解虚拟功能的具体示例。
是的,您可以使用接口在 Java 中编写虚拟函数。
Java接口方法都是“纯虚拟的”,因为它们被设计为被覆盖。例如:
interface Bicycle { //the function applyBrakes() is virtual because
void applyBrakes(); //functions in interfaces are designed to be
} //overridden.
class ACMEBicycle implements Bicycle {
public void applyBrakes(){ //Here we implementing applyBrakes()
System.out.println("Brakes applied"); //function, proving it is virtual.
}
}
是的,您可以使用抽象类在 Java 中编写虚拟函数。
Java抽象类包含隐含的“虚拟”方法,由扩展它的类实现。例如:
abstract class Dog {
final void bark() { //bark() is not virtual because it is
System.out.println("woof"); //final and if you tried to override it
} //you would get a compile time error.
abstract void jump(); //jump() is a virtual function because it
} //is part of an abstract class and isn't
//final.
class MyDog extends Dog{
void jump(){
System.out.println("boing"); //here jump() is being overridden, a
} //demonstration that it is virtual.
}
public class Runner {
public static void main(String[] args) {
MyDog myDog = new MyDog(); //instantiating myDog
myDog.jump(); //calling the overridden function jump()
}
}
你可以强制一个函数在泛型类中不是虚拟的,方法是让它final
例如:
class myJavaFoobarClass {
final boolean giveMeTrueFunction() //this Java function is NOT virtual
{ //because final keyword prevents this
return true; //function from being modified in a
} //subclass.
boolean isItRainingFunction() //this Java function IS virtual because
{ //without the final keyword, the function
return false; //can be overridden in a subclass.
}
}
至少在 Java 中没有 virtual 关键字。
它只是解析为您调用的任何方法的最衍生版本......
class A{
void sayhi(){ System.out.println("A");}
}
class B extends A{
void sayhi(){ System.out.println("B");}
}
A a = new B();
a.sayhi();
将打印“B”。
您可以通过声明类 Abstract 并保留已声明但未实现的纯虚拟方法来创建“纯虚拟”方法。或者通过使用接口/实现而不是类/扩展。接口基本上是一个类,其中所有方法都是纯虚拟的。这有一个额外的好处,一个类可以实现多个接口,因为与 C++ 不同,Java 类只能直接继承另一个类。
编辑:
针对您的评论,Naveen:
如果你说 A a = new A(); a.sayhi(); 它会打印“A”。
Java 术语是动态的。您可以将其视为虚拟的,但这可能会使一些 Java 开发人员感到困惑。至少那些不懂 C++ 的人。在 Java 中没有显式指针,所以我们不需要担心虚拟/非虚拟。没有 VTable,您只需回溯类及其祖先,直到找到所需方法的实现。只有单一继承,所以你不必担心构造函数的顺序(它总是自下而上)。
在 C++ 中,如果你有虚拟方法并执行类似的操作,你会得到不同的行为
a->sayhi();
其中 a 是 A* 指向 B 的实例而不是
a.sayhi();
其中 a 是 A 类型的对象,其中包含 B 类型的对象
在 Java 中,虚函数是完全不同的。在 Java 中,虚函数是指可以在其子类中覆盖的函数。所以Java的所有非静态方法都是虚函数。只有 final 和 private 函数没有被继承,所以它们不是虚函数。
所以我们可以说Java中所有非final和private的非静态函数都是虚函数。
所有支持多态的语言都使用 vtables 来解析对正确函数的方法调用。Java 和 .NET 也是如此。
它们都编译为某种中间语言(.NET 的 IL 和 java 的字节码),但 vtable 在这种中间语言中不可见。它由底层引擎支持(CLR for .NET)
我确信 Java 不使用 vtables,因为它能够支持二进制兼容性。
澄清: vtable 是在编译派生/子类时构建的。如果基类/超类布局发生变化,则需要重新编译 d/s 的 vtable。在 Java 中,情况并非如此。您可以更改 b/s 而无需重新编译 d/s。
嗯。有可能在运行时构建 vtable。加载类时。在这种情况下,Java 和 C++ 将相同但不同。
纯虚函数在 C++ 中。C# 使用接口来达到同样的目的,所以我建议你走那条路。