zoukankan      html  css  js  c++  java
  • 【细说Java】Java的重写与隐藏

      重写与隐藏,有些书上或介绍上可能名称不一样,但都大差不差。以前只了解重写,隐藏也听说过,但没有详细了解过,趁现在,整理一下这两方面的内容吧。

      首先,先说一下概念方面的东西。

    重写

      重写:子类继承了父类,在子类中声明了与父类具有相同的方法名称与参数列表,并且具有相同的返回类型(或者子类的返回类型是父类的子类型)的实例方法,那么就说子类重写了父类中的同名方法(但父类的方法必须在子类中可见),而重写则是实现多态的前提;子类可以向上转型为父类类型,这样,当通过父类的引用来调用重写的方法时,就可以表现出子类的行为。

    1. 如果子类重写了父类的方法,则有如下要求:

    (1)子类与父类的方法必须都是实例方法,即都不是静态方法;

    (2)子类与父类的方法需要具有相同的方法名称,参数列表,并且子类的返回类型与父类相同,或者是父类的子类型;

    public class JavaTest extends Super {
        @Override
        public Integer print() {
            return null;
        }
    }
    
    class Super {
        public Object print() {
            return null;
        }
    }

    由于Integer是Object的子类,因此满足重写的条件。

    (3)子类方法的访问权限不能小于父类的访问权限(可以有相同的访问权限);

    public class JavaTest extends Super {
        @Override
        protected Integer print() {
            return null;
        }
    }
    
    class Super {
        public Object print() {
            return null;
        }
    }

      访问权限由高到低依次为public,protected,包的访问权限,private。如果子类方法的访问权限低于父类的访问权限,那么编译器将给出错误信息。如上面的代码,编译器就会报错。

    (4)子类方法不能比父类方法抛出更多的已检测异常(也称编译时异常),即子类方法抛出的异常必须是父类的子集或和父类是相同的异常;

    public class JavaTest extends Super {
        @Override
        public Integer print() throws IOException {
            return null;
        }
    }
    
    class Super {
        public Object print() throws FileNotFoundException{
            return null;
        }
    }

    如上面代码,由于父类抛出的FileNotFoundException异常是子类抛出的IOException异常的子类,这是不允许的。

    注意:如果是未检测异常(运行时异常),则不受此限制,子类也不用显示的使用throws抛出。

    (5)父类的方法在子类中必须可见(子类可以使用super来访问父类中被重写的方法);

    public class JavaTest extends Super {
        
        public Integer print() {
            return null;
        }
    }
    
    class Super {
        private Object print()  {
            return null;
        }
    }

    如上面代码,父类中的方法是private类型的,在子类是无法访问的,但这不属于重写;

    2.重写方法的调用

      当子类向上转型为父类类型A,通过父类的引用来调用某个方法时,Java编译器会先检查当前类有没有实现同名方法,如果有,执行当前类的方法;若没有,则去执行父类的同名方法;若父类没有,则再检查父类的父类有没有实现这个方法,以此类推,直到父类A为止。

    public class JavaTest {
        public static void main(String[] args) {
            SuperClass test = new ThatClass();
            test.print1();
            test.print2();
            test.print3();
        }
    }
    
    class ThatClass extends ThisClass {
        public void print1() {
            System.out.println("ThatClass print1()");
        }
    }
    
    class ThisClass extends SuperClass {
        public void print1() {
            System.out.println("ThisClass print1()");
        }
    
        public void print2() {
            System.out.println("ThisClass print2()");
        }
    }
    
    class SuperClass {
        public void print1() {
            System.out.println("SuperClass print1()");
        }
    
        public void print2() {
            System.out.println("SuperClass print2()");
        }
    
        public void print3() {
            System.out.println("SuperClass print3()");
        }
    }

    执行结果如下:

    ThatClass print1()
    ThisClass print2()
    SuperClass print3()


      还有一种情况,当使用父类的引用调用某个方法(如A方法)时,如果A方法中又调用了另一个父类中的方法(假设为B方法),那么依然按照上述的原则来查找B方法(由子类到父类的查找顺序);

    public class JavaTest {
        public static void main(String[] args) {
            SuperClass test = new ThatClass();
            test.print3();
        }
    }
    
    class ThatClass extends ThisClass {
        public void print1() {
            System.out.println("ThatClass print1()");
        }
    }
    
    class ThisClass extends SuperClass {
        public void print1() {
            System.out.println("ThisClass print1()");
        }
    
        public void print2() {
            System.out.println("ThisClass print2()");
        }
    }
    
    class SuperClass {
        public void print1() {
            System.out.println("SuperClass print1()");
        }
    
        public void print2() {
            System.out.println("SuperClass print2()");
        }
    
        public void print3() {
            System.out.println("SuperClass print3()");
            print2();
            print1();
        }
    }

    打印结果如下:

    SuperClass print3()
    ThisClass print2()
    ThatClass print1()

      简单分析下,父类引用调用print3方法,由于子类都没有重写,所以调用父类的方法,而父类print3方法又调用了父类的其他两个方法,并且这两个方法在子类中均可见,因此按照由子类到父类的查找顺序进行。由于print1方法在子类中被重写,所以调用子类的print1方法,而print2在子类中没有被重写,而是在子类的直接父类中被重写,所以调用子类的直接父类的print2方法。

      注意:JDK5.0之后可以使用@Override注解来标明要重写的方法。如果没有重写,编译器就会报错,如下,编译器就会报错。

    class ThatClass extends ThisClass {
        @Override
        public void print1() {
            System.out.println("ThatClass print1()");
        }
    }
    
    class ThisClass  {
        private void print1() {
            System.out.println("ThisClass print1()");
        }
    }

    隐藏

    1. 什么是隐藏呢?

      隐藏其实和重写差不多,除了隐藏要求所有的方法都是静态方法,而重写是实例方法,其他的要求都是一样的。

      其实隐藏与重写最本质的区别是,重写是多态的前提,利用重写可以实现多态,而隐藏则无法实现多态。

    下面通过一个简单的例子来了解一下:

    public class JavaTest {
        public static void main(String[] args) {
            SuperClass superTest = new ThisClass();
            superTest.hiding();
            superTest.override();
            System.out.println("===================");
            ThisClass thisTest = (ThisClass)superTest;
            thisTest.hiding();
            thisTest.override();
        }
    }
    
    class ThisClass extends SuperClass {
        public void override() {
            System.out.println("ThisClass void override()");
        }
    
        public static void hiding() {
            System.out.println("ThisClass static void hiding()");
        }
    }
    
    class SuperClass {
        public void override() {
            System.out.println("SuperClass void override()");
        }
    
        public static void hiding() {
            System.out.println("SuperClass static void hiding()");
        }
    }

    结果如下:

    SuperClass static void hiding()
    ThisClass void override()
    ===================
    ThisClass static void hiding()
    ThisClass void override()

    通过以上例子,我们发现了重写与隐藏的根本区别:

    (1)如果子类重写了父类的方法,则通过父类的引用调用的是子类的方法;

    (2)如果子类隐藏了父类的方法,则通过父类的引用调用的仍然是父类的方法;

    (3)这表明了实例方法的调用是动态绑定的,是在运行的时候根据对象真正的类型来决定调用哪个方法,而静态方法的调用是静态绑定的,是根据引用的类型来决定调用哪个方法。

    2. 成员变量的隐藏

      当子类继承了父类,在子类中声明了与父类具有相同名称的成员变量时,那么就说子类隐藏了父类中的成员变量。子类不能重写父类中的成员变量,只能隐藏。

      所以,不管是静态变量还是实例变量,打印的是父类还是子类的成员变量取决于引用的类型,而不是对象的类型,因为子类隐藏了父类的成员变量。

    public class JavaTest {
        public static void main(String[] args) {
            SuperClass superTest = new ThisClass();
            System.out.println(superTest.min);
            System.out.println(superTest.max);
            System.out.println("===================");
            ThisClass thisTest = (ThisClass)superTest;
            System.out.println(thisTest.min);
            System.out.println(thisTest.max);
        }
    }
    
    class ThisClass extends SuperClass {
        public static int max = 8;
        public int min = 7;
    }
    
    class SuperClass {
        public static int max = 4;
        public int min = 5;
    }

    结果如下:

    5
    4
    ===================
    7
    8

    3. 成员变量的继承

    如果子类继承了父类的实例变量,子类将与父类共享该实例变量,在子类中修改将影响到父类,反之亦然。

    public class JavaTest {
        public static void main(String[] args) {
            ThisClass superTest = new ThisClass();
            System.out.println("修改之前:");
            superTest.printMin();
            System.out.println("修改子类之后:");
            superTest.setMin(4);
            superTest.printMin();
            System.out.println("修改父类之后:");
            superTest.setSuperMin(7);
            superTest.printMin();
        }
    }
    
    class ThisClass extends SuperClass {
    
        public void setMin(int value) {
            min = value;
        }
    
        public void setSuperMin(int value) {
            super.min = value;
        }
    
        public void printMin() {
            System.out.println("父类的min: " + super.min);
            System.out.println("子类的min: " + min);
        }
    }
    
    class SuperClass {
        public int min = 5;
    }

    结果如下:

    修改之前:
    父类的min: 5
    子类的min: 5
    修改子类之后:
    父类的min: 4
    子类的min: 4
    修改父类之后:
    父类的min: 7
    子类的min: 7

    至于静态成员变量就不举例了,静态成员变量和实例变量类似,但实例变量是基于对象的,即每一个对象中子类与父类都共享同一份实例对象的拷贝,而不同的对象互不干扰。而静态成员变量是基于类的,这将会导致所有子类与父类都共享同一个静态变量,无论创建多少对象,都是同一份拷贝。

    总结:Java的重写与隐藏其实差不多,最根本的区别就是重写可以实现多态,而隐藏则无法实现多态。

    参考自:《细说Java》

  • 相关阅读:
    hdu 4027 Can you answer these queries? 线段树
    ZOJ1610 Count the Colors 线段树
    poj 2528 Mayor's posters 离散化 线段树
    hdu 1599 find the mincost route floyd求最小环
    POJ 2686 Traveling by Stagecoach 状压DP
    POJ 1990 MooFest 树状数组
    POJ 2955 Brackets 区间DP
    lightoj 1422 Halloween Costumes 区间DP
    模板 有源汇上下界最小流 loj117
    模板 有源汇上下界最大流 loj116
  • 原文地址:https://www.cnblogs.com/xiaozhang2014/p/5323213.html
Copyright © 2011-2022 走看看