Single Dispatch

class Parent {
void print(String a) { log.info("Parent - String"); }
void print(Object a) { log.info("Parent - Object"); }
} class Child extends Parent {
void print(String a) { log.info("Child - String"); }
void print(Object a) { log.info("Child - Object"); }
}
String string = "";
Object stringObject = string; // What gets printed?
Child child = new Child();
child.print(string);
child.print(stringObject); Parent parent = new Child();
parent.print(string);
parent.print(stringObject);
child.print(string);        // Prints: "Child - String"
child.print(stringObject); // Prints: "Child - Object" parent.print(string); // Prints: "Child - String"
parent.print(stringObject); // Prints: "Child - Object"

被调用的方法取决于“实际”实例类型,而不是“声明的”实例类型。

Java 不支持双调度,因此,在处理方法参数时,重要的是参数的“声明”类型,而不是其“实际”类型。

双调度是根据接收器和参数类型选择在运行时调用的方法的过程的技术术语。(可以使用访问者模式达到一样的效果)

Hidden Override

class Parent {
void print(Object a) { log.info("Parent - Object"); }
} class Child extends Parent {
void print(String a) { log.info("Child - String"); }
}
String string = "";
Parent parent = new Child();
parent.print(string);
parent.print(string);  // Prints: "Parent - Object"

在检查子类覆盖之前,Java 首先会选择要调用的方法。 在这种情况下,声明的实例类型是 Parent ,Parent 中唯一匹配的方法是 Parent :: print(Object)。 当Java然后检查 Parent :: print(Object) 的任何潜在覆盖时,它找不到任何覆盖,因此这是执行的方法。

Exposed Override

class Parent {
void print(Object a) { log.info("Parent - Object!"); }
void print(String a) { throw new RuntimeException(); }
} class Child extends Parent {
void print(String a) { log.info("Child - String!"); }
}
String string = "";
Parent parent = new Child();
parent.print(string);
parent.print(string);  // Prints: "Child - String!"

Ambiguous Parameter

class Foo {
void print(Cloneable a) { log.info("I am cloneable!"); }
void print(Map a) { log.info("I am Map!"); }
}
HashMap cloneableMap = new HashMap();
Cloneable cloneable = cloneableMap;
Map map = cloneableMap; // What gets printed?
Foo foo = new Foo();
foo.print(map);
foo.print(cloneable);
foo.print(cloneableMap);
foo.print(map);           // Prints: "I am Map!"
foo.print(cloneable); // Prints: "I am cloneable!"
foo.print(cloneableMap); // Does not compile

不编译,因为有多个方法对给定参数同样有效。

Multiple Inheritance – Interfaces

interface Father {
default void print() { log.info("I am Father!"); }
} interface Mother {
default void print() { log.info("I am Mother!"); }
} class Child implements Father, Mother {}
new Child().print();

不编译,因为 Father 和 Mother 存在冲突的默认方法

Multiple Inheritance – Class and Interface

class ParentClass {
void print() { log.info("I am a class!"); }
} interface ParentInterface {
default void print() { log.info("I am an interface!"); }
} class Child extends ParentClass implements ParentInterface {}
new Child().print();  // Prints: "I am a class!"

如果类和接口之间存在继承冲突,则类获胜。

Transitive Override

class Parent {
void print() { foo(); }
void foo() { log.info("I am Parent!"); }
} class Child extends Parent {
void foo() { log.info("I am Child!"); }
}
new Child().print();  // Prints: "I am Child!"

覆盖方法对传递调用也会生效。如果方法被覆盖,那么 Parent :: print 将调用被覆盖的 foo() 版本。

Private Override

class Parent {
void print() { foo(); }
private void foo() { log.info("I am Parent!"); }
} class Child extends Parent {
void foo() { log.info("I am Child!"); }
}
new Child().print();  // Prints: "I am Parent!"

Parent.foo() 被声明为私有。 因此,当 Parent.print() 调用 foo() 时,它被硬编码为Parent.foo()。 无论子类中是否有 foo() 的其他实现或者调用 print() 的实例的实际类型。

Static Overrides

class Parent {
static void print() { log.info("I am Parent!"); }
} class Child extends Parent {
static void print() { log.info("I am Child!"); }
}
Child child = new Child();
Parent parent = child; parent.print(); // Prints: "I am Parent!"
child.print(); // Prints: "I am Child!"

Java 不允许重写静态方法。如果在父类和子类中都定义了相同的静态方法,则实例的实际类型根本不重要。只有声明的类型用于确定调用两个方法中的哪一个。

Static Linking

class Parent {
void print() { staticMethod(); instanceMethod(); }
static void staticMethod() { log.info("Parent::staticMethod"); }
void instanceMethod() { log.info("Parent::instanceMethod"); }
} class Child extends Parent {
static void staticMethod() { log.info("Child::staticMethod"); }
void instanceMethod() { log.info("Child::instanceMethod"); }
}
Child child = new Child();
child.print();
Parent::staticMethod
Child::instanceMethod

对于实例方法,即使调用者在父级中,覆盖也会生效。 但是,对于静态方法,即使变量的声明类型是 Child,由于中间的 print() 方法,也会调用 Parent :: staticMethod

Wrapping up

  • 始终使用 @Override 注释标记所有覆盖方法
  • 始终使用类引用而不是实例引用来调用静态方法
  • 设置 IDE 或 lint 错误提醒以强制执行上述和其他代码检测
  • 使用组合而不是继承

java 重写的 几大注意点的更多相关文章

  1. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  2. Java - 21 Java 重写(Override)与重载(Overload)

    Java 重写(Override)与重载(Overload) 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变.即外壳不变,核心重写! 重写的好 ...

  3. java 重写你可以这么理解 因为 方法名和参数类型个数相同 所以这就是重写了 然后 因为是重写 所以 返回值必须相同

    java  重写你可以这么理解    因为   方法名和参数类型个数相同  所以这就是重写了    然后  因为是重写  所以  返回值必须相同

  4. Java:重写equals()和hashCode()

    Java:重写equals()和hashCode() 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instan ...

  5. JAVA重写不需要@override

    一,如下代码, package com.boot.enable.bootenable; import org.springframework.scheduling.annotation.Async; ...

  6. java重写equals方法

    @Override public int hashCode() { return task.getId(); } @Override public boolean equals(Object obj) ...

  7. Java 重写(Overriding)和重载(Overloading)

    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现. 重写是父类与子类之间多态性的一种表现 重载是一类中多态性的一种表现.

  8. java 重写 重载

    首先我们来讲讲:重载(Overloading) (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型. 重载Overloading是一个类中多态 ...

  9. Java重写方法与初始化的隐患(转)

    原文出处: Shawon 虽然文章标题是Java, 但几乎所有面向对象设计的语言都遵守这个初始化流程, 感谢廖祜秋liaohuqiu_秋百万指出, 之前忘记提这个了. 前言 drakeet写了个和Re ...

随机推荐

  1. http协议。会话控制cookie、session

    http协议是无状态的协议.每次访问页面的http协议都是独立的,正是因为http协议是无状态的,所以导致访问一个页面后再去访问另一个页面的时候,一些数据会消失,比如:用户的登录信息就会消失.那么怎么 ...

  2. (九)文档和视图,Invalidate,数据库编程

    一.文档视图结构 文档类(CDocument):存储加载(读写)数据视图类(CView):显示和修改数据 1)单文档 a)文档模板:把框架窗口.文档.视图关联在一起b)文档类(CDocument): ...

  3. NotFoundError (see above for traceback): Key local3/weights not found in checkpoint

    解决办法 原文 https://www.jianshu.com/p/2de8e01af88d with tf.Session() as sess: tf.get_variable_scope().re ...

  4. NSNull

    集合中是不能放nil值的,因为nil是结尾,但是为了存放表示什么都没有的值,可以使用NSNull,它也是NSObject的一个子类. void null(){ NSNull *nl=[NSNull n ...

  5. When you want to succeed as bad as you wanna breathe, then you’ll be successful.

    上周末登了次山,回来就各种矫情犯懒.今天周四一周又要完蛋,我发现自己真的是对时间流逝无可奈何.然后中午看了把小码哥网站还有MJ博客什么的,各种首期班大爆照,心中羞愧无比.年纪大也不能放弃自己啊,要不人 ...

  6. 在eclipse中查找一个类中的方法在其他哪个类中被调用了

    选中你所要查看的方法名,ctrl+shift+G就可以查看所有调用过该方法的地方了.在Search视图里面可以查看得到这个样子是可以的,你也可以按Ctrl+H全文检索一下

  7. HDU 2176 取(m堆)石子游戏 —— (Nim博弈)

    如果yes的话要输出所有情况,一开始觉得挺难,想了一下也没什么. 每堆的个数^一下,答案不是0就是先取者必胜,那么对必胜态显然至少存在一种可能性使得当前局势变成必败的.只要任意选取一堆,把这堆的数目变 ...

  8. 说说如何使用unity Vs来进行断点调试

    转载自:http://dong2008hong.blog.163.com/blog/static/4696882720140293549365/ 大家可以从这下载最新版的unity vs. Unity ...

  9. axios中出现两次请求,OPTIONS请求和GET请求

    在项目中发现ajax中出现两次请求,OPTIONS请求和GET请求 查看到浏览器NetWork有两次请求,请求url一样: 查找原因是浏览器对简单跨域请求和复杂跨域请求的处理区别. XMLHttpRe ...

  10. LeetCode687----最长同值路径

    给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值. 这条路径可以经过也可以不经过根节点. 注意:两个节点之间的路径长度由它们之间的边数表示. 示例 1: 输入: 5 / \ 4 5 / ...