Java的继承封装多态.docx
- 文档编号:16356767
- 上传时间:2023-07-12
- 格式:DOCX
- 页数:20
- 大小:22.75KB
Java的继承封装多态.docx
《Java的继承封装多态.docx》由会员分享,可在线阅读,更多相关《Java的继承封装多态.docx(20页珍藏版)》请在冰点文库上搜索。
Java的继承封装多态
Java中的继承、封装、多态
Java中的继承、封装、多态
本博客包含以下内容,博主是菜鸟,正在努力学习中,如有错误或不足,欢迎交流讨论。
1.基本定义
2.初始化和类的加载
3.final关键字
4.类之间的关系
5.向上转型
6.多态的缺陷
7.构造器和多态
1、基本定义
继承:
在创建一个新类(子类、导出类)时,使用extends关键字表明它派生于一个已经存在的类(父类、基类、超类),导出类会获得基类的所有域和方法,但并不是一定能直接访问,和访问权限控制有关。
和组合相比,继承是隐式的放置基类子对象
packagereusing;
/**
*继承是隐式的放置基类对象
*@author小锅巴@date2016年3月20日
*
*/
classVillain{
privateStringname;
publicVillain(Stringname){
this.name=name;
}
protectedvoidset(Stringname){
this.name=name;
}
publicStringtoString(){
return"InVillainclass,"+"name:
"+name;
}
}
publicclassOrcextendsVillain{
privateintorcNumber;
publicOrc(Stringname,intorcNumber){
super(name);
this.orcNumber=orcNumber;
}
/**
*set方法是public的,导出类可直接访问,但是name是被封装在基类中,导出来中不能直接访问,但是可通过从基类继承的set方法间接访问,
*上面的构造器也是,使用的关键字super来调用基类的构造器
*/
publicvoidchange(Stringname,intorcNumber){
set(name);
this.orcNumber=orcNumber;
}
publicStringtoString(){
return"InOrcclass,"+super.toString()+"orcNumber:
"+orcNumber;
}
publicstaticvoidmain(String[]args){
Orco=newOrc("Limburger",12);
System.out.println(o);
o.change("Bob",19);
System.out.println(o);
}
}
/**
*输出:
InOrcclass,InVillainclass,name:
LimburgerorcNumber:
12
InOrcclass,InVillainclass,name:
BoborcNumber:
19
*/
封装:
封装即将成员变量或者方法的具体实现隐藏起来,对用户提供接口的形式来访问,用户不需要知道具体有什么以及怎么现实,只需要知道如何使用。
若是将一个类的字段和某些方法封装起来不让用户直接访问字段或者方法,只提供类似接口的方式访问,对用户来说,此类可以看做一种抽象数据类型,比如stack
packagec1Foundamentals;
importjava.util.Iterator;
publicclassStack
privateNodefirst;//topofstack
privateintN;
privateclassNode{
Itemitem;
Nodenext;
}
publicvoidpush(Itemitem){
Nodeoldfirst=first;
first=newNode();
first.item=item;
first.next=oldfirst;
N++;
}
publicItempop(){
Itemitem=first.item;
first=first.next;
N--;
returnitem;
}
publicbooleanisEmpty(){
returnfirst==null;
}
publicintsize(){
returnN;
}
publicIterator
returnnewListIterator();
}
privateclassListIteratorimplementsIterator
privateNodetemp=first;
publicbooleanhasNext(){
returnfirst!
=null;
}
publicItemnext(){
Itemitem=temp.item;
temp=temp.next;
returnitem;
}
publicvoidremove(){}
}
}
多态:
有称动态绑定、后期绑定或运行时绑定。
首先明确下什么是绑定:
将方法调用同一个方法主体关联起来。
若在程序执行前进行绑定,叫做前期绑定,由编译器和连接程序实现,比如C都是前期绑定;动态绑定,即在程序执行前不知道对象的类型(所属的类到底是基类还是导出类),但是在运行时根据方法调用机制能找到方法调用的正确类型从而进行绑定。
故后面需要学习运行时类型信息。
2、初始化和类的加载
当创建导出类对象时,该对象实际上包含了一个基类子对象,是隐式的,而不是直接显式地用基类创建对象。
从上述来看,调用导出类的构造器创建对象是需要调用基类构造器的,而导出类可能会依赖基类对象,导出类只能访问自己的成员,不能访问基类的成员(一般是private的),故创建导出类对象的第一步是调用基类的构造器,若是继承层次较多,则从根类开始调用。
如果导出类构造器没有显式地(通过super关键字)调用基类的构造器,则会自动地调用基类的默认构造器(无参的构造器),若基类构造器是有参数的,导出类构造器又没有显式的调用基类构造器,则Java编译器将报错。
类的代码在初次使用时才加载:
1、创建类的第一个对象;2、访问static域或者方法。
类的代码初次使用之处也是static初始化发生之处,所有static对象和static代码都在加载时依程序中的顺序依次初始化
3、final关键字
final关键字通常是表示无法改变的,有三种使用情况:
数据、方法、类。
(1)final数据
分两种情况:
表示永不改变的编译时常量,可在编译时执行计算,必须是基本类型且必须在定义时确切赋值,若同时还被static修饰则表示是一段不能改变的存储空间,通常大写;一个在运行时被初始化的值,它不希望被改变,可以是基本类型,也可是引用,若是引用,表示引用只能指向一个确切对象(但对象本身可以改变),而在初始化之后不能指向别的对象。
但是不能因为一个基本类型数据被final修饰就认定在编译时知道其值,例如用生成的随机数值初始化它,那么每创建一个对象,其值都不一样,和编译时常量的定义不相符
空白final:
被声明为final但没有给定初始值的域,空白final域提供了更大的灵活性,可以根据对象不同而有所不同,一般在构造器中用表达式进行赋值。
final参数:
将方法的形参(引用)用final修饰,表示无法在方法中更改参数引用所指向的对象或者参数的值本身,即可读但不可写
packagereusing;
importjava.util.Random;
/**
*final数据
*@author小锅巴@date2016年3月20日
*
*/
classValue{
inti;
publicValue(inti){
this.i=i;
}
}
classGizmo{
publicvoidspin(){
System.out.println("Spining!
");
}
}
publicclassFinalData{
privatestaticRandomrand=newRandom(47);
privateStringid;
//空白final域
privatefinalintnumber;
publicFinalData(intnumber,Stringid){
this.number=number;//空白final域的初始化,根绝不同的对象而有所不同
this.id=id;
}
//编译时常量,必须是基本类型,且被final修饰,定义时必须进行确切的赋值
privatefinalintvalueOne=9;
privatestaticfinalintVALUE_TWO=99;//一段不可改变的存储空间
publicstaticfinalintVALUE_THREE=39;//典型常量定义方式,一段不可改变的存储空间
//非编译时常量
privatefinalinti4=rand.nextInt(20);//每次创建对象,i4的值都可能改变,编译时并不知道其值,但是在创建一个对象后i4的不可改变
staticfinalintINT_5=rand.nextInt(20);//被static修饰了,表示只有一次赋值(属于类,只有一份),但是在编译时,并不知道确切的值,
privateValuev1=newValue(11);//没有被final修饰,创建后可以改变v1所指向的对象,每次创建对象都会创建Value对象
privatefinalValuev2=newValue(22);//被final修饰了,不可改变引用v2所指向的对象而指向别的对象,每次创建对象都会创建Value对象
privatestaticfinalValueVAL_3=newValue(33);//被static和final修饰了,只会创建一个Value对象,且创建后不可改变VAL_3引用所指向别的对象
publicStringtoString(){
returnid+":
number="+number+"i4="+i4+",INT_5="+INT_5;
}
//final参数引用g不能指向别的对象
voidwith(finalGizmog){
//g=newGizmo();//报错Thefinallocalvariableicannotbeassigned.Itmustbeblankandnotusingacompoundassignment
}
voidwithout(Gizmog){
g=newGizmo();
}
voidf(finalinti){
//i++;//报错
}
publicstaticvoidmain(String[]args){
FinalDatafd1=newFinalData(1,"fd1");
//System.out.println(++fd1.valueOne);//报错提示:
ThefinalfieldFinalData.valueOnecannotbeassigned
fd1.v2.i++;//v2不能指向别的对象,当时v2所指向的对象本身可以改变
//fd1.v2=newValue(0);//报错提示:
ThefinalfieldFinalData.v2cannotbeassigned
fd1.v1=newValue(9);//v1没有被final修饰,可改变所指向的对象
//fd1.VAL_3=newValue
(1);//原因同v2
System.out.println(fd1);
System.out.println("CreatingnewFinalData");
FinalDatafd2=newFinalData(2,"fd2");
System.out.println(fd1);
System.out.println(fd2);
}
}
/**输出:
fd1:
number=1i4=15,INT_5=18
CreatingnewFinalData
fd1:
number=1i4=15,INT_5=18
fd2:
number=2i4=13,INT_5=18
*/
(2)final方法
使用final方法是为了把方法锁定,防止继承时被覆盖从而修改其行为,只有明确禁止覆盖时,才将方法设置为final,不要因为性能而将方法设置为final。
final关键字与private关键字:
类中所有private方法都隐式指定为final的。
这可能会带来混淆,如果基类有一个同名的private方法,导出类是否真的覆盖了这个方法?
并没有,为了避免这个问题,要么覆盖时加上注解,要么就避免方法名和基类private方法同名。
packagereusing;
/**
*final方法带来的混淆,
*实际上final方法并不能被覆盖,只不过是定义了新的同名的方法而已,使用Override注解可以解决此问题
*@author小锅巴@date2016年3月20日
*
*/
classWithFinals{
privatefinalvoidf(){
System.out.println("WithFinals.f()");
}
privatevoidg(){
System.out.println("WithFinals.g()");
}
privatevoidh(){
System.out.println("WithFinals.h()");
}
publicvoidy(){
System.out.println("InWithFinalsclass");
}
}
classOverridingPrivateextendsWithFinals{
privatefinalvoidf(){
System.out.println("OverrdingPrivate.f()");
}
privatevoidg(){
System.out.println("OverrdingPrivate.g()");
}
//加上Override注解,会报错,提示:
Themethodh()oftypeOverridingPrivatemustoverrideorimplementasupertypemethod
//@Override
//privatevoidh(){
//System.out.println("OverrdingPrivate.h()");
//}
publicvoidy(){
System.out.println("InOverridingPrivateclass");
}
}
classOverridingPrivate2extendsOverridingPrivate{
publicfinalvoidf(){
System.out.println("OverrdingPrivate2.f()");
}
publicvoidg(){
System.out.println("OverrdingPrivate2.g()");
}
publicvoidy(){
System.out.println("InOverridingPrivate2class");
}
}
publicclassFinalOverriding{
publicstaticvoidmain(String[]args){
OverridingPrivate2op2=newOverridingPrivate2();
op2.f();
op2.g();
op2.y();
OverridingPrivateop=op2;//向上转型
//op.f();//报错提示Themethodf()fromthetypeOverridingPrivateisnotvisible
//op.g();
op.y();
WithFinalswf=op2;//向上转型
//wf.f();
//wf.g();
op.y();
}
}
/**输出:
OverrdingPrivate2.f()
OverrdingPrivate2.g()
InOverridingPrivate2class
InOverridingPrivate2class
InOverridingPrivate2class
*/
使用final能否提高性能?
这个问题我看了好几次,没看懂,先留着这个问题。
(3)final类
将类定义为final,表示该类不能被任何人继承,即该类不需要有任何改动且不能有子类。
final类的域可以选择是或不是final,和非final类的域定义为final域的规则一样,但是由于final类禁止继承,所以final类的所有方法都隐式的是final的。
final类用得不多,因为要预见一个类如何被复用通常非常困难。
4.类之间的关系(复用类的三种形式)
∙继承
已经说了,就不赘述了。
只加一点,继承的两种关系:
B继承自A,并且没有B没有添加任何多的域或者方法,称为完全替代原则,可以说B与A是is-a关系,比如圆继承自图形,可以说圆是一个图形;B继承自A,但是B增加了新的方法,B与A是is-a-like关系,比如电子书继承自书,但是电子书除了可以阅读(假定定义的书只有这么一个功能)还可以充电,可以说电子书像一本书。
∙组合
在新类中显示地放置子对象,各个子对象之间没有什么联系,子对象是新类的一部分,新类与放置的子对象是has-a关系,比如电脑有CPU、RAM、显卡等等。
packagereusing;
/**
*组合
*@author小锅巴@date2016年3月21日
*
*/
classCPU{
publicStringtoString(){
return"CPU";
}
}
classRAM{
publicStringtoString(){
return"RAM";
}
}
classGraphicChip{
publicStringtoString(){
return"GraphicChip";
}
}
publicclassComputer{
privateCPUcpu;
privateRAMram;
privateGraphicChipgraphicChip;
publicComputer(){
cpu=newCPU();
ram=newRAM();
graphicChip=newGraphicChip();
}
publicStringtoString(){
return"Acomputerhas"+cpu+ram+graphicChip;
}
publicstaticvoidmain(String[]args){
Computerc=newComputer();
System.out.println(c);
}
}
/**
*输出
AcomputerhasCPURAMGraphicChip
*/
在组合和继承之间选择,组合是显示放置子对象,继承是隐式放置。
组合通常用于想在新类中使用现有类的功能而非它的接口,这种情况下可以在新类中嵌入一个对象,让其实现若需要的功能,如Computer类中的cpuram。
组合中放置的对象一般为private,但有时将放置的对象声明为public将有助于用户理解如何去使用类。
通过继承,使用现有的通用类开发它的特殊版本,这时用组合是毫无意义的,比如车子继承自交通工具,是is-a关系,使用组合车子与交通工具就是has-a关系,这显然说不通。
利用现有类来开发新的类,使用组合和继承都可以,但是继承其实并不常用,如果需要从新类向基类进行向上转型,就应该选择继承,否则应该考虑组合
∙代理
Java并不直接支持代理,它是继承和组合的折中,将一个类的对象置于新类中(像组合),但同时也暴露了该对象的所有方法(像继承)。
TIJ上只有这么多,我还是纳闷代理和组合的区别,维基百科看了还是不太懂,大致翻了翻HeadFirst设计模式,从目录标题来看组合是为了管理良好的集合,代理是为了控制对象访问,看设计模式时再好好研究下两者的区别吧。
5.转型
向上转型:
将某个对象的引用视为其基类类型的引用,在继承层次中向上移动。
由于导出类可能会扩展基类,故向上转型会丢失导出类的信息(成员变量或方法),无法再访问导出类中基类没有的信息。
向下转型:
与向上转型相反,在继承层次中向下移动,向下转型要先向上转型,即导出类的对象先向上转型然后再向下转型,因为基类对象如果直接向下转型,由于导出类信息会比基类多,逻辑上说不通,会抛出异常。
转型的原因:
向上转型,可以不管导出类的存在,直接和基类打交道,不必为每个导出类编写特定类型的方法;向下转型,由于向上转型会丢失导出类的信息,向下转型的正确性有运行时类型识别(RTTI)作为保证。
转型是通过多态来实现的。
packagereusing;
classUseful{
publicvoidf(){}
publicvoidg(){}
}
classMoreUsefulextendsUseful{
publicvoidf(){}//覆盖基类
publicvoidg(){}//覆盖基类
publicvoidu(){}
publicvoidv(){}
publicvoidw(){}
}
publicclasscasting{
publicstaticvoidma
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 继承 封装