Java方法继承、方法重载、方法覆盖小总结

 override为覆盖又叫重写,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:
   1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载.
   2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。
   3、重写的方法的返回值必须和被重写的方法的返回一致;
   4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;
   5、被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。

   6、静态方法不能被重写为非静态的方法(会编译出错)。

     overload对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:
    1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int, float), 但是不能为fun(int, int));
    2、不能通过访问权限、返回类型、抛出的异常进行重载;
    3、方法的异常类型和数目不会对重载造成影响;
    下面是对override和overload的测试程序,其中注释中的内容都是会产生编译错误的代码,我们将注释去掉,看看在编译时会产生什么效果。

// 对overload测试的文件:OverloadTest.java

public class OverloadTest {

// 下面几个方法用来验证可以通过定义不同的参数类型和参数的数目进行方法重载。
public void fun(){
   System.out.println(
"method fun in OverloadTest, no parameter" );
}

public void fun(float f) {
   System.out.println(
"method fun in OverloadTest, parameter type: float" );
}
    
public void fun(int i){
   System.out.println(
"method fun in OverloadTest, parameter type: int" );
}

public void fun(int i1, int i2) {
   System.out.println(
"method fun in OverloadTest, parameter type: int, int" );
}
    
// 下面的两个方法用来验证可以通过定义不同的参数顺序进行方法重载。
// 需要注意:这里的参数肯定不是相同的类型,否则的顺序的先后就毫无意义。
public void fun1(int i, float f) {
   System.out.println(
"method fun1 in OverloadTest, sequence of parameters is: int, float" );
}

public void fun1(float f, int i) {
   System.out.println(
"method fun1 in OverloadTest, sequence of parameters is: float, int" );
}

// 下面的两个方法用来验证方法抛出的异常对于重载的影响.
// 无论是异常的类型还是异常的个数都不会对重载造成任何的影响。
public void fun2() throws TestException {
   System.out.println(
"fun2 in OverloadTest, exception: TestException" );
}

public void fun2(int i) throws TestException, TestException1 {
   System.out.println(
"fun2 in OverloadTest, exception: TestException, TestException1" );
}

public void fun2(float f) throws Exception {
   System.out.println(
"fun2 in OverloadTest, exception: Exception" );
}

// 不能通过抛出的异常类型来重载fun方法。
//public void fun(int i) throws Exception {
// System.out.println("method fun in OverloadTest, parameter type: int, exception: Exception");
//}

    
// ? 不能通过返回值重载fun方法。
//public boolean fun(int i) throws Exception {
// System.out.println("method fun in OverloadTest, parameter type: int, exception: Exception, return: boolean");
// return true;
//}

private void fun3() { }

// 不能通过不同的访问权限进行重载
public void fun3() { }

public static void main(String [] args) {
  
// 这里只是定义了OverloadTest的实例,所以test不会调用
  
// OverloadTest1中的方法。
   OverloadTest test = new OverloadTest1();
  
// 这里定义了OverloadTest1的实例,因为OverloadTest1是OverloadTest
  
// 的子类,所以test1会调用OverloadTest中的方法。
   OverloadTest1 test1 = new OverloadTest1();

  try {
   int i = 1, j = 2, m = 3;

   
// 这里不会调用OverloadTest1的fun方法
   
// test.fun(i, m, j);
    test1.fun(i, j, m);
    test1.fun();
   
// 这个调用不会执行,因为fun3()在OverloadTest中访问权限是priavte
   
//test1.fun3();
    test1.fun3(i);
   } catch (Exception e) { }
}
}
class OverloadTest1 extends OverloadTest{
// 在子类中重载fun
public void fun(int i, int m, int n) {
   System.out.println(
"Overload fun1 in OverloadTest1, parameter type: int, int, int" );
}

// 这个不是对父类中方法的重载,只是一个新的方法。
public void fun3(int i) {
   System.out.println(
"fun2 in OverloadTest1" );
}
}

// 对override测试的文件:OverrideTest.java
public class OverrideTest {
public void fun() throws TestException {
   System.out.println(
"method fun in OverrideTest" );
}

private void fun1() {
   System.out.println(
"method fun1 in OverrideTest" );
}

public static void main(String [] args) {
   OverrideTest test = new OverrideTest1();
  try {
     test.fun();
     test.fun1();
   } catch (Exception e) { }
}
}

class OverrideTest1 extends OverrideTest{
// 以下正常Override
public void fun() throws TestException2 {
   System.out.println(
"fun in OverrideTest1" );
}

// 不能Override父类中的方法,因为它定义了不同的异常类型和
// 返回值。
//public int fun() throws TestException1 {
// System.out.println("method fun in Test");
// return 1;
//}
    
// 不能Override父类中的方法,因为它抛出了比父类中非法范围
// 更大的异常。
//public void fun() throws Exception {
// System.out.println("fun in OverrideTest1");
//}

// 这个方法并没有Override父类中的fun1方法,因为这个方法在
// 父类是private类型,所以这里只是相当于定义了一个新方法。
public void fun1() {
   System.out.println(
"method fun1 in Test" );
}
}

class TestException extends Exception{
public TestException(String msg) {
  super (msg);
}
}

class TestException1 extends TestException {
public TestException1(String msg) {
  super (msg);
}
}

class TestException2 extends TestException {
public TestException2(String msg) {
  super (msg);
}
}

 

 

1、方法继承:利用extends关键字一个方法继承另一个方法,而且只能直接继承一个类。
•当Sub类和Base类在同一个包时Sub类继承Base类中的public/protected/默认级别的变量个方法
•在不同包时继承public/protected级别的变量和方法。
2、方法重载:如果有两个方法的方法名相同,但参数不一致,哪么可以说一个方法是另一个方法的重载。
•方法名相同
•方法的参数类型,个数顺序至少有一项不同
•方法的返回类型可以不相同
•方法的修饰符可以不相同
•main方法也可以被重载
3、方法覆盖:如果在子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。
•子类的方法名称返回类型及参数签名 必须与父类的一致
•子类方法不能缩小父类方法的访问权限
• 子类方法不能抛出比父类方法更多的异常
•方法覆盖只存在于子类和父类之间,同一个类中只能重载
•父类的静态方法不能被子类覆盖为非静态方法
•子类可以定义于父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法(满足覆盖约束),   
•            而且Java虚拟机把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。
•父类的非静态方法不能被子类覆盖为静态方法
•父类的私有方法不能被子类覆盖
•父类的抽象方法可以被子类通过两种途径覆盖(即实现和覆盖)(P169)
•父类的非抽象方法可以被覆盖为抽象方法
4、Super关键字:super和this关键字都可以用来覆盖Java语言的默认作用域,使被屏蔽的方法或变量变为可见(三种情况下的不可见P171)。
•父类的成员变量和方法为private使用super访问编译出错
•在类的构造方法种,通过super语句调用这个类的父类的构造方法
•在子类种访问父类的被屏蔽的方法和属性
•只能在构造方法或实例方法内使用super关键字,而在静态方法和静态代码块内不能使用super
5、多态:
•对于一个引用类型的变量,Java编译器按照它的声明的类型来处理
•对于一个引用类型的变量,运行时Java虚拟机按照它的实际引用的对象来处理
•运行时环境中,通过引用类型变量来访问所引用对象的方法和属性时,Java虚拟机采用以下绑定规则
            1)实例方法与引用变量实际引用的对象的方法绑定,属于动态绑定
            2)静态方法与引用变量所声明的类型的方法绑定,属于静态绑定
            3)成员变量(包括静态和实例变量)与引用变量所声明的类型的成员变量绑定,属于静态绑定                                                                                                              


6、继承的利弊和使用原则:
•集成数的层次不可太多
•集成数的上层为抽象层
            (1)定义了下层子类都用友的相同属性和方法,并且尽可能默认实现,从而提高重用性
            (2)代表系统的接口,描述系统所能提供的服务
•继承关系最大的弱点:打破封装
•精心设计专门用于被继承的类
            (1)对这些类必须提供良好的文档说明
            (2)尽可能的封装父类的实现细节,把代表时间细节的属性和方法定义为private类型
            (3)如果某些实现细节必须被子类访问,定义为protected类型
            (4)把不允许子类覆盖的方法定义为final类型
            (5)父类的构造方法不允许调用可被子类覆盖的方法
            (6)如果某些类不是专门为了继承而设计,那么随意继承它是不安全的

 

深入理解java多态性

昨天看到一个关于多态性的帖子,参考了回帖者的理解,加入了一些自己的看法,整理出来供大家参考,不一定完全正确,欢迎大家批评指正。

(一)相关类

class  A  {
         
public  String show(D obj) {
                
return  ( " A and D " );
         }
 
         
public  String show(A obj) {
                
return  ( " A and A " );
         }
 
}
 
class  B  extends  A {
         
public  String show(B obj) {
                
return  ( " B and B " );
         }

         
public  String show(A obj) {
                
return  ( " B and A " );
         }
 
}

class  C  extends  B {}  
class  D  extends  B {}  

(二)问题:以下输出结果是什么?

        A a1  =   new  A();
        A a2 
=   new  B();
        B b =
new  B();
        C c 
=   new  C(); 
        D d 
=   new  D(); 
        
System.out.println(a1.show(b));   ①
        System.out.println(a1.show(c));   ②
        System.out.println(a1.show(d));   ③
        System.out.println(a2.show(b));   ④
        System.out.println(a2.show(c));   ⑤
        System.out.println(a2.show(d));   ⑥
        System.out.println(b.show(b));     ⑦
        System.out.println(b.show(c));     ⑧
        System.out.println(b.show(d));     ⑨    

(三)答案

              ①   A and A
              ②   A and A
              ③   A and D
              ④   B and A
              ⑤   B and A
              ⑥   A and D
              ⑦   B and B
              ⑧   B and B
              ⑨   A and D

(四)分析

        ①②③比较好理解,一般不会出错。④⑤就有点糊涂了,为什么输出的不是"B and B”呢?!!先来回顾一下多态性。

        运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。

        方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现, 重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方 法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。方法 的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载 Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方 法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。

        当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了。)

        好了,先温习到这里,言归正传!实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、 super.show((super)O)。让我们来看看它是怎么工作的。

        比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是 a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。

        再比如⑧,b.show(c),b是一个引用变量,类型为B,则this为b,c是C的一个实例,于是它到类B找show(C obj)方法,没有找到,转而到B的超类A里面找,A里面也没有,因此也转到第三优先级this.show((super)O),this为b,O为C, (super)O即(super)C即B,因此它到B里面找show(B obj)方法,找到了,由于b引用的是类B的一个对象,因此直接锁定到类B的show(B obj),输出为"B and B”。

        按照上面的方法,可以正确得到其他的结果。

        问题还要继续,现在我们再来看上面的分析过程是怎么体现出蓝色字体那句话的内涵的。它说:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用 变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。还是拿a2.show(b)来说吧。

        a2是一个引用变量,类型为A,它引用的是B的一个对象,因此这句话的意思是由B来决定调用的是哪个方法。因此应该调用B的show(B obj)从而输出"B and B”才对。但是为什么跟前面的分析得到的结果不相符呢?!问题在于我们不要忽略了蓝色字体的后半部分,那里特别指明:这个被调用的方法必须是在超类中定义 过的,也就是被子类覆盖的方法。B里面的show(B obj)在超类A中有定义吗?没有!那就更谈不上被覆盖了。实际上这句话隐藏了一条信息:它仍然是按照方法调用的优先级来确定的。它在类A中找到了 show(A obj),如果子类B没有覆盖show(A obj)方法,那么它就调用A的show(A obj)(由于B继承A,虽然没有覆盖这个方法,但从超类A那里继承了这个方法,从某种意义上说,还是由B确定调用的方法,只是方法是在A中实现而已); 现在子类B覆盖了show(A obj),因此它最终锁定到B的show(A obj)。这就是那句话的意义所在。

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值