黑马程序员_面向对象(继承)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、继承

关于继承的描述

我们在定义类的时候,比如电视和电脑类,在类中我们分别定义每个事物中的属性和行为,但是,具体看来,电视和电脑有共同的地方,比如都有有显示屏,都有插头,这些如果没定义一个类的话就会显得比较臃肿,所以可以把这些共有的或者说共性的东西提起出来,单独进行描述,只要让电脑和电视与我们单独描述的类联系上就可以了。

那么现实生活中,我们就以不断的某些事物的共性进行不断的抽取,然后进行单独描述,然后使之与原来事物建立联系,这样就方便我们描述和编程了。

要建立联系,就要用到java中的一个关键字:extends

示例代码如下:

                            class Person{

         String name;

         int age;

}

class Student{

         //String name;

         // age;

         void Study(){}

}

class Worker{

         //String name ;

         //int age;

         void work(){}

}

有上述示例代码可看出,Person类把后面的两个类中共性的东西抽取出来,单独在一个类中进行描述,而那两个类只需描述本类所特有的属性和行为即可。那么如何用extends建立两者之间的连接?

         格式为:  class  Student   extends  Person

                                   {

                   Void Study(){} 

}

这样继承Person类后,Student就具有Person类中的属性和行为,这样就是两者之间产生了联系,我们之前所希望的就发生了。这里,把向上抽取取来的,就是被继承的类称为父类,继承这个类的类称为子类,这就是面向对象中的继承。

面向对象的继承关系,提高了代码的复用性,而且让类与类之间产生了关系,有了这个关系,才有了之后的多态。继承的时候,要注意,继承这个特征会把父类的所有东西都会继承过来,所以,要注意使用继承的情况。这就又回到我们最开始的地方,有两点,一是事物共性,二是两者联系(个人是理工男,所以两者联系觉得说成附属关系更好理解点)。当事物没有上述两点的时候,硬性抽取变成父类时没有意义的。

(关于联系或者父子类的关系,做一点距离,这里说的父类更多是抽象的,比如说:鸟类,这就是父类,而各个鸟种,像鸽子,鹰,海鸥,都是子类,父类时有哪些子类所抽取出来的共性,那么鸟类这个抽象的就有那么鸟的共有特征,不如飞行,觅食。这就有了关系,有了共性的描述。)

 

关乎于继承的一点特性

Java语言中,只支持单继承,不支持多继承,因为多继承容易带来安全隐患,当多个父类中定义了相同的功能,当功能内容不同的时候,不确定要运行哪一个。比方说C是子类,AB是父类,C同时继承了AB类,那么当AB类中各有一个相同功能(方法名和方法参数都一样),那么当A运行时,就不知道该运行哪个类了。

java中保留了这种多继承机制,用另一种形式来体现,多实现。

上述图标来说,子类继承父类,父类有的东西就可以访问,而父类又继承父类的父类,这是可以的。

这里就涉及到如何使用一个继承体系中的功能

想要使用体系,先查阅系统父类的描述,因为父类中定义的是该体系中共性的功能。通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。那么在具体调用时,要创建最子类的对象,因为一、因为父类可能是抽象的,不能创建对象,二、创建子类对象,可以使用更多的功能,包含共有的和特有的。

               

 

总结的来说,继承

1)  我们实际需要把某类事物的共性进行抽取并独立描述,以方便我们描述同时简化代码。同时这一点表示,不要为了简化代码二随便继承,继承必须在类与类之间有共性,有所属关系才能用继承。

2)  注意继承的关键字extends

3)  继承,可以简化书写,提高复用性,也为多态打下基础

4)  继承是有共性内容,事物间有联系的属性和行为的抽取,然后单独描述的,所以父类和子类要有联系,有附属关系,就像猫科和猫。不要进行硬性的抽取和继承。

5)  继承是单继承,就是说一个子类只能有一个父类来继承,通俗的说一个孩子只能有一个父亲

6)  想使用继承体系中的功能,知道父类功能的描述,基本就可以使用这个继承体系了。

7)  在继承体系中创建对象,要创建子类的对象,因为有两点:一、父类可能不能创建对象,二、子类有更多功能,含有共有的和特有的。

二、子父类成员

1变量:

子类继承父类,在主函数中建议对象,那么,子类引用就可以通过:对象名.变量,就可以进行调用。就相当于子类中有父类的变量,当然实际情况就是如此。

但当子父类中变量名称一样时,对象调用这个变量,就会调用子类的变量而不会调用父类中的同名变量,因为有对象的建立,通过对象名调用,就相当于变量名前加上this关键字,使得引用指向现在这个对象。如果想指向的父类的变量,就用到一个新关键字,super

情况如下:

                          class   Fu{

             int num = 4;

}

 class   Zi{

   int num=3;

}

子父类成员变量同名情况就是这样。

关键字super

解读super关键字,就得说到this关键字。我们知道,this关键字是指向本类对象,代表本类对象的引用,super就是指向父类的,代表父类对象的引用。

对于thissuper使用的情况如下:

如果子类中出现非私有同名成员变量时,1)子类要访问本类中的变量,用this或什么都不加2)子类要访问父类中的同名变量,用super

 

如果父类有定义的同名变量函数,那么,子类就尽量不要定义了。

 

2函数:

子类既然继承了父类,那么就有父类中的所有功能,函数也不例外。调用规则同对象调用本类方法一致,对象名.函数。但是如果子父类函数同名,这个结果是什么样。如果子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,就如同父类函数被覆盖一样,这就是函数的另一个特性:重写。当然父类函数还在,但是没有运行。

思考下,当子类继承父类,有了父类的功能,如果到子类中,子类虽具备这个功能,但是和父类工能不一致,比如相同的方法work(),父类中是人的work,这可能是写作,也可能是唱歌,但子类中就不一样,我们就让他学习,但同样是work()这个方法。这时,就没必要定义新功能,而是使用覆盖重写,保留父类定义的功能,但是具体内容重新编写。当我执行子类对象的方法时,就会执行子类自己重新编写的方法。这就是沿袭父类功能,编写

子类特有功能。

  示例:

                          class   Fu{

             work(){ 唱歌,写作 }

}

 class   Zi extends Fu{

   work(){ 学习 }

}

通过以上例子和描述,这可以得值,可以通过这个方法进行功能的拓展。我们在开发程序时,就可以用到重写,比如当变更新功能时,不能直接在原码上进行修改,而是在创建一个可继承这个的程序,进行继承并复写功能,写入新功能。这样,不改原码,但定义了新功能,这就是功能的拓展。而某些我们不需要修改的,直接用super指向父类的函数几好,节省了开发时间。

重写时注意:

1)  子类覆盖父类,必须保证子类权限大于等于父类权限,才可以重写

2)  静态只能覆盖静态(但这个极少用到)

 

重载和重写的区别

1)  重载:只看同名函数的参数列表

2)  重写:字符类方法要一模一样(包括返回类型,因为返回类型如果不一样的话,子类中就相当于有返回类型不一样但是其他都一样的函数,那么在调用这个函数的时候,该返回什么类型呢)。

 

3构造函数

先看程序:class   Fu{

             Fu(){ 父类 }

}

 class   Zi  extends Fu{

   Zi(){ 子类 }

}

在主函数中建立对象,程序一运行,就会执行构造函数里的语句,结果是父类先运行,然后子类运行。为什么呢??因为在子类构造函数的第一行,有一句默认的隐式语句super(),代表调用父类的构造函数(前面我们学习的,this(),代表的是调用本类的构造函数)。super():会访问父类中空参数的构造函数,而且子类中所有构造函数(不管是有参数或没参数),默认第一行都是super()。注意1)引用的都是空参数的,如果想引用非空参数的父类的构造函数,在子类构造函数第一行,写super(参数)。2)子类构造函数一定会访问父类构造函数,如果父类没有空参数的构造函数,那么就在子类造函数第一行,手动添加super(参数)。

为什么子类一定要访问父类中的构造函数?

因为父类中的数据,子类可以直接获取,所以子类对象在建立的时候,就要看看父类时如何对数据进行初始化,所以要访问以下父类中构造函数。看一下示例:

 class Person{

                          private String name ;

                          Person(String name){

                            this.name =name;

                          }

}

class Student extends Person{

                          Student(String name){

                            super(name);

                          }

}

上路例子中,Student类中,也需要定义name,但是父类中有这个构造方法了,且父类中name被私有化了,所以用supername),使用父类中的构造方法进行初始化。

 

4final关键字

这货是一个修饰符,什么都可以修饰,看看他的特征。

特征:1)可以修饰类,方法,变量

3)                 被修饰的类不可以被继承

4)                 被修饰的方法不可以被覆盖

5)                 被修饰的变量是一个常量,只能被赋值一次(潜规则:标示符全部大写),而且可以配合publicstatic修饰符,可以变成全局常量。

6)                内部类在局部时只能访问被final修饰的局部变量(这句注意,以后内部类用的多,记住这小知识点)

final的出现,其实与继承有关,因为继承在一定程度上打破了对象的封装功能,我们在使用时,如果继承某个类,就可以把里面的内容及进行修改,这样就打破了封装。而现实需求中,不希望被修改,所以final来修饰,就不可继承的。


 

5、抽象类

先看程序:

                   class Student

{

         void study(){}

}

class BaseStudent extends Student

{

         void study(){

                   语句1

         }

}

classAdvStudent extends Student

{

         void study(){

                   语句2

         }

}

看上面的语句,后两个进行抽取方法study,在Student类中重新描述,建立一个空语句的函数。既然是空语句,那么执行起来是对于的。所以,当多个类中出现相同功能的时候,但主题功能不同,这是可以向上抽取的。这里,只抽取功能定义,而不抽取功能主题。则,第一类中的方法改为:void study();

因为这种方法没有方法体,所以这里用一个关键字修饰一下,这个关键字是:abstract(只可修饰类和方法)。这是写法就改为: abstract void study(); 。因为这个方法没有方法体,那么这个类创建对象就没有意义,所以这个类也应该是抽象的,这就告诉我们,这个类中有抽象方法,不能建立对象。

 

总结的来说:(当然,首先要继承)

1)  抽象方法一定是定义在抽象类中。就是说,只要类中有一个抽象方法,那么这个类就是抽象类。

2)  抽象方法和抽象类都必须被abstract关键字修饰

3)  抽象类不可以建立对象,因为抽象方法没有意义。(简单说,无法实例化

4)  抽象类中的抽象方法要被使用,必须有子类复写其所有的抽象方法,然后建立对象调用

5)  如果子类只覆盖部分抽象方法那么该类也是一个抽象类

注意:抽象类有两类:1)里面方法全部是抽象方法,那么我们调用的时候,要全部复写

                       2)里面部分方法时抽象方法,那么我们调用的时候,只把抽象方法复写即可。

                  3)里面部分方法是抽象方法,那么我们在调用的是,如果没有全部复写里面的抽象方法,那么这个类也必须的是抽象类(意思就是不能建立对象)

                  4)如果我们必须具备某个父类的功能,但具体实现方式不一样,我们就可以去复写抽象类的抽象方法,那么其他的功能我们可以直接使用父类的,就不用复写,直接拿过来用即可。所以定义抽象类时,要注意对功能的描述,有的是不明确的或者说具体功能描述不清的(即子类功能内容不同),定义抽象。有的是子类用的那么就不定义抽象

 

6、接口

接口可以理解为一个特殊的抽象类,就是当抽象类中的方法都是抽象的,那没该类可以通过接口的形式来表示。接口的表示方法为    interface {

}

class用于定义类,interface用于定义接口,注意interface也是一个类,编译完也是class文件。

定义接口,格式特点:

1)  接口中常见定义:常量,抽象方法

2)  接口中成员都有固定修饰符

常量:public static final

方法:  public  abstract

                  

                   Interface  Inter{

          public  static  final  int  NUM = 3;

                     public  abstract   void  show();

}

看上述程序,接口中成员都是public的,成员都有固定修饰符,就像程序里的。我们可以省略int前面的修饰符,也可以省略void前面的修饰符,因为是接口,所以即便省略,系统会自动给加上,所以要注意。

既然是接口,是抽象的,那么我们使用其中的方法,所以我们就建立子类对象继承接口的方法,但现在这里使用关键字implement来实现,注意是实现接口中的方法。

示例程序:

             class  Test  implement Inter{

              覆盖接口中所有方法;

}

 

接口的另一大功能:多实现.

这个功能也是对多继承不支持的转换形式,java支持多实现。

1、类和接口之间

就是说,一个类可以实现多个接口,比如一个子类,可实现接口A和接口B,程序例子:

                                     class Test  implement AB

为什么可以多实现了,因为接口中方法没有主体即执行部分,所以两个接口中同名方法,子类只需实现一次就好,不会出现不知道该执行哪一个方法的错误。一个类,在继承一个类的同时还可以实现多个接口,这就像一个儿子只有一个亲爹,但是可以有多个干爸。程序:

                class Test  extends  C   implements A,  B         

2、接口和接口之间

   接口与接口之间,如果用实现的话,就得覆盖接口的方法,这是不现实的也没有意义的。所以接口与接口之间是继承关系。

   而且接口与接口之间,可以实现多继承,但是要方法的返回类型也是一样的。否则,我们在实现时,就会出现之前多继承出现的状况:不知道执行哪个方法。

 

  注意:我们定义接口中成员都是public的,所以实现接口,并覆盖方法时要注意,所有方法权限修饰符都是public的。

 

 

总结来说:

1)  接口是一个特殊的抽象类,里面的方法全都是抽象的。所以无法实例化。

2)  Interface 最后生成的文件也是class文件,所以里面的静态成员变量可以直接类名调用

3)  接口中定义一般是常量和抽象方法,注意是常量

4)  接口中成员都有固定修饰符,即是是省略了,系统也自动加上,但是为方便读写,还是编译时期就加上。

5)  用接口的方法,需要子类对接口实现,是由implement来完成的。要全部覆盖接口抽象方法,否则子类也是抽象的。

6)  接口可以多实现

7)  类与接口之间:类可以再继承某个类的同时,再实现多个接口

接口与接口之间:是继承关系,而不是实现关系

                可以实现多继承,但是注意方法格式(返回值类型一致)

        8)接口的一大特点就是:功能拓展。对外暴露规则,就相当于一个接口,只要你符合我的接口这个规则,就可以。降低耦合性,使得程序可以模块式开发。

                                                                                                                                                               

 

要注意一下继承和实现的区别,继承是一个属于另一个或者说有所属关系,接口则是一个可拓展的功能。这样,应该就理解了接口的功能拓展的意思。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值