首先,我要说明的是,继承、封装、多态并不是针对JAVA,c#或者其他某种语言产生的,它是面向对象思想下产生的一个概念。
让我自己说的话,我只能用三句话来描述(不知道对不对,请高手指点):
* 继承:使得子类继承父类的属性和方法,也可以使用父类的功能。
* 封装:将具体实现隐藏,只留给用户使用的接口。
* 多态:相似类型在使用同一基类方法时,可以表现出与基类不同的行为。
一直记不住这些概念,百度了下,放在这供参考:
1、继承(inheritance) 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。
继承是为了重用父类代码,同时为实现多态性作准备。
2、封装(encapsulation) 类使得数据和对数据的操作集成在一起,从而对使用该类的其他人来说,可以不管它的实现方法,而只管用它的功能,从而实现所谓的信息隐藏。 封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据。
3、多态(polymorphism)
方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来的非常强大的功能,但是复杂的继承关系也给C++开发者带来了更大的麻烦,为了规避风险,Java只允许单继承,派生类与基类间有IS-A的关系(即“猫”is a “动物”)。这样做虽然保证了继承关系的简单明了,但是势必在功能上有很大的限制,所以,Java引入了多态性的概念以弥补这点的不足,此外,抽象类和接口也是解决单继承规定限制的重要手段。同时,多态也是面向对象编程的精髓所在。 多态又分为设计时多态和运行时多态,例如重载又被称为设计时多态,而对于覆盖或继承的方法,JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态。总而言之,面向对象的设计的典型特点就是继承,封装和多态,这些特点也是面向对象之所以能如此盛行的关键所在。
对于多态,可以总结它为:
一、使用父类类型的引用指向子类的对象;该引用只能调用父类中定义的方法和变量;
二、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
三、变量不能被重写(覆盖),”重写“的概念只针对方法。
对多态的理解:
个人觉得多态是最为抽象的一个概念,那就用实例来解释了。
1. public, protected的方法具有多态性,但是如果某个方法是静态的,就不具有多态性,因为静态方法是与类而非单个对象相关联的。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.wx.test; public class Base { public void publicMethod() { System.out.println("Base public Method"); } protected void protectedMethod() { System.out.println("Base protected Method"); } public static void publicStaticMethod() { System.out.println("Base protected Method"); } } public class Sub1 extends Base { public void publicMethod() { System.out.println("Sub1 public Method"); } protected void protectedMethod() { System.out.println("Sub1 protected Method"); } public static void publicStaticMethod() { System.out.println("Sub1 protected Method"); } } public class Sub2 extends Base { public void publicMethod() { System.out.println("Sub2 public Method"); } protected void protectedMethod() { System.out.println("Sub2 protected Method"); } public static void publicStaticMethod() { System.out.println("Sub2 protected Method"); } } public class Test { public static void main(String[] args) { Base base1 = new Sub1(); Base base2 = new Sub2(); base1.publicMethod(); base2.publicMethod(); base1.protectedMethod(); base2.protectedMethod(); base1.publicStaticMethod(); base2.publicStaticMethod(); } }
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Sub1 public Method Sub2 public Method Sub1 protected Method Sub2 protected Method Base protected Method Base protected Method
从以上结果可以看到,将子类对象转换成父类对象,对于public和protected方法调用时,是调用了实际创建的子类方法。问题就在于编译器只有一个父类对象,它无法知道调用哪个方法才对。这个问题解决办法就是后期绑定,就是编译器确实不知道调用哪个方法,运行时才根据对象类型进行绑定,从而调用子类方法。也就是编译器一直不知道对象类型,方法调用机制可以知道类型信息,从而找到正确的方法体。
2.“覆盖”私有方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.wx.test; public class Super { private void function() { System.out.println("Super private Method"); } public static void main(String[] args) { Super sup = new Sub(); sup.function(); } } public class Sub extends Super{ public void function() { System.out.println("Sub public Method"); } }
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Super private Method
我们期望输出Sub public Method,但是由于private方法被自动认为是final方法而且对于子类是不可见的,因此Sub中的function()是一个新的方法。所以私有方法是不能被覆盖的。
3. 属性不可以是多态的
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.wx.test; public class Super { public int field = 0; public int getField() { return field; } } public class Sub extends Super{ public int field = 1; public int getField() { return field; } } public class Test { public static void main(String[] args) { Super sup = new Sub(); Sub sub = new Sub(); System.out.println("sup.field = "+ sup.field +"," + "sup.getField() = " + sup.getField()); System.out.println("sub.field = "+ sub.field +"," + "sub.getField() = " + sub.getField()); } }
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
sup.field = 0,sup.getField() = 1
sub.field = 1,sub.getField() = 1
不过在实践中这种情况一般不会发生,一是属性会被定义为private,因此不能直接访问,二是子类不会定义和基类相同名字的属性,这种做法容易混淆。