热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java重写的7个规则

几年前你可能会遇到这样一个面试题:“重写和重载的区别”、而现在随着科技的更迭、面试的问题越来越高级、面试官的问题也越来越深入、此文是上述面试题的一个延伸、让你从简单的重写规则中更加深入的理解其软件工程

几年前你可能会遇到这样一个面试题:“重写和重载的区别”、而现在随着科技的更迭、面试的问题越来越高级、面试官的问题也越来越深入、此文是上述面试题的一个延伸、让你从简单的重写规则中更加深入的理解其软件工程面向对象的思想。


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 Tata 追記标注補足1(下接博文后追記)

    C-1補足2当子类的方法重写或实现父类的方法时,方法的后置条件(即方法的返回值)要比父类更严格。[参照2]// 2016/11/22 17:56 Tata 追記

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

// C-1補足2の例を挙げります:
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 Meta 追記 add


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


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

     // 2016/12/01 17:05 Tata 追記 add Start

    final方法可以被继承、但是不能被重写、一个方法如果被final修饰、那么也就意味着、这个方法不会被改动(声明一个final方法的主要目的是防止方法的内容被修改)。

     // 2016/12/01 17:12 Tata 追記 add End


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

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

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

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

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

       子类可以定义于父类的静态方法同名的静态方法、以便在子类中隐藏父类的静态方法(满足覆盖约束)、而且Java虚拟机把静态方法和所属的类绑定、而把实例方法和所属的实例绑定。

       如果在上记的方法上追记@Override注解的话、该方法会出编译错误。应为该方法实际不是重写方法。

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

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

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

       1、abstract方法能否被static修饰?

          不能被因为抽象方法要被重写、而static和子类占不到边、即上述。// 2016/12/06 20:59 Meta 追記

       2、为什么静态方法不能被覆盖?// 2016/12/15 午后 追記

          可以参看上面从java编程思想摘出的话、另外在总结下:覆盖依赖于类的实例,而静态方法和类实例并没有什么关系。而且静态方法在编译时就已经确定,而方法覆盖是在运行时确定的(动态绑定)(也可以说是java多态体现在运行时、而static在编译时、与之相悖)。

       3、构造方法能否被重写、为什么? // 2016/12/15 晚 追記

          不能、构造方法是隐式的static方法、同问题2。其实这个问题回答切入点很多、首先构造方法无返回值、方法名必须和所在类名相同、这一点就必杀了子类无法重写父类构造方法。另外多态方面、重写是多态的一种提现方式、假设在子类重写了构造方法是成立的、那么子类何谈实例成父类。另外重要得一点、子类可以使用super();调用父类的构造方法、且必须放在子类构造方法内的第一行。 请参看另一篇博文: <>


XX01重写规则補足

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

   補足2:父类的非抽象方法可以被覆盖为抽象方法[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();
}

以上规则更加详细的说明请参看另一篇博文: <>


// 2016/11/21 20:27 Tata 追記标注補足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补足。


// 2016/11/22 11:08 Tata 追記補足2

里氏替换原则(リスコフの置換原則(りすこふのちかんげんそく)、英:Liskov Substitution Principle)

    这项原则最早是在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 Tata 追記

       2、子类中可以增加自己特有的方法。

       3、当子类重写或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

       4、当子类的方法重写或实现父类的方法时,方法的后置条件(即方法的返回值)要比父类更严格。[参照2]


    // 2016/11/22 18:54 Tata 追記 add Start - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    追记来源/Michael727(简书作者)。

    原文链接:http://www.jianshu.com/p/2aa66a36af26

    里氏替换原则的核心是抽象,抽象又依赖于继承这个特性,在OOP当中,继承的优缺点都相当的明显。

   继承的优点

       ①、代码重用,减少创建的成本,每个子类拥有父类的方法和属性。

       ②、子类和父类基本相似,但又与父类有所区别。

       ③、提高代码的可扩展性,实现父类的方法就可以了,很多开源框架的扩展接口都是通过继承父类完成的。

       ④、提高产品或项目的开放性

   继承的缺点:。

       ①、继承是侵入性的,只要继承就必须拥有父类的所有属性和方法。

       ②、可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类的属性和方法。

       ③、增强了耦合性。当父类的常量、变量和方法被修改时,必须考虑子类的修改,而且在缺乏规范的环境下,这种修好可能带来非常糟糕的结果:大片的代码需要重构。

    // 2016/11/22 18:54 Tata 追記 add End - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



Flag Counter


推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • Java学习笔记之使用反射+泛型构建通用DAO
    本文介绍了使用反射和泛型构建通用DAO的方法,通过减少代码冗余度来提高开发效率。通过示例说明了如何使用反射和泛型来实现对不同表的相同操作,从而避免重复编写相似的代码。该方法可以在Java学习中起到较大的帮助作用。 ... [详细]
  • 本文介绍了在使用Laravel和sqlsrv连接到SQL Server 2016时,如何在插入查询中使用输出子句,并返回所需的值。同时讨论了使用CreatedOn字段返回最近创建的行的解决方法以及使用Eloquent模型创建后,值正确插入数据库但没有返回uniqueidentifier字段的问题。最后给出了一个示例代码。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 浏览器如何工作(How browsers work)的阅读笔记
    浏览器如何工作(Howbrowserswork)的阅读笔记1.整体结构完整的浏览器整体框架的发改如下:UI:就是那些我们常常 ... [详细]
author-avatar
手机用户2602889563
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有