我对Overriding与隐藏Java有何不同感到困惑。任何人都可以提供有关这些差异的更多细节吗我读了Java Tutorial,但示例代码仍让我感到困惑。
为了更清楚,我理解压倒一切。我的问题是我没有看到隐藏是有什么不同的,除了一个是在实例级别而另一个在类级别。
查看Java教程代码:
public class Animal {
public static void testClassMethod() {
System.out.println("Class" + " method in Animal.");
}
public void testInstanceMethod() {
System.out.println("Instance " + " method in Animal.");
}
}
然后我们有一个子类cat:
public class Cat extends Animal {
public static void testClassMethod() {
System.out.println("The class method" + " in Cat.");
}
public void testInstanceMethod() {
System.out.println("The instance method" + " in Cat.");
}
public static void main(String[] args) {
Cat myCat = new Cat();
Animal myAnimal = myCat;
Animal.testClassMethod();
myAnimal.testInstanceMethod();
}
}
然后他们说:
该程序的输出如下:
Animal中的类方法。
Cat中的实例方法
对我来说,直接从Animal类调用类方法testClassMethod(),在Animal类中执行该方法这一事实非常明显,没有什么特别之处。然后他们从对myCat的引用中调用testInstanceMethod(),所以再次非常明显的是,然后执行的方法是Cat实例中的方法。
从我所看到的隐藏行为就像覆盖一样,为什么要做出这种区分。如果我使用上面的类运行此代码:
Cat.testClassMethod();
我会得到:Cat中的类方法。但是如果我从Cat中删除testClassMethod(),那么我将得到:Animal中的类方法。
这告诉我,在子类中编写一个静态方法,与父级中的签名相同,几乎可以覆盖。
希望我清楚地表明我的困惑,有人可以解决问题。首先十分感谢!
超越基本上支持后期绑定。因此,将在运行时决定调用哪个方法。它用于非静态方法。隐藏适用于所有其他成员(静态方法,实例成员,静态成员)。它基于早期绑定。更清楚的是,在编译期间决定要调用或使用的方法或成员。
在你的例子中,第一次调用Animal.testClassMethod()
是对static
方法的调用,因此,非常确定将调用哪种方法。
在第二次调用myAnimal.testInstanceMethod()
中,它调用非静态方法。这就是你所说的运行时多态。直到运行时才调用哪种方法。
如需进一步说明,请阅读this。
链接的java教程页面解释了覆盖和隐藏的概念
子类中具有相同签名(名称,加上其参数的数量和类型)和返回类型作为超类中的实例方法的实例方法会覆盖超类的方法。
如果子类定义的静态方法与超类中的静态方法具有相同的签名,则子类中的方法会隐藏超类中的方法。
隐藏静态方法和覆盖实例方法之间的区别具有重要意义:
回到你的例子:
Animal myAnimal = myCat;
/* invokes static method on Animal, expected. */
Animal.testClassMethod();
/* invokes child class instance method (non-static - it's overriding) */
myAnimal.testInstanceMethod();
上述陈述尚未隐藏。
现在更改下面的代码以获得不同的输出:
Animal myAnimal = myCat;
/* Even though myAnimal is Cat, Animal class method is invoked instead of Cat method*/
myAnimal.testClassMethod();
/* invokes child class instance method (non-static - it's overriding) */
myAnimal.testInstanceMethod();
除了上面列出的示例之外,这里还有一个小示例代码,用于阐明隐藏和覆盖之间的区别:
public class Parent {
// to be hidden (static)
public static String toBeHidden() {
return "Parent";
}
// to be overridden (non-static)
public String toBeOverridden() {
return "Parent";
}
public void printParent() {
System.out.println("to be hidden: " + toBeHidden());
System.out.println("to be overridden: " + toBeOverridden());
}
}
public class Child extends Parent {
public static String toBeHidden() {
return "Child";
}
public String toBeOverridden() {
return "Child";
}
public void printChild() {
System.out.println("to be hidden: " + toBeHidden());
System.out.println("to be overridden: " + toBeOverridden());
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.printParent();
child.printChild();
}
}
child.printParent()
的召唤输出:
被隐藏:父母
要被覆盖:孩子
child.printChild()
的召唤输出:
被隐藏:孩子
要被覆盖:孩子
正如我们可以从上面的输出中看到的那样(特别是粗体标记的输出),方法隐藏的行为与覆盖不同。
Java允许隐藏和覆盖仅用于方法。同样的规则不适用于变量。不允许覆盖变量,因此只能隐藏变量(静态或非静态变量之间没有差异)。下面的示例显示了如何覆盖方法getName()
并隐藏变量name
:
public class Main {
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.name); // prints Parent (since hiding)
System.out.println(p.getName()); // prints Child (since overriding)
}
}
class Parent {
String name = "Parent";
String getName() {
return name;
}
}
class Child extends Parent {
String name = "Child";
String getName() {
return name;
}
}
在运行时,无论方法调用是在父类方法还是子类方法中定义,都始终为实例执行重写方法的子版本。以这种方式,除非使用语法ParentClassName.method()引用对父方法的显式调用,否则永远不会使用父方法。或者,在运行时,如果在父类中定义了对方法的调用,则始终会执行隐藏方法的父版本。
在方法重写中,方法解析由JVM基于运行时对象完成。在方法隐藏中,方法解析由编译器在引用的基础上完成。从而,
如果代码被写成,
public static void main(String[] args) {
Animal myCat = new Cat();
myCat.testClassMethod();
}
产出如下: Animal中的类方法。
如何在java中隐藏静态方法? Cat类正在扩展Animal类。所以在Cat类中会有两个静态方法(我的意思是Child类的静态方法和Parent类的静态方法)但是JVM如何隐藏Parent静态方法?它是如何处理堆和堆栈的?
静态方法是隐藏的,非静态方法被覆盖。当调用不符合“something()”vs“this.something()”时,差异显而易见。
我似乎无法用言语表达,所以这里举个例子:
public class Animal {
public static void something() {
System.out.println("animal.something");
}
public void eat() {
System.out.println("animal.eat");
}
public Animal() {
// This will always call Animal.something(), since it can't be overriden, because it is static.
something();
// This will call the eat() defined in overriding classes.
eat();
}
}
public class Dog extends Animal {
public static void something() {
// This method merely hides Animal.something(), making it uncallable, but does not override it, or alter calls to it in any way.
System.out.println("dog.something");
}
public void eat() {
// This method overrides eat(), and will affect calls to eat()
System.out.println("dog.eat");
}
public Dog() {
super();
}
public static void main(String[] args) {
new Dog();
}
}
OUTPUT:
animal.something
dog.eat
这是覆盖和隐藏之间的区别,
如果我理解你的问题,那么答案是“你已经压倒一切”。
“这告诉我,在子类中编写一个静态方法,与父级同名,几乎可以覆盖。”
如果在子类中编写一个方法,其名称与超类中的方法完全相同,则它将覆盖超类的方法。不需要@Override注释来覆盖方法。但它确实使您的代码更具可读性,并强制编译器检查您实际上是否覆盖了一个方法(例如,并没有拼错子类方法)。
覆盖仅在实例方法中发生。当引用变量的类型为Animal且对象为Cat时,则从Cat调用实例方法(这是重写)。对于相同的acat对象,使用Animal的类方法。
public static void main(String[] args) {
Animal acat = new Cat();
acat.testInstanceMethod();
acat.testClassMethod();
}
输出是:
The instance method in Cat.
Class method in Animal.
public class First {
public void Overriding(int i) { // will be overrided in class Second }
public static void Hiding(int i) { // will be hidden in class Second
// because it's static }
}
public class Second extends First {
public void Overriding(int i) { // overrided here }
public static void Hiding(int i) { // hidden
// because it's static }
}
记忆规则很简单:扩展类的方法不能将static更改为void,也不能将void更改为static。这将导致编译错误。
但是如果void Name改为void,则命名为Overriding。
如果静态名称更改为静态名称,则为隐藏名称。 (当编译器在超类的对象中看到静态方法时,它不检查子类中的方法。)
在这段代码中,我使用'private'访问修饰符而不是'static'来显示隐藏方法和覆盖方法之间的区别。
class Animal {
// Use 'static' or 'private' access modifiers to see how method hiding work.
private void testInstancePrivateMethod(String source) {
System.out.println("\tAnimal: instance Private method calling from "+source);
}
public void testInstanceMethodUsingPrivateMethodInside() {
System.out.println("\tAnimal: instance Public method with using of Private method.");
testInstancePrivateMethod( Animal.class.getSimpleName() );
}
// Use default, 'protected' or 'public' access modifiers to see how method overriding work.
protected void testInstanceProtectedMethod(String source) {
System.out.println("\tAnimal: instance Protected method calling from "+source);
}
public void testInstanceMethodUsingProtectedMethodInside() {
System.out.println("\tAnimal: instance Public method with using of Protected method.");
testInstanceProtectedMethod( Animal.class.getSimpleName() );
}
}
public class Cat extends Animal {
private void testInstancePrivateMethod(String source) {
System.out.println("Cat: instance Private method calling from " + source );
}
public void testInstanceMethodUsingPrivateMethodInside() {
System.out.println("Cat: instance Public method with using of Private method.");
testInstancePrivateMethod( Cat.class.getSimpleName());
System.out.println("Cat: and calling parent after:");
super.testInstanceMethodUsingPrivateMethodInside();
}
protected void testInstanceProtectedMethod(String source) {
System.out.println("Cat: instance Protected method calling from "+ source );
}
public void testInstanceMethodUsingProtectedMethodInside() {
System.out.println("Cat: instance Public method with using of Protected method.");
testInstanceProtectedMethod(Cat.class.getSimpleName());
System.out.println("Cat: and calling parent after:");
super.testInstanceMethodUsingProtectedMethodInside();
}
public static void main(String[] args) {
Cat myCat = new Cat();
System.out.println("----- Method hiding -------");
myCat.testInstanceMethodUsingPrivateMethodInside();
System.out.println("\n----- Method overriding -------");
myCat.testInstanceMethodUsingProtectedMethodInside();
}
}
输出:
----- Method hiding -------
Cat: instance Public method with using of Private method.
Cat: instance Private method calling from Cat
Cat: and calling parent after:
Animal: instance Public method with using of Private method.
Animal: instance Private method calling from Animal
----- Method overriding -------
Cat: instance Public method with using of Protected method.
Cat: instance Protected method calling from Cat
Cat: and calling parent after:
Animal: instance Public method with using of Protected method.
Cat: instance Protected method calling from Animal
根据我最近的Java研究
OCP Java 7书中的示例,第70-71页:
public class Point {
private int xPos, yPos;
public Point(int x, int y) {
xPos = x;
yPos = y;
}
public boolean equals(Point other){
.... sexy code here ......
}
public static void main(String []args) {
Point p1 = new Point(10, 20);
Point p2 = new Point(50, 100);
Point p3 = new Point(10, 20);
System.out.println("p1 equals p2 is " + p1.equals(p2));
System.out.println("p1 equals p3 is " + p1.equals(p3));
//point's class equals method get invoked
}
}
但如果我们写下以下主要内容:
public static void main(String []args) {
Object p1 = new Point(10, 20);
Object p2 = new Point(50, 100);
Object p3 = new Point(10, 20);
System.out.println("p1 equals p2 is " + p1.equals(p2));
System.out.println("p1 equals p3 is " + p1.equals(p3));
//Object's class equals method get invoked
}
在第二个主要部分中,我们使用Object类作为静态类型,因此当我们在Point对象中调用equal方法时,它正在等待一个Point类作为参数到达,但是Object来了。所以Object类等于运行方法,因为我们在那里有一个equals(Object o)。在这种情况下,Point的类等于dates't覆盖,但隐藏了Object类equals方法。
public class Parent {
public static void show(){
System.out.println("Parent");
}
}
public class Child extends Parent{
public static void show(){
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent parent=new Child();
parent.show(); // it will call parent show method
}
}
// We can call static method by reference ( as shown above) or by using class name (Parent.show())