很多人会问:

  1. 本来有的方法为什么要再写一遍?
  2. 重写的意义是什么?
  3. 两个方法调用的时候不会有冲突吗?
  4. 为什么不再新建一个全新的方法呢?

当一个子类继承一个父类时,它同时继承了父类的属性和方法。子类可以直接使用父类的属性和方法,如果父类的方法不能满足子类的需求,则可以在子类中对父类的方法进行重写(或覆盖)。

在方法重写时,如果子类需要引用父类中原有的方法,可以使用 super 关键字。当子类重写父类方法后,在子类对象使用该方法时,会执行子类中重写的方法。如果子类型重写了父类型的同名方法,那么只知道父类型的定义就可以调用子类型的方法了

实际应用中,用得最多的一种运行时多态,就是用只知道父类型(可能是类,更多的可能是接口)的定义,这样只能调用父类型的方法(这在大型软件里很常用,父类型和子类型可能是不同的人编写的,以利于协作编程)。

简约规则:
在子类重写父类方法时,需要遵守以下几个重写规则。
□ 重写方法名、参数和返回类型必须与父类方法定义一致。
□ 重写方法的修饰符不能比父类方法严格。例如父类方法时用 public 修饰,那么重写方法不能使用protected或private等修饰。
□ 重写方法如果有throws 定义,那么重写方法throws 的异常类型可以是父类方法throws的异常类型及其子类类型。
方法在重写时有很多细节需要注意,否则即使定义了方法,也可能不属于重写,不具有方法重写之后的特征。

重写的规则:
1、重写规则之一:
访问修饰符的限制一定要不小于被重写方法的访问修饰符

    比如:Object类有个toString()方法, 开始重写这个方法的时候我们总容易忘记Public修饰符, 出错的原因就是:没有加任何访问修饰符的方法具有包访问权限, Default访问权限小于Public访问权限, 所以编译器出错。

2、重写规则之二:
参数列表必须与被重写方法的相同。
重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。

3、重写规则之三:
C-1:返回类型必须与被重写方法的返回类型相同。
父类方法A:void catch(){} 子类方法 B:int catch(){} 两者虽然参数相同, 返回类型不同, 所以不是重写。
父类方法A:int catch(){} 子类方法 B:long catch(){} 返回类型虽然兼容父类, 但是不同就是不同, 所以不是重写。

     C-1補足1:  如果在没有加注@Override的情况下, 方法名和参数列表完全相同,且满足规则A的情况下, 返回值类型必须完全一致的情况下、才不会出现编译错误(即为该方法为强制重写方法)。如果以上条件中参数列表不同,且返回值类型不同这样编译并不会出现错误(这个方法为在子类的新方法,且不是重写方法)。// 2016/11/21 19:44 bluetata 追記标注補足1(下接博文后追記)C-1補足2:  当子类的方法重写或实现父类的方法时, 方法的后置条件(即方法的返回值)要比父类更严格。[参照2]// 2016/11/22 追記

即:如果重写方法的参数列表和方法名相同,且其他条件满足的情况下,方法的返回值为父类的子类,那么该方法也为重写方法

package com.ibm.dietime1943.test;

public class Computer {
public Computer sale() { return new Computer(); }

public HP make() { return new HP(); }
}

class IBM extends Computer {

@Override
public IBM sale() { return new IBM(); }
}

class HP extends Computer {

@Override
public Computer make() { return new Computer(); } // compilation error
}

4、重写规则之四:
重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。

举个简单的例子:父类异常好比父亲偷盗抢掠、那么儿子不能比父亲更坏、要学好、自然异常就要少。虽然举得例子与社会主义核心价值观有冲突、但是比较形象。// 2016/12/10 10:55 bluetata 追記 add

5、重写规则之五:
如果一个方法不能被继承, 则不能重写它。
比较典型的就是父类的private方法。因为private说明该方法对子类是不可见的, 子类再写一个同名的方法并不是对父类方法进行复写(Override), 而是重新生成一个新的方法, 也就不存在多态的问题了。同理也可以解释final, 因为方法同样是不可覆盖的。

6、重写规则之六:
不能重写被标识为final的方法。

 // 2016/12/01 17:05 bluetata 追記 add Startfinal方法可以被继承, 但是不能被重写, 一个方法如果被final修饰, 那么也就意味着, 这个方法不会被改动(声明一个final方法的主要目的是防止方法的内容被修改)。// 2016/12/01 17:12 bluetata 追記 add End

7、重写规则之七:
静态方法不能被重写。

《JAVA编程思想》中多次的提到:方法是静态的、他的行为就不具有多态性。静态方法是与类、而非单个对象相关联的。

父类的普通方法可以被继承和重写,不多作解释,如果子类继承父类,而且子类没有重写父类的方法,但是子类会有从父类继承过来的方法。静态的方法可以被继承,但是不能重写。如果父类中有一个静态的方法,子类也有一个与其方法名,参数类型,参数个数都一样的方法,并且也有static关键字修饰,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。通俗的讲就是父类的方法和子类的方法是两个没有关系的方法,具体调用哪一个方法是看是哪个对象的引用;这种父子类方法也不在存在多态的性质。《JAVA编程思想》:只有普通的方法调用可以是多态的,静态方法是与类而不是与某个对象相关联。

// 2016/11/22 16:45 bluetata 追記 add Start

補足1:父类的静态方法不能被子类覆盖为非静态方法。

   子类可以定义于父类的静态方法同名的静态方法、以便在子类中隐藏父类的静态方法(满足覆盖约束)、而且Java虚拟机把静态方法和所属的类绑定、而把实例方法和所属的实例绑定。如果在上记的方法上追记@Override注解的话、该方法会出编译错误。应为该方法实际不是重写方法。

補足2:父类的非静态方法不能被子类覆盖为静态方法。

// 2016/11/22 16:45 bluetata 追記 add End

補足3:面试可能会遇到的此处相关问题(与静态相关)

            1、abstract方法能否被static修饰?不能被static修饰, 因为抽象方法要被重写、而static和子类占不到边、即上述。// 2016/12/06 20:59 bluetata 追記反过来也一样static方法一定不能被abstract方法修饰, static不属于对象而属于类,  static方法可以被类直接调用(抽象方法需要被实例才能被调用, 这里说的实例是实现的意思,也就是重写后实现其方法), 这样注定了static方法一定有方法体, 不能是没有方法体的抽象方法(被abstract修饰)  // 2018/07/10 18:17 bluetata 追記2、为什么静态方法不能被覆盖? // 2016/12/15 午后 追記可以参看上面从java编程思想摘出的话、另外在总结下:覆盖依赖于类的实例,而静态方法和类实例并没有什么关系。而且静态方法在编译时就已经确定,而方法覆盖是在运行时确定的(动态绑定)(也可以说是java多态体现在运行时、而static在编译时、与之相悖)。3、构造方法能否被重写、为什么? // 2016/12/15 晚 追記不能、构造方法是隐式的static方法、同问题2。其实这个问题回答切入点很多、首先构造方法无返回值、方法名必须和所在类名相同、这一点就必杀了子类无法重写父类构造方法。另外多态方面、重写是多态的一种提现方式、假设在子类重写了构造方法是成立的、那么子类何谈实例成父类。另外重要得一点、子类可以使用super()调用父类的构造方法、且必须放在子类构造方法内的第一行。 请参看另一篇博文: <<Super和this用法,对象的加载顺序>>4、静态方法为什么不能访问非静态变量或方法?  // 2018/07/10 午后追记对于前面123问题理解后, 问题4也不难理解, 还是引用下《JAVA编程思想》:静态方法是与类而不是与某个对象相关联  用static修饰的成员属于类, 非static修饰的成员属于实例对象, 也就是类可以直接调用静态成员, 这样假设如果类直接调用了静态成员, 而静态成员调用了非静态变量或方法, 这样在内存中是找不到该非静态变量方法的, 因为静态方法需要创建对象后才可调用.另外通过类加载说明: 类的加载全过程:加载->验证->准备->解析->初始化 在这里加载到解析阶段都是JVM进行主导,而在初始化阶段才是真正java执行代码的阶段. static成员在初始化阶段之前会被加载到方法区中, 并且进行初始化赋值等操作,并且分配内存, 而非static成员确是在加载后解析后的初始化阶段才会被"加载"分配内存, 也就是代码中使用new进行创建实例的时候, 这样也就验证了 类可以直接调用static成员没有问题, 而直接调用非static的成员就会出问题, 因为违背了java加载初始化的逻辑.

注意: 如果static调用非static成员 编译器会出现 No enclosing instance of type * is accessible 异常错误.

XX01、重写规则補足:

補足1:父类的抽象方法可以被子类通过两种途径覆盖(即实现和覆盖)。

補足2:父类的非抽象方法可以被覆盖为抽象方法[2]。

      [2]子类必须为抽象类。

package com.ibm.dietime1943.test;

public class Computer {

public Computer send() { return new Computer();}
}

abstract class Lenovo extends Computer {

@Override
public abstract Computer send();
}

以上规则更加详细的说明请参看另一篇博文: <<JAVA中 @Override 的作用>>

// 2016/11/21 20:27 bluetata 追記标注補足1(上接博文后追記)

举例(来源于OCJP题库):

Given:

  1. public class Blip {
  2. protected int blipvert(int x) { return 0; }
  3. }
  4. class Vert extends Blip {
  5. // insert code here
  6. }
    Which five methods, inserted independently at line 5, will compile? (Choose five.)
    A. public int blipvert(int x) { return 0; }
    B. private int blipvert(int x) { return 0; }
    C. private int blipvert(long x) { return 0; }
    D. protected long blipvert(int x) { return 0; }
    E. protected int blipvert(long x) { return 0; }
    F. protected long blipvert(long x) { return 0; }
    G. protected long blipvert(int x, int y) { return 0; }
    Answer: A,C,E,F,G
    Explanation:继承关系后,子类重写父类的方法时,修饰符作用域不能小于父类被重写方法,所以A正确,B不正确。选项CEFG均不满足重写规则,不是重写方法(在子类的普通方法)。选项D即为不满足C-1补足。

里氏替换原则

这项原则最早是在1987年、由麻省理工学院的由芭芭拉·利斯科夫(Barbara Liskov)在一次会议上名为“数据的抽象与层次”的演说中首先提出。里氏替换原则的内容可以描述为: “派生类(子类)对象能够替换其基类(超类)对象被使用。” 以上内容并非利斯科夫的原文,而是译自罗伯特·马丁(Robert Martin)对原文的解读。其原文为:Let q(x) be a property provable about objectsx of type T. Thenq(y) should be true for objectsy of typeS where S is a subtype ofT.严格的定义:如果对每一个类型为T1的对象o1、都有类型为T2的对象o2、使得以T1定义的所有程序P在所有的对象o1都换成o2时、程序P的行为没有变化、那么类型T2是类型T1的子类型。通俗的定义:所有引用基类的地方必须能透明地使用其子类的对象。更通俗的定义:子类可以扩展父类的功能,但不能改变父类原有的功能。里氏替换原则包含以下4层含义:1、子类可以实现父类的抽象方法、但是不能[1]覆盖父类的非抽象方法。(核心)[参照1]

在我们做系统设计时、经常会设计接口或抽象类、然后由子类来实现抽象方法、这里使用的其实就是里氏替换原则。子类可以实现父类的抽象方法很好理解、事实上子类也必须完全实现父类的抽象方法、哪怕写一个空方法、否则会编译报错。

里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法、实际上是在设定一系列的规范和契约、虽然它不强制要求所有的子类必须遵从这些规范、但是如果子类对这些非抽象方法任意修改、就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

[1]处的说明:该处的不建议原则、并不是硬性规定无法不能的含义。增加新功能时、尽量添加新方法实现、而不是(不建议)去重写父类的方法、也不建议重载父类的方法。// 2016/11/22 15:33 bluetata 追記

   2、子类中可以增加自己特有的方法。3、当子类重写或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。4、当子类的方法重写或实现父类的方法时,方法的后置条件(即方法的返回值)要比父类更严格。[参照2]// 2016/11/22 18:54 bluetata 追記 add Start - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -追记来源/Michael727(简书作者)。原文链接:http://www.jianshu.com/p/2aa66a36af26里氏替换原则的核心是抽象,抽象又依赖于继承这个特性,在OOP当中,继承的优缺点都相当的明显。

继承的优点:

   ①、代码重用,减少创建的成本,每个子类拥有父类的方法和属性。②、子类和父类基本相似,但又与父类有所区别。③、提高代码的可扩展性,实现父类的方法就可以了,很多开源框架的扩展接口都是通过继承父类完成的。④、提高产品或项目的开放性。

继承的缺点:。

   ①、继承是侵入性的,只要继承就必须拥有父类的所有属性和方法。②、可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类的属性和方法。③、增强了耦合性。当父类的常量、变量和方法被修改时,必须考虑子类的修改,而且在缺乏规范的环境下,这种修好可能带来非常

糟糕的结果:大片的代码需要重构。

方法重写(Java篇)相关推荐

  1. 重写toString()方法(Java篇)

    java 中重写toString()方法 toString()方法 一般出现在System.out.println(类名.toString()); toString()是一种自我描述方法 本身返回的是 ...

  2. JAVA SE、EE、ME,JRE、JDK,基本数据类型,访问修饰符、函数、封装的概念、UML类图、构造方法、this关键字、static关键字、方法重载、方法重写、包(packahe)

    运行第一个JAVA程序: 这里使用的开发环境是eclipse,新建一个java工程,然后可以看到src这个是存放java代码的地方,然后在src文件右击新建一个class(类),然后可以看到下图,同样 ...

  3. 【JVM系列3】方法重载和方法重写原理分析,看完这篇终于彻底搞懂了

    深入分析Java虚拟机中方法执行流程及方法重载和方法重写原理 前言 思考 栈帧 局部变量表(Local Variables) 操作数栈(Operand Stacks) 动态连接(Dynamic Lin ...

  4. Java基础【之】面向对象编程(封装、继承(extends、方法重写、super)、多态(动态绑定、重载/重写)、代码实现)

    Java基础[之]面向对象编程(封装.继承.多态.代码实现) 1.封装 2.继承 2.1.extends 2.2.方法重写 2.3.super 3.多态 3.1.对象的多态.方法的多态 3.2.动态绑 ...

  5. java子类对象和成员变量的隐写方法重写

    1.子类继承的方法只能操作子类继承和隐藏的成员变量名字类新定义的方法可以操作子类继承和子类新生命的成员变量,但是无法操作子类隐藏的成员变量(需要适用super关键字操作子类隐藏的成员变量.) publ ...

  6. Java Arrays.Sort方法重写

    当原始的java sort方法无法满足我们的需求时候,我们需要自定义一些排序方法,此时需要重写Array.sort方法重写. 模板代码如下,默认是从小到大排序的,如果想从大到小,把a-b换为b-a即可 ...

  7. Java 方法重载 方法重写

    方法重载规则 参数个数不同 参数个数相同,但参数列表中对应的某个参数的类型不一样 方法的返回类型和参数名称不参与重载 "编译期绑定",,因为未产生对象,只看参数.引用类型绑定方法 ...

  8. Java记录 -24- equals方法重写

    Java equals方法重写 假如我们要求,当两个学生的id一样时就是同一个人,代码实现如下: public class EqualsTest {public static void main(St ...

  9. Java面向对象之继承,方法重写,super关键字,员工类系列继承题

    在程序中,如果想声明一个类继承另一个类,需要使用extends关键字. 格式: class 子类 extends 父类 {} 继承的好处 1.继承的出现提高了代码的复用性,提高软件开发效率. 2.继承 ...

最新文章

  1. 基于C++的骨架提取的鼻祖算法
  2. 谷歌Jeff Dean团队发文,探讨「学习模型」如何替代传统索引结构
  3. 加油!兄弟连的兄弟们!
  4. Web2.0时代,RSS你会用了吗?(技术实现总结)(转载)
  5. PNG,GIF,JPG的区别及如何选
  6. mongotemplate模糊查_java 中 mongodb的各种操作 模糊查询 精确查询 等等
  7. Java学习笔记1.2.1 使用Java集成开发环境 - Eclipse
  8. 使用Sysmon分析宏病毒(Macros Downloader)
  9. c语言编程 通讯录排序,C语言实现一个通讯录
  10. linux下使用SSH实现端口映射
  11. Visio 画流程图 入门
  12. python做词云统计_python词频统计,生成词云
  13. Unity笔记—常用小功能整合
  14. python 正则re模块 group() groups()
  15. 致远OA—V5版本系统预置用户密码恢复方法
  16. 发现ULC(UltraLightClient)
  17. 程序员爬取 5000+ 口红商品数据,差点比女朋友更懂口红?
  18. Python在计算机里快速查找文件
  19. 巧用google实现快速搜索
  20. 网闸——安全隔离网闸:从第一代走向第二代

热门文章

  1. python新手入门英文词汇笔记(1-1)_Python新手入门英文词汇(1-1)
  2. poco c++ 开发指南_掌握这个框架,你将会开发通杀全平台的万能爬虫
  3. python randrange函数_Python学习-函数补充2-内置函数
  4. 软件开发计划_敏捷软件开发实践:估算与计划读书笔记123第21章 关于计划的沟通...
  5. mysql设置自动递增_《MySQL数据库》约束条件和自增长序列
  6. 7.33oracle安装不了,在RedHat7.0下安装Oracle的经历
  7. 用VS.NET2003制作WEB应用程序的安装包
  8. ​“统治”移动处理器市场的Arm为何明年Q1才发布AI处理器?
  9. 模型OnMouseXXX事件
  10. 编码之道:取个好名字很重要(转)