前言

关于所有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. Web应用中解决问题的方案步骤?

    我们学一个东西,通常两个目的: - 为了解决现有的问题 - 为了解决将来可能会有的问题 所以,在学这些东西之前,先必须了解,它们是用来解决什么问题的. 在Web应用中,我们需要解决的问题可以归纳为三类 ...

  2. Python函数&异常处理

    1. 函数基础 1.1 参数和返回值 1.1.1 参数 位置参数.关键字参数 def my_func1(x, y, z): print(x+y+z, "计算结束") my_func ...

  3. mybatis配置和使用

    1,配置 MyBatis实现映射器的2种方式:XML文件形式和注解形式,下文主要是用xml形式,比较好维护 mybatis-config.xml文件: <?xml version="1 ...

  4. python基础内容整理(一)

    基本数据类型 字符串 String 字符串是不可变类型 字符串的分割: s.split(sep)以给定的sep为分隔符对s进行分割. In [4]: s = "hello world&quo ...

  5. Beta冲刺<4/10>

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺--第四天(05.22) 作业正文 如下 其他参考文献 ... B ...

  6. SSTI-服务端模板注入漏洞

      原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露.代码执行.GetShell ...

  7. vueX基础知识点笔记

    vuex是专门用来管理vue.js应用程序中状态的一个插件.他的作用是将应用中的所有状态都放在一起, 集中式来管理.需要声明的是,这里所说的状态指的是vue组件中data里面的属性.简单的来说, 它就 ...

  8. Ubuntu18.04 IP配置问题

    18.04 LTS 提供了通过 netplan.io 轻松配置网络连接 参考 Ubuntu18.04 发行release cn.ubuntu.com/server

  9. Java中List集合去除重复数据的方法1

    1. 循环list中的所有元素然后删除重复 public   static   List  removeDuplicate(List list)  {         for  ( int  i  = ...

  10. linux下安装jdk并设置环境变量

      首先去官网下载jdk安装包 我这里下载的是jdk7,因为jdk8之后做了很大的改动,所以现在常用的还是jdk7.下载地址:www.oracle.com/technetwork/cn/java/ja ...