本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!