Java入门系列之重写

前言

关于所有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中只要方法签名一致就可以达到重写,不过建议通过注解方法来显式声明重写以免引起不必要的问题,同时,即使基类方法私有,我们也可借助于内部类来实现重写。

posted @ 2020-07-11 00:15  Jeffcky  阅读(538)  评论(0编辑  收藏  举报