前言

关于所有Java系列文章面向有一定基础的童鞋,所写每一篇希望有一定含金量,有些内容可能会从Java整个语法全局考虑穿插后续要讲解的内容以成系统,若不理解,请看完后再学习。上一节我们讲解完了final关键字,本节我们继续来对比讲解Java和C#中的重写,二者语言的重写区分非常清晰,Java子类中基类方法签名一样或通过注解@Override显式声明,C#中基类通过virtual关键字修饰,子类通过ovveride关键字表示重写,具体细节请往下看。

重写

既然是重写必然就涉及到继承,我们首先来看看Java中的重写是怎样的呢?如下:

public class Main {
public void f() {
System.out.println("Main.f");
} public static void main(String[] args) {
Main main = new Sub();
main.f();
}
} class Sub extends Main {
public void f() {
System.out.println("Sub.f");
}
}

当调用基类的f方法时,此时发现已被子类所重写,所以如上正常打印出Sub.f,要是我们将上述基类中方法修改为私有的呢

可能我们期待输出子类中的打印结果,但是修改为私有后,说明此时对子类不再可见,也就相当于使用了final,在这种情况下,子类中方法则是一个全新的方法,很显然说明:只有非私有方法才可以被重写。对于这种情况下编译不会报错, 但是也不会按照我们所期望的结果来执行,所以建议对于基类中的私有方法命名为和子类不同的名字,为了让重写一目了然或更加明确,在1.5版本发布了注解功能,我们可以通过注解来显式声明要重写基类方法,若基类为私有,此时通过注解则会编译报错,因为找不到要重写的方法,这种体验更加友好,比如如下:

public class Main {
private void f() {
System.out.println("Main.f");
} public static void main(String[] args) {
Main main = new Sub();
main.f();
}
} class Sub extends Main { //编译错误,未找到基类(超类)中要重写的方法
@Override
public void f() {
System.out.println("Sub.f");
}
}

举一反三,我们来思考一个问题,是不是方法签名一致,子类就可以重写基类方法呢?很显然不是,若是静态方法,必然不能被重写,如果通过注解@Override声明那么必然编译报错,否则调用基类方法,对于接口中的静态方法同理。所以还是建议使用注解来声明重写。那么为什么通过注解显式声明重写基类方法或通过关键字final修饰方法就会在编译阶段报错呢?因为它们在编译阶段就完成了静态绑定,而不是运行时动态绑定。问题又来了,上述我们讲解到若在子类中不通过注解显式声明重写,同时在基类中方法私有,此时一定可以编译通过(上述已演示),并且会调用基类方法并打印出结果,事实情况一定是这样吗?很显然也不是如此,如下示例:

class Super {
private void f() {
System.out.println("Super.f");
}
} class Derived extends Super {
public void f() {
System.out.println("Derived.f");
}
public static void main(String[] args) {
Super aSuper = new Derived(); //编译报错(因为基类方法私有)
aSuper.f();
}
}

初一看,这不是一样么,其实是不一样,上述可以那是因为调用方在基类里面,当然可以调用内部的私有方法,如上情况只对基类内部私有, 当然也就不能调用,这里就又引出一个问题,是不是声明为基类的私有方法,子类就无法进行重写呢?根据我们上述打印的结果来看,理论上不可行,事实情况是可以的,通过内部类(后续会出文章详细讲解)来实现。

class Main {

    private void f() {
System.out.println("Main.f");
} class Inner extends Main {
private void f() {
System.out.println("Inner.f");
}
} public static void main(String args[]) { //内部类实例必须通过外部类实例创建
Main outer = new Main();
Inner inner = outer.new Inner(); //内部类可以在内部访问外部的所有成员(包括私有)
inner.f(); // 调用外部类方法
outer = inner;
outer.f();
}
}

C#若明确需要重写,那么基类方法声明为虚有的virtual,子类通过ovverride关键字修饰方法达到重写目的,若没有这两个关键字,和Java中一样只是方法签名一致,那么说明编译器会提醒是否通过new关键字来表明隐藏基类的方法

class Program
{
public void F()
{
Console.WriteLine("Main.f");
} static void Main(string[] args)
{
Program program = new Sub();
program.F(); Console.ReadKey();
}
} class Sub : Program
{
public new void F()
{
Console.WriteLine("Sub.f");
}
}

总结

Java和C#中的重写区分度非常清晰,Java中只要方法签名一致就可以达到重写,不过建议通过注解方法来显式声明重写以免引起不必要的问题,同时,即使基类方法私有,我们也可借助于内部类来实现重写。

Java入门系列之重写的更多相关文章

  1. Java入门系列-26-JDBC

    认识 JDBC JDBC (Java DataBase Connectivity) 是 Java 数据库连接技术的简称,用于连接常用数据库. Sun 公司提供了 JDBC API ,供程序员调用接口和 ...

  2. Java入门系列-19-泛型集合

    集合 如何存储每天的新闻信息?每天的新闻总数是不固定的,太少浪费空间,太多空间不足. 如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,可以使用Java集合框架. Java 集合框架提 ...

  3. Java入门系列之类继承、抽象类、接口(五)

    前言 C#和Java关于类.抽象类.接口使用方式基本相似,只是对应关键字使用不同罢了,本节呢,我们只是对照C#和Java中关于这三个概念在具体使用时,看看有哪些不一样的地方. 类继承 C#和Java在 ...

  4. Java入门系列之hashCode和equals(十二)

    前言 前面两节内容我们详细讲解了Hashtable算法和源码分析,针对散列函数始终逃脱不掉hashCode的计算,本节我们将详细分析hashCode和equals,同时您将会看到本节内容是从<E ...

  5. Java入门系列之字符串创建方式、判断相等(一)

    前言 陆续从0开始学习Java出于多掌握一门语言以后的路也会更宽,.NET和Java兼顾,虽然路还很艰难,但事在人为.由于Java和C#语法相似,所以关于一些很基础的内容不会再重头讲,Java系列中所 ...

  6. Java入门系列(九)Java API

    String,StringBuilder,StringBuffer三者的区别 1.首先说运行速度,或者说是执行速度 在这方面运行速度快慢为:StringBuilder > StringBuffe ...

  7. Java入门系列(七)Java 集合框架(JCF, Java Collections Framework)

    Java 集合概述 List.Set.Map可以看做集合的三大类 java集合就像一个容器,可以将多个对象的引用丢进该容器中. Collection和Map是java集合的根接口. List List ...

  8. Java入门系列(五)JVM内存模型

    概述 根据<Java 虚拟机规范>中的说法,Java 虚拟机的内存结构可以分为公有和私有两部分. 公有指的是所有线程都共享的部分,指的是 Java 堆.方法区.常量池. 私有指的是每个线程 ...

  9. Java入门系列(四)内部类

    为什么需要内部类? 真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题没有多继承.实际上,C++的多继承设计起来很复杂,而java通过内部类加 ...

随机推荐

  1. C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!(应该是全网第一篇)

    一:背景 1. 讲故事 好消息,.NET 5.0 终于在2020年6月10日发布了第五个预览版,眼尖的同学一定看到了在这个版本中终于支持了 C# 9.0,此处有掌声,太好了!!! .Net5官方链接 ...

  2. is ==小数据池编码解码

    ==      比较      比较的是两边的值    is      比较      比较的是内存地址   判断两个东西指向的是不是同一个对象         取内存地址 id() 小数据池     ...

  3. <WP8开发学习笔记>获取手机的常用型号(如Lumia920,而非RM-822)

    之前WP7时代可以用API获得WP手机的型号如lumia510,但是到了WP8后用APi只能获得硬件版本号了如RM-822,这种型号可以让我们更详细的了解具体的硬件版本,比如国行和港行,设备版本号不一 ...

  4. C# Winform界面不能适配高DPI的解决方法

    1. 将 Form 的 AutoScaleMode 属性设置为 DPI: 2. 在Program.cs中修改代码 class Program { [STAThread] static void Mai ...

  5. asp.net Mvc 路由详解,非常详细.

    关于路由的理解 为什么要定义路由?路由的定义在开发中的工作量非常小,但是非常重要,因为任何请求都离不开路由. 各个电商网站的 URL 使用非常灵活,都离不开路由的定义,请大家参考几家电商的 URL 如 ...

  6. Shiro反序列化复现

    Shiro反序列化复现 ——————环境准备—————— 目标靶机:10.11.10.108 //docker环境 攻击机ip:无所谓 vpsip:192.168.14.222 //和靶机ip可通 1 ...

  7. 关于Ubuntu系统忘记密码的解决方法合集

    昨天有台机器的Ubuntu系统密码出了问题,一直提示错误.由于里面的数据比较重要,不建议重装系统,所以百度了一会,最终解决了忘记密码问题.整理了一个大合集分享出来. 第一种:参考教程如下       ...

  8. CRC16冗余循环检测计算器-好用。modbus RTU

    开始使用 http://cht.nahua.com.tw/index.php?url=http://cht.nahua.com.tw/software/crc16/&key=Modbus,%2 ...

  9. WeChair项目Beta冲刺(6/10)

    团队项目进行情况 1.昨日进展    Beta冲刺第六天 昨日进展: 前后端并行开发,项目按照计划有条不絮进行 2.今日安排 前端:扫码占座功能和预约功能并行开发 后端:扫码占座后端逻辑开发,编码预约 ...

  10. IDEA+Maven+Tomcat构建Web项目的三种方法

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 本文将介绍三种方 ...