JAVA编程学习记录(继承、多态、抽象、接口)
发布日期:2021-07-23 18:13:28 浏览次数:8 分类:技术文章

本文共 7990 字,大约阅读时间需要 26 分钟。

思想引入:随着编程学习的深入,我们编写的程序会越来越复杂,越来越多的代码会出现在我们的程序中,所以我们需要将代码优化总结,提高代码利用率。

第四周

继承是多态和抽象的前提、抽象是接口的前提;

这四种方法将会使我们Java程序代码的利用率越来越高

一、继承

关键字:extends

思想:当我们定义一个类时,会发现和某些类中的很多东西(属性(变量)、行为(方法))有相似之处,这时我们就要想能不能不再定义这些东西了,直接把那个类中的东西拿过来使用?

Java为我们提供了继承(extends)的方法

什么是继承?

    多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

    多个类可以称为那个类的子类,单独这个类称为父类超类或者基类

    子类可以直接方法父类中非私有的属性和行为; 

           //子类继承父类是继承了父类所有公共的东西,但不能使用私有的东西,只能通过父类的公共方法间接访问

    通过extends关键字可以让类与类之间产生继承关系;

例:class 子类 extends 父类{    }

继承的好处:

1.提高代码的复用性;

2.提高代码的维护性;

3.让类与类之间产生了关系,是多态的前提

继承的特点:

1.Java语言只支持单继承,不支持多继承

    class 子类 extends 父类 ,祖父类 {    }    //Java语言不支持多继承定义,但和Java同为面向对象的C++是支持多继承的

2.Java支持多层继承,即:

    class 子类 extends 父类{    }

    class 父类 extends 祖父类{    }

继承的注意事项

1.不能为了使用某个类里面的部分功能去使用继承!

//因为一旦继承了那个类就拥有了那个类的所有非私有的东西(属性和行为)!

如果两个类之间存在‘is a’的关系则使用继承

    举例:有一个动物类,动物有名字、颜色等属性,有吃饭等行为

class 动物{        private String 名字;    private String 颜色;    public static void (){        System.out.println("吃);    }}//有一个狗类,它也有名字、颜色的属性,吃的行为class 狗{    private String 名字;    private String 颜色;    public static void (){          System.out.println("吃);    } }//一个一个定义很麻烦,这时就可以用到继承extends了class 狗 extends 动物{    }//狗也可以有它自己特有的行为,比如导盲class 狗 extends 动物 {     public static void 导盲(   ){           System.out.println(“狗可以导盲”);       }  }//假如有一个人类,只需要继承名字和吃,就不要用继承了

Java开发设计原则:

    a.低耦合;耦合:类和类之间的关系,尽量降低耦合;

    b.高内聚;内聚:指的是做一件事情的能力,即尽量将一个类中能完成的事情不要用多个类去做;

2.构造方法不能被继承,但是可以通过super关键字去访问;

3.私有的东西可以通过公共的方法间接去访问;

4.成员变量的名称问题:

    a.当前子类继承父类的时候,子类和父类中的成员变量名不一致时,采用分别输出;

    b.当子类和父类中的成员名一致时:先在子类的局部位置找,有就输出-->没找到,到子类的成员位置找,有就输出-->没找到,到父类的成员位置找,有就输出-->没有就没有这个变量了

常见问题:

如果父类的无参构造没有提供会怎么样?怎么办?

子类的初始化过程中,首先回去执行父类的初始化动作。因为子类的构造方法中默认有一个super()。子类要使用父类的成员变量,这个初始化,必须在子类初始化之前完成。所以,子类的初始化过程中,会先执行父类的初始化。

所以没有无参构造肯定出现报错

办法:

1.可以将父类的无参构造提供出来

2.可以用super关键字去访问父类的带参构造..

3.还可以在子类中通过this(),访问本类中的有参构造,间接的去访问父类带参构造

//子类必须有一个构造方法(有参构造/无参构造),让父类进行初始化!

继承中成员方法的问题

1.子类继承父类,访问成员方法名不一致时,采用分别调用!

2.当子类中的成员方名和父类中的成员方法名一致的情况:

    a.现在子类的成员位置找,如果有就调用

    b.如果没有找到,在父类的成员位置找,有就调用

super关键字

前面说了构造方法不能直接继承,需要通过super关键字访问父类的构造方法

概念:super代表父类的存储空间标识,可以理解为父亲的引用。

同this的用法很相似:

this代表对象的引用(谁调用的代表谁);

super代表当前子类对父类的引用;

成员访问特点以及与this的区别

成员变量:

    this.变量---->本类的

    super.变量---->父类的       

构造方法:

    this()---->本类的无参构造      

    super()---->父类的无参构造

    this(“ ”)---->本类的有参构造

    super(“ ”)---->父类的有参构造

成员方法:   

    this.方法名()---->本类的成员方法

    super.方法名()---->父类的成员方法

方法重写(覆盖)

概念:子类中出现与父类一模一样的方法时(除了权限修饰符,权限修饰符大于等于不包括private,返回值类型,方法名和参数列表相同),会出现覆盖操作,也成为重写或者复写。

当父类方法私有时(被private修饰),子类无法调用,因此无法重写;

什么时候用?

当子类中的某个方法与父类中的相同,但又特殊于父类时,采用方法重写;这样既继承了父类的方法,又有了子类特殊的功能;

注意事项:

1.重写时,子类方法权限一定要大于父类方法权限;

2.静态只能覆盖静态;

方法重写( override)与方法重载(overload)的区别

方法重写是在子类中的方法与父类中的方法一样时,在子类中写一个方法,它的名字、返回值、参数列表都与父类中的一样,起到了覆盖(替代)作用;

方法重载是一个类中已经有了一个方法A,又有了一个名字、返回值类型一致但参数列表不同的方法;

final关键字

概念:final是一个关键字,可以用于修饰类、成员变量、成员方法

特点

1.它修饰的类不能被继承

2.它修饰的成员变量是一个变量    

    //被final修饰的常量,名称字母全部大写,且必须初始化:两种初始化,显示初始化、构造方法初始化

    final不仅可以修饰基本数据类型还可以修饰引用类型

         如果final修饰的是一个基本数据类型,那么该值不能再改变。

        如果final修饰的是一个引用类型数据,那么它的地址值不能再改变,但是堆内存中的成员变量的值是可以变得。

3.它修饰的成员方法不能被重写

final与private的区别

1.final修饰的类可以访问

    private不可以修饰外部类,但可以修饰内部类(外部类私有化是没有意义的)

2.final修饰的方法不可以被子类重写

    private修饰的方法表面上看是可以被子类重写的,但其实是不可以的,子类是看不到父类的私有方法的

3.final修饰的变量只能在显示初始化或者构造函数初始化的时候赋值一次,以后不允许更改

    private修饰的变量也不允许直接被子类或一个包中的其它类访问或修改,但可以通过set、get方法进行改值和取值;

二、多态

概念:对象在不同时刻变现出来的不同状态

多态的前提:

1.要有继承或者实现关系

2.要有方法重写

3.要有父类引用(初始化)指向子类对象:父类名 f = new 子类名( );

    //通过父类对象的创建是通过子类在堆内存新建了一个对象,由于子类又继承了父类;父类的引用(初始化 )是通过子类新建对象进行的

多态在程序中的体现:父类或者接口(本文下面详细写)的引用指向或者接受自己的子类对象;

好处及作用:

1.多态的存在提高了代码的复用性(可替换性):由继承保证;多态对已存在代码具有可替换性。例如,多态对圆Circle类有效,对其它任何圆形(如圆环)几何体也有效;

2.提高了代码的扩展性:由多态保证(父类的引用指向子类对象);增加新的子类不影响已有类的多态性、继承性。

3.提高了代码后期的可维护性;

多态的成员访问特点:

父类名 f = new 子类名( );

全程结合这个父类引用(初始化)指向子类对象来学习;

左边的父类就是进行引用或者初始化的,右边new后面的子类就是对象类;

成员变量:编译看左,运行看左

    编译时期:看引用型(初始化)变量所属的类中是否有所调用的变量;

    运行时期:同样看引用型(初始化)变量所属的类是否有调用的变量;

多态中的成员变量访问无论编译还是运行都看引用型(初始化)变量所属的类;

//多态中调用成员变量时,其实左边多指的就是父类,编译时期看父类中的成员变量是否编译通过,通过了就可以运行了,运行也看父类,直接调用父类里的成员变量;

成员方法(非静态static的):编译看左,运行看右

    编译时期:要查看引用(初始化)变量所属的类中是否有所调用的成员;

    运行时期:要查看对象(被new的那个,多指子类)所属的类中是否有所调用的成员,如果父子出现同名的方法,会运行子类中的方法,因为方法有覆盖的特性;

//多态中调用成员方法时,由于多态的前提是存在方法重写(覆盖),方法重写就是因为子类与父类中有同名的方法,且这个方法存在特殊性,所以我们肯定要优先使用子类中的成员方法,这时就不难理解了。当然,由于子类是继承父类的,所以我们编译时要先在父类找有没有这个同名方法,如果有就可以编译通过,运行时就直接使用子类中的方法了;

静态成员方法:全看左边

    编译时期:看引用型(初始化)变量所属的类中是否有所调用的变量;

    运行时期:也是看引用型(初始化)变量所属的类是否有所调用的变量;

//静态的成员方法与类有关,不属于方法重写

构造方法:无论子类,还是父类,都是对对象进行初始化;

多态方便了继承的子类调用,但是存在问题!

弊端:

不能调用子类的特有功能

父类调用的时候只能调用父类里面的方法,不能调用子类的特有方法,因为你并不清楚将来会有什么样的子类继承你;

多态只能使用父类里面的方法,不能使用子类里的独有方法,这是它的弊端。那有没有方法让我们可以调用呢?

注意:一定不能将父类的对象转换成子类类型

将父类的引用强制转换子类的引用  ,向下转型使用不当,会出现一个异常:属于运行时期异常:ClassCastException

父类的引用指向子类对象(向上转型),这个引用既然可以被提升,也就可以强制转型(向下转型)

向下转型:子类的引用指向子类对象:父类名 f = new 子类名( ); 子类名 z = (子类名)f ;

多态自始至终都是子类对象在变化!!

例题:

/** * 看程序,写结果 *  * 	继承:继承父类中所有的东西,除过构造方法 * 		继承的初始化:分层初始化 *  * @author Administrator * */class A {	public void show() {		show2();	}	public void show2() {		System.out.println("我");	}}class B extends A {		/**	 *解决问题的关键	 * public void show(){	 * 	show2() ;	 * }	 */	public void show2() {		System.out.println("爱"); //爱	}}class C extends B {	public void show() {		super.show();	}	public void show2() {		System.out.println("你");// 你	}}public class DuoTaiTest {	public static void main(String[] args) {		A a = new B();		a.show();				B b = new C();		b.show();	}}

 抽象

之前我们学过面向对象,对象是一个虚拟的概念,只是被我们用概念实例化了,比如动物、人、水果等等,这些都是一个抽象的概念,动物里面应该有更详细的分类,类、种、科、纲、目等等,直到一个具体的动物个体才是一个真正的事物,比如一只叫Tom的宠物猫,它属于动物、哺乳动物等等;

所以我们可以将这些抽象概念共有的属性、本质的内容提取出来,放在一个抽象的类里面,再由一个个具体的实例去使用;

概念:抽象就是从多个事物中将共性的、本质的内容抽象出来;

关键字:abstract

抽象类的特点:

1.抽象方法一定在抽象类中;

2.抽象方法和抽象类都必须被abstract修饰;

3.抽象类不可以被new创建对象,因为调用抽象方法没有意义;

4.抽象类中可以有抽象方法,也可以有非抽象方法,抽象方法用于子类实例化;

5.抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用;如果子类只覆盖了部分的抽象方法,那么该子类还是一个抽象类;

6.如果一个类是抽象类,那么继承它的子类要么是抽象类,要么重写所有抽象方法;

    特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象;

抽象类的成员特点:

1.成员变量:可以是变量,也可以是常量;

2.构造方法:可以无参,也可有参。用来给对象进行初始化;

3.成员方法:可以有抽象方法,还可以有非抽象方法;

    //抽象方法被abstract修饰,抽象方法只有返回值、名称、参数列表,没有方法体,必须在子类中具体实现该方法(给出方法体)

抽象类的注意事项:

1.抽象不能被实例化,为什么还有构造函数?

只要是class 定义的类里面就肯定有构造函数。抽象类中的函数是给子类实例化的。

2.一个类没有抽象类,为什么要被定义为抽象类?

这个类不想被继承,也不想被实例化

3.抽象关键字不能和那些关键字共存?

final:如果方法被抽象,就需要被覆盖,而final是不可以被覆盖的,所以冲突的;

private:如果函数被私有了,子类无法直接访问,就更谈不上覆盖;

static:不需要对象,类名就可以调用抽象方法,而调用抽象方法没有意义;

抽象类是抽象的概念,需要大量的代码才能更加熟悉它的用法和作用

在菜鸟教程看到的很有意思的一个实例:

abstract class 葵花宝典 {    public abstract void 自宫();}class 岳不群 extends 葵花宝典 {    public void 自宫(){        System.out.println("剪刀");    }}class 林平之 extends 葵花宝典{    public void 自宫(){        System.out.println("指甲刀");    }}class AbstractTest  {    public static void main(String[] args) {        岳不群 岳 = new 岳不群();        岳.自宫();        林平之 林 = new 林平之();        林.自宫();    }}

接口

概念:接口是抽象方法和常量值的集合。从本质上讲,接口是一个特殊的抽象类,这种抽象类只包含常量和方法的定义,而没有变量和方法的实现。

格式:interface 接口名{    }

接口的出现将“多继承”通过另一种形式体现出来,即“多实现”

实现:implements

格式:class 类名 implements 接口名{    }

接口的特点:

1.接口不能被实例化;

2.一个类如果出现了接口,要么是抽象类,要么实现接口中的所有方法;

3.接口的子类如果是抽象类,那么该类没有意义,不能被实例化

接口的成员特点:

接口中的成员修饰符是固定的

成员变量(实为常量):public static final:接口里定义的变量是全局常量,而且修饰符只能是这三个关键字,都可以省略,常量名要大写;

成员方法:public abstract:接口里定义的方法都是抽象的,两个修饰符关键字可以省略

构造方法:接口不存在构造方法;

//推荐永远给出修饰符;

继承与实现的区别:

1.类与类之间称为继承关系:因为该类无论是抽象还是非抽象的,它的内部都可以定义非抽象方法,这个方法可以直接被子类使用,子类继承即可,只能单继承,可以多层继承;

2. 类与接口之间是实现关系:因为接口中的方法都是抽象的,必须由子类实现才可以实例化。可以单实现,也可以多实现;还可以在继承一个类的同时实现多个接口。((class) extends (class) implements (interface1,interface2…))

3.接口与接口之间是继承关系:一个接口可以继承另一个接口,并添加新的属性和抽象方法,并且接口可以多继承。((interface) extends (interface1,interface2…))

抽象类与接口的区别:

成员变量:

抽象类:可以有变量,也可以有常量

接口:只能有常量

成员方法:

        抽象类:可以有抽象类的方法,也可以有非抽象的方法

接口:只能有抽象的方法

构造方法

抽象类:有

接口:无

类与抽象类和接口的关系

类与抽象类的关系是继承:extends

类与接口的关系是实现:implements

接口的思想特点:

1.接口是对外暴露的规则;

2.接口是程序额功能扩展;

3.接口的出现降低耦合性(实现了模块化开发,定义好规则,每个人实现自己的模块,大大提高了开发效率);

4.接口可以用来多实现;

5.对个无关的类可以实现同一个接口;

6.一个类可以实现多个相互之间没有关系的接口;

7.与继承关系类似,接口与实现类之间存在多态性;

总结:

这个时候再来看继承、多态、抽象、接口之间的关系会发现:

继承是让我们更少的去定义太多的属性和行为,减少重复。

多态是减少我们去记太多的方法名,比如我们画图形,可以画矩形、画圆形、画三角形,按一般思路来讲:我们要创建一个画矩形的方法、创建一个画圆形的方法、创建一个画三角形的方法。很麻烦,要记很多方法名;这时我们去创建一个画的方法或者说是功能,当我们要需要画某个图形的时候,我们去继承那个画的方法,然后再详细写画的方式即可;大大简化了方法的实现和调用。当然,这些都是建立在继承和方法重写的基础之上来做的;

抽象就是继承的升级版,将多个事物共有的但又比较特殊的属性定义为抽象方法,比如吃,南方人爱吃米,北方人爱吃面,但都是吃的行为,只是吃的东西不同,所以就可以把吃这个功能抽象化,让南方人的类和北方人的类去继承它,然后将它重写(覆盖);它主要描写一些概念性的东西,然后在子类中具体去实现这些概念。比如吃,吃什么就是具体的了。这样可以提高开发效率,抽象类更多是作为其他类的父类;

而接口是抽象的plus版,将需要的功能都写在一个固定的代码块中,通过接口关键字来使用它;只是和抽象存在一些区别;

转载地址:https://blog.csdn.net/otmqixi/article/details/79993383 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JAVA编程学习记录(知识补充)
下一篇:JAVA编程学习记录(面向对象)

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月12日 18时25分51秒