码出高效:Java开发手册-第2章(9)

简介: 本章开始讲解面向对象思想,并以Java 为载体讲述面向对象思想在具体编程语言中的运用与实践。当前主流的编程语言有50 种左右,主要分为两大阵营:面向对象编程与面向过程编程。面向对象编程(Object-Oriented Programming,OOP)是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。OOP 的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木一样隔离可控、高效简单,是当今编程领域的一股势不可......

2.4.7 覆写

      多态中的override,本书翻译成覆写。如果翻译成重写,那么与重构意思过于接近;如果翻译成覆盖,那么少了“写”这个核心动词。如果父类定义的方法达不到子类的期望,那么子类可以重新实现方法覆盖父类的实现。因为有些子类是延迟加载的,甚至是网络加载的,所以最终的实现需要在运行期判断,这就是所谓的动态绑定。动态绑定是多态性得以实现的重要因素,元空间有一个方法表保存着每个可以实例化类的方法信息,JVM 可以通过方法表快速地激活实例方法。如果某个类覆写了父类的某个方法,则方法表中的方法指向引用会指向子类的实现处。代码通常是用这样的方式来调用子类的方法,通常这也被称作向上转型:

Father father = new Son();

// Son 覆写了此方法

father.doSomething();

      向上转型时,通过父类引用执行子类方法时需要注意以下两点:

(1)无法调用到子类中存在而父类本身不存在的方法。

      (2)可以调用到子类中覆写了父类的方法,这是一种多态实现。

      想成功地覆写父类方法,需要满足以下4 个条件:

(1)访问权限不能变小。访问控制权限变小意味着在调用时父类的可见方法无法被子类多态执行,比如父类中方法是用public 修饰的,子类覆写时变成private。设想如果编译器为多态开了后门,让在父类定义中可见的方法随着父类调用链路下来,执行了子类更小权限的方法,则破坏了封装。如下代码所示,在实际编码中不允许将方法访问权限缩小:

class Father {

      public void method() {

             System.out.println("Father's method");

      }

}

class Son extends Father {

      // 编译报错,不允许修改为访问权限更严格的修饰符

      @override

      private void method() {

             System.out.println("Son's method");

      }

}

      (2)返回类型能够向上转型成为父类的返回类型。虽然方法返回值不是方法签名的一部分,但是在覆写时,父类的方法表指向了子类实现方法,编译器会检查返回值是否向上兼容。注意,这里的向上转型必须是严格的继承关系,数据类型基本不存在通过继承向上转型的问题。比如int 与Integer 是非兼容返回类型,不会自动装箱。再比如,如果子类方法返回int,而父类方法返回long,虽然数据表示范围更大,但是它们之间没有继承关系。返回类型是Object 的方法,能够兼容任何对象,包括class、enum、interface 等类型。

(3)异常也要能向上转型成为父类的异常。异常分为checked 和unchecked 两种类型。如果父类抛出一个checked 异常,则子类只能抛出此异常或此异常的子类。而unchecked 异常不用显式地向上抛出,所以没有任何兼容问题。

(4)方法名、参数类型及个数必须严格一致。为了使编译器准确地判断是否是覆写行为,所有的覆写方法必须加@Override 注解。此时编译器会自动检查覆写方法签名是否一致,避免了覆写时因写错方法名或方法参数而导致覆写失败。例如,AbstractCollection 的clear 方法,当覆写此方法时,写成c1ear,注意是数字的1,这会导致定义了两个不同的方法。此外,@Override 注解还可以避免因权限控制可见范围导致的覆写失败。如图2-7 所示,Father 和Son 属于不同的包,它们的method() 方法无权限控制符修饰,是默认仅包内可见的。Father 的method 的方法在Son 中是不可见的。所以,Son 中定义的method 方法是一个“新方法”,如果加上@Override,则会提示:Method does not override method from its superclass。

4.jpg

图2-7 Father 和Son 的覆写关系

      综上所述,方法的覆写可以总结成容易记忆的口诀:“一大两小两同”。

  •       一大:子类的方法访问权限控制符只能相同或变大。
  •       两小:抛出异常和返回值只能变小,能够转型成父类对象。子类的返回值、抛出异常类型必须与父类的返回值、抛出异常类型存在继承关系。
  •       两同:方法名和参数必须完全相同。

      根据这个原则,再看一个编译和运行都正确的覆写示例代码:

class Father {

      protected Number doSomething(int a, Integer b, Object c) throws

             SQLException {

             System.out.println("Father's doSomething");

             return new Integer(7);

      }

}

class Son extends Father {

      /**

       * 1. 权限扩大,由protected 到public(一大)

       * 2. 返回值是父类的Number 的子类 (两小)

       * 3. 抛出异常是SQLException 的子类

       * 4. 方法名必须严格一致 (两同)

       * 5. 参数类型与个数必须严格一致

       * 6. 必须加@Override

       */

      @Override

      public Integer doSomething(int a, Integer b, Object c) throws

             SQLClientInfoException {

             if(a == 0) {

                    throw new SQLClientInfoException();

             }

             return new Integer(17);

      }

}

      覆写只能针对非静态、非final、非构造方法。由于静态方法属于类,如果父类和子类存在同名静态方法,那么两者都可以被正常调用。如果方法有final 修饰,则表示此方法不可被覆写。

      如果想在子类覆写的方法中调用父类方法,则可以使用super 关键字。在上述示例代码中,在Son 的doSomething 方法体里可以使用super.doSomething(a,b,c) 调用父类方法。如果与此同时在父类方法的代码中写一句this.doSomething(),会得出什么样的运行结果呢?

public class Father {

      protected void doSomething() {

             System.out.println("Father's doSomething");

             this.doSomething();

      }

      public static void main(String[] args) {

             Father father = new Son();

             father.doSomething();

      }

}

class Son extends Father {

      @Override

      public void doSomething() {

             System.out.println("Son's doSomething");

             super.doSomething();

      }

}

      在经过了一系列的父子方法循环调用后,JVM 崩溃了,发生了StackOverflowError,如图2-8所示。

5.jpg

图2-8 覆写产生的StackOverflowError

相关文章
|
21天前
|
监控 JavaScript 前端开发
《理解 WebSocket:Java Web 开发的实时通信技术》
【4月更文挑战第4天】WebSocket是Java Web实时通信的关键技术,提供双向持久连接,实现低延迟、高效率的实时交互。适用于聊天应用、在线游戏、数据监控和即时通知。开发涉及服务器端实现、客户端连接及数据协议定义,注意安全、错误处理、性能和兼容性。随着实时应用需求增加,WebSocket在Java Web开发中的地位将更加重要。
|
1月前
JavaWeb 开发之 ServletContext 的和使用
JavaWeb 开发之 ServletContext 的和使用
23 1
|
1天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
6天前
|
IDE Java 数据库连接
使用 Java 进行桌面应用开发
【4月更文挑战第19天】Java 是一款广泛应用于企业级、网络和桌面应用开发的编程语言。其跨平台特性使Java程序能在不同操作系统上运行,而JDK提供了开发所需工具和库。使用Swing等GUI库构建用户界面,结合JDBC进行数据库操作,Socket实现网络通信。虽然面临性能和用户体验的挑战,但通过优化和选用合适的IDE,Java仍能开发出高效稳定的桌面应用。
|
7天前
|
前端开发 Java Go
开发语言详解(python、java、Go(Golong)。。。。)
开发语言详解(python、java、Go(Golong)。。。。)
|
7天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
146 10
|
7天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
14天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
16 0
|
16天前
|
人工智能 小程序 Java
JAVA开发智慧学校系统源码+人脸电子班牌布局
智慧校园是通过利用物联网,大数据技术来改变师生和校园资源相互交互的方式,以便提高交互的明确性、灵活性和响应速度,从而实现智慧化服务和管理的校园模式。
|
19天前
|
安全 前端开发 Java
Java Web开发知识点学习总结
Java Web开发涉及Java基础、Servlet、JSP、数据库操作(SQL+JDBC)、MVC设计模式、Spring框架、Hibernate ORM、Web服务(SOAP&RESTful)、安全认证(HTTP Basic/Digest/OAuth)及性能优化(缓存、异步、负载均衡)。
17 3