• java 关于多态的一点总结


    http://www.cnblogs.com/wenruo/p/5352683.html

    一直不是很理解多态,今天看了两遍《think in java》第八章,试着总结一下。

    多态的本质就是动态绑定。

    将一个方法调用同一个方法主体关联起来叫做绑定。java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。

    所以先不考虑static和final,看看多态是怎么样的。

    记得c++中父类指针指向子类对象时默认是使用父类函数的,除非父类中函数是虚函数。

    而在java不同。一个父类的引用指向子类的对象,如果子类重写了父类的方法,那么默认是调用子类的方法。

    重写(override)和重载(overload)的区别

    听说面试经常考这个?但是完全不一样嘛。重载要求是方法名相同,参数列表不同,而且和继承无关。但是重写的要求是子类中方法与父类中方法参数列表完全相同,返回值也要相同,或者子类返回值为父类返回值的子类型。重写可以通过@Override标注出来,防止出错,也使代码结构清晰。

    用父类引用指向子类对象,会“缩小”接口。一个父类的引用,就算指向了子类的对象,那也只能使用父类有的方法,只不过有一些被重写了而已。一个应该使用父类的方法可以传参为一个子类,称作向上转型(Upcast)。很好理解,因为一个子类拥有父类所有的接口,可以满足方法所有的需求。

    多态的好处:

    现在来看窝就理解了两点

    1.简化代码。写一个参数为父类的方法可以代替为很多子类分别写一个方法。

    2.可扩展性。只要写一个父类,就可以随时扩充一个子类。而原来指向父类的方法不需要改变就可以用于新子类。这很重要。

    借用别人博客的例子 同时考察了重写和重载

     1 class A  {  
     2     public String show(D obj) {  
     3            return ("A and D");  
     4     }   
     5     public String show(A obj) {  
     6            return ("A and A");  
     7     }   
     8 }   
     9 class B extends A {  
    10     public String show(B obj) {  
    11            return ("B and B");  
    12     }  
    13     public String show(A obj) {  
    14            return ("B and A");  
    15     }   
    16 }  
    17 class C extends B {}   
    18 class D extends B {}
    19 
    20 public class Main {
    21     public static void main(String[] args) {
    22         A a1 = new A();  
    23         A a2 = new B();  
    24         B b = new B();  
    25         C c = new C();   
    26         D d = new D();
    27         System.out.println(a1.show(b)); // 1
    28         System.out.println(a1.show(c)); // 2
    29         System.out.println(a1.show(d)); // 3
    30         System.out.println(a2.show(b)); // 4
    31         System.out.println(a2.show(c)); // 5
    32         System.out.println(a2.show(d)); // 6
    33         System.out.println(b.show(b));  // 7
    34         System.out.println(b.show(c));  // 8
    35         System.out.println(b.show(d));  // 9
    36     }
    37 }

    1~3,都是A类引用指向A类对象,那么没有重写多态什么的,直接根据参数选择方法,很简单。

    4~6,A类引用指向B类对象,会涉及多态。对于A类引用来说,只会有两个方法,一个是show(D),一个是show(A)。show(D)没有被重写,输出"A and D",show(A)被重写输出"B and A"。然后根据参数选择方法就好了。

    7~9,B类引用指向B类对象,不涉及多态。B一共有3个方法,继承自父类的show(D)输出"A and D",继承自父类又被重写的show(A)输出"B and A",子类中添加的show(B)输出"B and B"。然后根据参数选择。

    输出:

    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

    再写一个我觉得需要注意的

     1 class F {
     2     public void f1() {
     3         System.out.println("f1 in F");
     4         f2();
     5     }
     6     public void f2() {
     7         System.out.println("f2 in F");
     8     }
     9 }
    10 
    11 class S extends F {
    12     public void f1() {
    13         System.out.println("f1 in S");
    14         f2();
    15     }
    16     public void f2() {
    17         System.out.println("f2 in S");
    18     }
    19 }
    20 
    21 public class Main {
    22     public static void main(String[] args) {
    23         F f = new S();
    24         f.f1();
    25     }
    26 }

    输出

    f1 in S
    f2 in S

     1 class F {
     2     public void f1() {
     3         System.out.println("f1 in F");
     4         f2();
     5     }
     6     public void f2() {
     7         System.out.println("f2 in F");
     8     }
     9 }
    10 
    11 class S extends F {
    12     public void f2() {
    13         System.out.println("f2 in S");
    14     }
    15 }
    16 
    17 public class Main {
    18     public static void main(String[] args) {
    19         F f = new S();
    20         f.f1();
    21     }
    22 }

    输出

    f1 in F
    f2 in S

     1 class F {
     2     public void f1() {
     3         System.out.println("f1 in F");
     4     }
     5 }
     6 
     7 class S extends F {
     8     public void f1() {
     9         System.out.println("f1 in S");
    10         f2();
    11     }
    12     public void f2() {
    13         System.out.println("f2 in S");
    14     }
    15 }
    16 
    17 public class Main {
    18     public static void main(String[] args) {
    19         F f = new S();
    20         f.f1();
    21     }
    22 }

    输出

    f1 in S
    f2 in S

     1 class F {
     2     public void f1() {
     3         System.out.println("f1 in F");
     4         f2();
     5     }
     6     public void f2() {
     7         System.out.println("f2 in F");
     8     }
     9 }
    10 
    11 class S extends F {
    12     public void f1() {
    13         System.out.println("f1 in S");
    14         f2();
    15     }
    16 }
    17 
    18 public class Main {
    19     public static void main(String[] args) {
    20         F f = new S();
    21         f.f1();
    22     }
    23 }

    输出

    f1 in S
    f2 in F

    主要注意一下第二个吧,可以看出即使是父类的方法中调用的方法也会被重写。

    然后考虑一下final

    考虑一下下面的代码输出什么

     1 class F {
     2     final void f() {
     3         System.out.println("F");
     4     }
     5 }
     6 
     7 class S extends F {
     8     final void f() {
     9         System.out.println("S");
    10     }
    11 }
    12 
    13 public class Main3 {
    14     public static void main(String[] args) {
    15         F f = new S();
    16         f.f();
    17     }
    18 }

    答案是:

    嗯。。编译错误。。Cannot override the final method

    所以final方法不能被重写。当你不想让一个方法被重写,可以把方法设置为final

    然后看一下private方法。虽然private也是final的,但是在这里还是有一点区别,因为父类的private方法对于子类是不可见的。

     1 class F {
     2     private void f() {
     3         System.out.println("F");
     4     }
     5 }
     6 
     7 class S extends F {
     8     // @Override 加上这句会出现错误 可知并不是重写
     9     private void f() {
    10         System.out.println("S");
    11     }
    12 }
    13 
    14 public class Main3 {
    15     public static void main(String[] args) {
    16         F f = new S();
    17          //f.f(); Error:The method f() from the type F is not visible
    18     }
    19 }

     对于private方法可以在子类添加相同方法,但并不是重写,只是一个无关的全新方法,同时这样会造成混淆,所以不要这样使用。

    接下来是static

    按照《thinking in java》上的说法,构造器也是static的(虽然并不理解)。构造器中最好只调用final方法。因为其他方法可能会造成重写,而我们又知道,初始化的时候是先初始化父类再初始化子类的,这样就会导致子类还未初始化完成就被调用,可能产生一些错误。

    静态方法的继承

     1 class StaticSuper {
     2     static void f() {
     3         System.out.println("Super");
     4     }
     5 }
     6 
     7 class StaticSub extends StaticSuper {
     8     static void f() {
     9         System.out.println("Sub");
    10         // super.f(); error:Cannot use super in a static context
    11     }
    12 }
    13 
    14 public class Main {
    15     public static void main(String[] args) {
    16         StaticSuper sup = new StaticSub();
    17         sup.f();
    18         StaticSub sub = new StaticSub();
    19         sub.f();
    20     }
    21 }

    输出:

    Super
    Sub

    可知对于静态方法不存在多态,子类中方法会覆盖父类相同方法。但是静态方法是否被继承?

     1 class StaticSuper {
     2     static void f() {
     3         System.out.println("Super");
     4     }
     5 }
     6 
     7 class StaticSub extends StaticSuper {
     8 }
     9 
    10 public class Main {
    11     public static void main(String[] args) {
    12         StaticSuper sup = new StaticSub();
    13         sup.f();
    14         StaticSub sub = new StaticSub();
    15         sub.f();
    16     }
    17 }

    输出

    Super
    Super
    可见静态方法是会被继承的。

    上面所有讨论的都是方法。对于域,是不存在多态的!

     1 class Super {
     2     public int field = 0;
     3     public int getField() {
     4         return field;
     5     }
     6 }
     7 
     8 class Sub extends Super {
     9     public int field = 1;
    10     public int getField() {
    11         return field;
    12     }
    13     public int getSuperField() {
    14         return super.field;
    15     }
    16 }
    17 
    18 public class Main {
    19     public static void main(String[] args) {
    20         Super sup = new Sub();
    21         System.out.println(sup.getField());
    22         Sub sub = new Sub();
    23         System.out.println(sub.getField());
    24         System.out.println(sub.getSuperField());
    25     }
    26 }

    输出

    1
    1
    0

    over~~

  • 相关阅读:
    十道海量数据处理面试题与十个方法大总结
    TopK的一个简单实现
    Spark1.0.0 学习路线指导
    Apache Spark源码走读之1 -- Spark论文阅读笔记
    倾情大奉送--Spark入门实战系列
    分布式发布订阅消息系统 Kafka 架构设计
    hive入门学习线路指导
    (5.3.1)数据库迁移——数据库迁移解决孤立用户与权限问题
    Shell初学(八)linux下的ACL
    Shell初学(七)linux账户管理/群组管理
  • 原文地址:https://www.cnblogs.com/wenruo/p/5352683.html
Copyright © 2020-2023  润新知