栏目

首页 首页 软件查看内容

牛刀小试 - 浅析Java的继承与动态绑定

最近更新| 发布者: 站长-黑杰克| 查看: |

什么是继承?

继承也是面向对象的重要特性之一。顾名思义,继承就是指从已有的类中派生出新类的动作。新的类能吸收已有类的数据属性和行为,并能扩展新的能力。视频-域名解析以及绑定域名.

 

而通俗一点的来说,就是指Java中可以通过继承的方式,从现有的类派生出新的类。该现有类被称为超类(父类),而派生出的新类就被称为子类(派生类)。

首先,子类访问继承超类当中的所有非私有的方法和成员变量;其次,还可以在父类原有的成员的基础上添加一些新的方法和域,或者对父类的方法进行覆写(override)。

 

所有通常也这样讲:父类是子类的一般化表现形式;而子类是父类的特有化表现形式。

 

Java中使用关键字“extends”用于声明一个类继承自另一个类。第三讲 eclipse的使用和Java语言基础.

 

继承的体现

首先,假定我们自定义了一个雇员类“Employee”:

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. public class Employee {
  4.  
  5. private String name; // 姓名
  6. private int salary; // 收入
  7.  
  8. public String getName() {
  9. return name;
  10. }
  11.  
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15.  
  16. public int getSalary() {
  17. return salary;
  18. }
  19.  
  20. public void setSalary(int salary) {
  21. this.salary = salary;
  22. }
  23.  
  24. }

一个部门中,雇员通常分为经理和普通雇员。二者之间大部分的行为相似,但可能在收入上略有不同。第二讲java概述及环境搭建

假设普通雇员的收入来源为工资,而经理的收入结构则是由工资 + 绩效奖金构的。于是这个时候,原有的雇员类就不足以描述经理了。

于是,使用继承从原有的雇员类中派生出一个新的经理类“Manager”:

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. public class Manager extends Employee {
  4. // 子类新的特有实例域:绩效奖金
  5. private int bonus;
  6.  
  7. public int getBonus() {
  8. return bonus;
  9. }
  10.  
  11. public void setBonus(int bonus) {
  12. this.bonus = bonus;
  13. }
  14.  
  15. }

 

继承的特性

1、子类会继承超类当中的方法以和实例域

 

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. public class JavaExtendsDemo {
  4. public static void main(String[] args) {
  5. Manager m = new Manager();
  6. m.setName("张经理");
  7. System.out.println(m.getName());
  8. m.setBonus(20000);
  9. System.out.println(m.getBonus());
  10. }
  11. }

在这里可以看到的是,我们在派生类“Manager”当中并没有定义成员变量“name”,也没有定义其相关的set/get方法。

但是我们仍然可以通过“Manager”类对这些成员进行访问,正是因为继承的机制带来的。

“Manager”继承自“Employee”类,所以“Emoloyee”类当中的所有非私有化的成员都被隐式的继承到了子类“Manager”当中。

 

2、方法覆写(override)

前面我们已经说过了,经理的收入结构为:工资 + 奖金。所以“Employee”类当中获取雇员收入的方法“getSalary”就不适用于描述经理的收入了。

对于“Manager”类中,“getSalary”方法返回的值应当是:salary + bonus。这时候就涉及到继承中一个重要的知识点:方法覆写(override)

覆写是指:除方法体以外,方法的所有声明都当与父类中相同,而方法的访问修饰符只能比父类更加宽松。

 

一定要牢记覆写的规则,才不会再使用时出错。下面通过一道网上看见的华为的面试题,来更形象的理解一下覆写的概念:

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. /*
  4. * QUESTION NO: 3
  5. *1. class A {
  6. *2. protected int method1(int a, int b) { return 0; }
  7. *3. }
  8. *
  9. * Which two are valid in a class that extends class A? (Choose two)
  10. * A. public int method1(int a, int b) { return 0; }
  11. * B. private int method1(int a, int b) { return 0; }
  12. * C. private int method1(int a, long b) { return 0; }
  13. * D. public short method1(int a, int b) { return 0; }
  14. * E. static protected int method1(int a, int b) { return 0; }
  15. */
  16. public class Test {
  17.  
  18. }
  19.  
  20. class A {
  21. protected int method1(int a, int b) {
  22. return 0;
  23. }
  24. }
  25.  
  26. class B extends A {
  27. // 合法,通过提高访问权限(protected → public)的方式覆写父类方法。
  28. public int method1(int a, int b) {
  29. return 0;
  30. }
  31.  
  32. // 不合法,注意覆写的规则:方法的访问修饰符只能比父类更加宽松。
  33. private int method1(int a, int b) {
  34. return 0;
  35. }
  36.  
  37. // 合法,但并不是方法覆写。而仅仅是在类B中,针对继承的方法method1进行了重载;
  38. private int method1(int a, long b) {
  39. return 0;
  40. }
  41.  
  42. // 不合法,首先方法返回类型不能作为方法重载的标示。那么就只能是覆写的情况,但覆写要求:除方法体外,方法所有声明都与父类中相同。
  43. public short method1(int a, int b) {
  44. return 0;
  45. }
  46.  
  47. // 不合法,同样还是因为方法声明与父类不同。
  48. static protected int method1(int a, int b) {
  49. return 0;
  50. }
  51. }

 

3、引用父类关键字“super”

我们在第2点特性中说到,对于“Manager”类中,“getSalary”方法返回的值应当是:salary + bonus。也就是说“Manager”类中,getSalary的实现应当为:

[java] view plaincopyprint?
  1. public int getSalary(){
  2. return bonus + salary;
  3. }

但实际上,这样做肯定是行不通的,因为salary在父类中被声明为私有的。所以在子类中是无法直接进行访问的。

所以,我们只能通过父类中,针对于该成员变量提供的访问方法“getSalary”获取它的值。所以可能正确的实现应该是:

[java] view plaincopyprint?
  1. @Override
  2. public int getSalary() {
  3. return getSalary() + bonus;
  4. }

但因为我们本身已经根据新的需求对该方法进行了覆写,所有这样做也是行不通的。

这是因为通过方法的覆写,“Employee”类当中已经有了自己特有的“getSalary”方法,所以上面的做法实际上是在让“getSalary”不断的调用自身,直到程序崩溃。


 

想要调用到父类当中的成员,就可以使用Java的关键字之一:super。

super是Java提供的一个用于只是编译器调用超类成员的特殊关键字。所以修改后的“getSalary”方法应当为:

[java] view plaincopyprint?
  1. @Override
  2. public int getSalary() {
  3. return super.getSalary() + bonus;
  4. }


 

 

4、多态以及动态绑定

多态是指:同一事物根据上下文环境不同使用不同定义的能力。例如我们熟悉的重载、覆写都是多态的一种体现。多态是面向对象的又一重要特性。

而多态在Java里继承中的体现形式,主要分别为:

  • 对象的多态:例如我们定义了一个超类动物类"Animal",其中老虎类"Tiger"和鱼类"Fish"都继承自"Animal"。这就是一种类的对象的多态体现。因为一个动物(Animal)构造出的对象既可以是一头老虎"Tiger",也可以是一条鱼“Fish”。
  • 方法的多态:假设动物类“Animal”提供了一个动物呼吸的方法“Breath”。但因为老虎和鱼的呼吸方式是不同的,老虎用肺呼吸,而鱼通过腮呼吸。所以我们需要分别在对应的子类中覆写“Breath”方法。这也方法的多态的一种体现形式。
     

动态绑定是指:程序在运行期(而不是编译时期)根据对象的具体类型进行绑定,所以又被称为运行时绑定。动态绑定的执行过程大概为:

1、首先,编译器首先查看并获取到对象的声明类型和方法名;

然后在其声明类型对应的相应类及其超类的方法表进行查找;

最终搜索出所有方法声明为“pulbic”的对应方法名的方法,得到所有可能被执行的候选方法。

 

注:方法表也就是指,当一个类第一次运行,被类装载器(classloader)进行装载工作时,其自身及其超类的所有方法信息都会被加载到内存中的方法区内。

 

2、编译器将查看调用方法时传入的参数的类型。

如果在执行第一步工作中所获得的所有候选方法中,存在一个与提供的参数类型完全符合,则会决定绑定调用该方法。

这个过程被称为重载解析。也就可以明白:用重载表现的多态,其动态绑定的解析工作就是这样完成的。

另外,由于Java允许类型转换,所以这一步过程可能会很复杂。

如果没有找到与参数类型匹配的方法,或者经过类型转换过后有多个匹配的方法,则会报告编译错误。

 

注:方法名+参数列表 = 方法签名。所以前面我们谈到的方法的覆写规则也可以理解为:方法签名必须与父类相同,访问修饰符只能更宽松。

但值得注意的是,方法返回类型不是方法签名的一部分。在JDK1.5之前,要求覆写时,返回类型必须相同。

而在这之后的版本中,覆写允许将父类返回类型的子类作为返回类型,这被称为协变返回类型
 

 

3、与之对应的:如果是被声明为private、static、final的方法或构造器,编译器将可以直接的准确知道应当调用的方法。这种方式又被称为静态绑定。

 

4、如果采用的是动态绑定的方式。当程序运行时,一定会选择:对象引用所指向的实际对象所属类型中,最合适的方法。

这也是为什么在继承中,子类覆写超类中的方法后。如果使用超类的类型进行声明,而实际引用子类的对象的对象引用调用方法,会准确的调用到子类中覆写后的方法的原因。

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. public class Test {
  4. public static void main(String[] args) {
  5. Animal [] animals = new Animal[3];
  6. animals[0] = new Animal();
  7. animals[1] = new Tiger();
  8. animals[2] = new Fish();
  9. for (Animal animal : animals) {
  10. animal.breath();
  11. }
  12. }
  13. }
  14. class Animal{
  15. void breath(){
  16. System.out.println("动物呼吸");
  17. }
  18. }
  19. class Tiger extends Animal{
  20. @Override
  21. void breath() {
  22. System.<span style="font-size:12px;">out</span>.println("老虎用肺呼吸");
  23. }
  24. }
  25. class Fish extends Animal{
  26. @Override
  27. void breath() {
  28. System.out.println("鱼用腮呼吸");
  29. }
  30. }
  31.  
  32. /*
  33. 运行结果为:
  34. 动物呼吸
  35. 老虎用肺呼吸
  36. 鱼用腮呼吸
  37. */


5、继承结构:单继承和多重继承

Java中不支持多继承。其继承结构分为:单继承和多重继承。

顾名思义,单继承也就是指:一个类只能继承自一个超类。

而多重继承则是指:C类继承自B类,而B类继承自A类这样的继承层次结构。

多重继承的应用是很常见的。还是以动物为例,老虎继承自动物。而老虎可能又分为东北虎之类的很多品种,那么新定义的东北虎类就应该继承老虎类。

这就是多重继承的一种常见体现形式。

 

6、继承体系中子类的构造过程

[java] view plaincopyprint?
  1. package com.tsr.j2seoverstudy.extends_demo;
  2.  
  3. public class JavaExtendsDemo {
  4. public static void main(String[] args) {
  5. Son s = new Son();
  6. }
  7. }
  8.  
  9. class Far{
  10. Far(){
  11. System.out.println("父类构造初始化..");
  12. }
  13. }
  14.  
  15. class Son extends Far{
  16. Son() {
  17. //这里有一个隐式的构造语句:super(),调用父类的构造初始化工作..
  18. System.out.println("子类构造初始化..");
  19. }
  20. }
  21. /*
  22. 运行结果为:
  23. 父类构造初始化..
  24. 子类构造初始化..
  25. */

也就是说,子类的构造过程是依赖于父类的,当开始子类的构造工作之前,会先完成其所属超类的对象构造工作。

这是理所应当的,因为我们已经知道子类的很多属性与方法是依赖于父类的,如果在此之前不先完成父类的构造工作,对于子类的使用就很容易引起错误。
 

 

什么时候使用继承?

要了解什么使用继承,首先我们应当知道使用继承可以带来的好处是什么:

  • 通过继承构成体系,能让程序的条理性更加清晰。
  • 若出现特殊需求,子类可以较方便的改动父类的实现。
  • 最大的好处在于方便实现代码的复用,减少了编码工作。

所以,最长在什么时候使用继承呢?

1、声名远扬的“IS-A”关系

例如上面说到的“经理是一个雇员”,“老虎是一种动物”都是归属于这种关系。

2、想要通过继承实现多态

以一段代码更好理解,假如我们的程序提供了一个接口用于获取动物的奔跑速度:

[java] view plaincopyprint?
  1. int getSpeed(Animal animal){
  2. return animal.getRunSpeed();
  3. }

通过继承,通过这样简单的代码。只要传入的参数对象的类型位于该Animal的继承体系当中,就可以通过动态绑定的机制正确获取到其奔跑速度。

这实际上也是”策略设计模式“的一种体现方式。而如果假设不使用继承的话,就需要提供对应数量的方法来分别获取不同动物的奔跑速度。
 

 

但与此同时,继承也有一定的弊端:

  • 类继承是在编译时刻静态定义的,所以无法在运行时改变继承类的实现。
  • 通过继承的复用方式被称为“白箱复用",因为父类的实现细节对子类可见。
  • 父类通常都至少定义了子类的部分行为,所以父类的改变都有可能影响到子类的使用。

  •     所以,正如这个世界上任何事物都有两面性一样,对于继承的使用还是应该结合实际情况作出最适合的决定。
  •  
    文章由黑客安全天启网原创,个人网站不得转载。站长负责技术培训QQ9326665,关于—培训介绍,点击观看更多视频教程


文章由天启科技原创,抄袭必究,转载请注明:
本文地址:http://www.goodgoodhack.com/bingdufangfan/1254.html
文章由天启科技站长黑杰克原创,免费学习黑客技术,业务联系站长QQ9326665

最新视频