java 重写的 几大注意点
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 重写的 几大注意点的更多相关文章
- JAVA 注解的几大作用及使用方法详解
JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...
- Java - 21 Java 重写(Override)与重载(Overload)
Java 重写(Override)与重载(Overload) 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变.即外壳不变,核心重写! 重写的好 ...
- java 重写你可以这么理解 因为 方法名和参数类型个数相同 所以这就是重写了 然后 因为是重写 所以 返回值必须相同
java 重写你可以这么理解 因为 方法名和参数类型个数相同 所以这就是重写了 然后 因为是重写 所以 返回值必须相同
- Java:重写equals()和hashCode()
Java:重写equals()和hashCode() 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instan ...
- JAVA重写不需要@override
一,如下代码, package com.boot.enable.bootenable; import org.springframework.scheduling.annotation.Async; ...
- java重写equals方法
@Override public int hashCode() { return task.getId(); } @Override public boolean equals(Object obj) ...
- Java 重写(Overriding)和重载(Overloading)
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现. 重写是父类与子类之间多态性的一种表现 重载是一类中多态性的一种表现.
- java 重写 重载
首先我们来讲讲:重载(Overloading) (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型. 重载Overloading是一个类中多态 ...
- Java重写方法与初始化的隐患(转)
原文出处: Shawon 虽然文章标题是Java, 但几乎所有面向对象设计的语言都遵守这个初始化流程, 感谢廖祜秋liaohuqiu_秋百万指出, 之前忘记提这个了. 前言 drakeet写了个和Re ...
随机推荐
- spring boot 入门 使用spring.profiles.active来分区配置(转)
很多时候,我们项目在开发环境和生成环境的环境配置是不一样的,例如,数据库配置,在开发的时候,我们一般用测试数据库,而在生产环境的时候,我们是用正式的数据,这时候,我们可以利用profile在不同的环境 ...
- vscode调整字体大小
在vscode中,通过setting>User>Text Editor>Font可以调整字体大小,但是这里只是调整右侧的代码编辑区域的字体,左侧的侧边栏确无法调整字体大小,找了很久都 ...
- 逆向bfs搜索打表+康拓判重
HDU 1043八数码问题 八数码,就是1~8加上一个空格的九宫格,这道题以及这个游戏的目标就是把九宫格还原到从左到右从上到下是1~8然后最后是空格. 没了解康托展开之前,这道题怎么想都觉得很棘手,直 ...
- Casual Literary Notes
写在前面的话: 人生中总会有一些惊喜,它会给予坚守的人们以奖励,提醒着人们,生命中不光是辛劳和付出. 很多收获,最后看来,往往都是因为当初的那一点坚持. -- 雷宇<现场> 1.18 上午 ...
- 深度学习笔记(十三)YOLO V3 (Tensorflow)
[代码剖析] 推荐阅读! SSD 学习笔记 之前看了一遍 YOLO V3 的论文,写的挺有意思的,尴尬的是,我这鱼的记忆,看完就忘了 于是只能借助于代码,再看一遍细节了. 源码目录总览 tens ...
- Jenkins忘记admin密码补救措施
遇到将Jenkins登录名密码忘记的情况,下面的方式可以重置密码. 进入C:\Program Files (x86)\Jenkins\users 目录可以看到admin开头的文件夹,里面有个confi ...
- bzoj4152
The Captain HYSBZ - 4152 给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x2|,|y1-y2|),求从1号点走到n号点的最小费用. Input ...
- LK光流算法公式详解
由于工程需要用到 Lucas-Kanade 光流,在此进行一下简单整理(后续还会陆续整理关于KCF,PCA,SVM,最小二乘.岭回归.核函数.dpm等等): 光流,简单说也就是画面移动过程中,图像上每 ...
- mysql zip方式安装
下载zip文件解压到安装目录,此时是没有data文件夹和my.ini文件的. 1.首先自己新建my.ini,内容如下: [client] port=3306 default-character-set ...
- django-admin 配置
本节讲django-admin配置方法: 1.在工程配置文件中(settings.py)中启用admin组件.确保有如下两行配置: 2.执行数据库迁移的命令,确保对应的表在数据库中已经添加了 #pyt ...