java教学案例

java教学案例


2023年12月13日发(作者:三星dv300f)

JAVA面向对象教学案例

类和对象

1,什么是面向对象?

面向对象和面向过程是两种程序的编写的方式,其区别与联系是一个很大的话题,仁者见仁智者见智,因为面向对象包含有三大典型特征:封装、继承、多态,具有信息隐藏、重用等特征,如果单纯强调某一个特征就好象瞎子摸象,不能完整的看到整个事物。这个专题我们不做深入的探讨,只作出以下几个比喻:

如果把写程序比作是组装一台电脑,那么:

面向过程的思想是自己动手做CPU,完成CPU的电路的设计,完成硬盘的制作,完成电脑主板等所有配件,最终把配件组装在一起,那么在这个过程中我们的知识量必须足够大才能把电脑攒出来。

面向对象思想则相对简单的多,只需要去市场上把电脑配件买来,拼装一下就OK了。这体现了信息隐藏和重复使用的特点。隐藏的信息是面向过程中我们做的事情:CPU的工作原理和设计,估计没有几个人能理解,但是我们只是通过购买就把这些知识都获取到了,剩下的就是怎么用CPU了,比自己学习CPU的制作要简单不知道多少倍了。重复使用指的就是CPU设计好了,可以依照设计制作出无数的CPU出来而不用再重复设计它。

再举一个例子:

在工厂的生产线上工作的工人仅仅是负责自己的工作,例如汽车生产线的工人有的给车上车座子,有的人上轮子,有的人调试刹车„„分工明确。如果每个人都是一个多面手,负责所有的工作,那么人员的培训的工作量是非常大的,工作效率也是很低的。例如先装刹车系统,再装动力系统,再装车架和车壳,然后是车队内饰,这样他必须非常熟悉每个业务流程,先做什么,再做什么,这就是过程,这是以过程为导向的,也就是面向过程。

相对而言,现代的流水线作业就是把工作分工,每个人可以熟练的处理自己份内的事情,这样每个人变成了生产线上的一个组件,一个工序上的工人具有相同的技术能力,当有人生病的时候,可以其他人任意的代替而不会产生影响,这体现了重用;组装汽车的时候,把工序上的人凑齐了就好了,他们相互配合就能把汽车组装起来,每个人都不需要别人怎样做的,这体现了信息隐藏。原来汽车的组装是一个过程,现在变成了凑齐组装工人,变成了以人为导向的,也就是面向对象。

2,世界是由对象组成的。

学习过哲学的人都知道,哲学里有两个流派:唯物主义和唯心主义。唯物主义认为世界是有物质组成的,所有的东西都是物质,人的意识能够反映物质的运动规律。唯心主义认为世界是不存在的,仅仅是人的大脑在变化罢了——菩提本非树,明镜亦非台,本来无一物,何处惹尘埃。

在南北朝的时候,佛教禅宗传到了第五祖弘忍大师,弘忍大师当时在湖北的黄梅开坛讲学,手下有弟子五百余人,其中翘楚者当属大弟子神秀大师。神秀也是大家公认的禅宗衣钵的继承人。弘忍渐渐的老去,于是他要在弟子中寻找一个继承人,所以他就对徒弟们说,大家都做一首畿子(有禅意的诗),看谁做得好就传衣钵给谁。这时神秀很想继承衣钵,但又怕因为出于继承衣钵的目的而去做这个畿子,违法了佛家的无为而作意境。所以他就在半夜起来,在院墙上写了一首畿子身是菩提树,心为明镜台。时时勤拂拭,勿使惹尘埃。这首畿子的意思是,要时时刻刻的去照顾自己的心灵和心境,通过不断的修行来抗拒外面的诱惑,和种种邪魔。是一种入世的心态,强调修行的作用。而这种理解与禅宗大乘教派的顿悟是不太吻合的,所以当第二天早上大家看到这个畿子的时候,都说好,而且都猜到是神秀作的而很佩服的时候,弘忍看到了以后没有做任何的评价。因为他知道神秀还没有顿悟。

而这时,当庙里的和尚们都在谈论这首畿子的时候,被厨房里的一个火头僧—慧能禅师听到了。慧能当时就叫别人带他去看这个畿子,这里需要说明的一点是,慧能是个文盲,他不识字。他听别人说了这个畿子,当时就说这个人还没有领悟到真谛啊。于是他自己又做了一个畿子,央求别人写在了神秀的畿子的旁边,菩提本无树,明镜亦非台,本来无一物,何处惹尘埃。有这首畿子可以看出慧能是个有大智慧的人(后世有人说他是十世比丘转世),他这个畿子很契合禅宗的顿悟的理念。是一种出世的态度,主要意思是,世上本来就是空的,看世间万物无不是一个空字,心本来就是空的话,就无所谓抗拒外面的诱惑,任何事物从心而过,不留痕迹。这是禅宗的一种很高的境界,领略到这层境界的人,就是所谓的开悟了。

弘忍看到这个畿子以后,问身边的人是谁写的,边上的人说是慧能写的,于是他叫来了慧能,当着他和其他僧人的面说:写得乱七八糟,胡言乱语,并亲自擦掉了这个畿子。然后在慧能的头上打了三下就走了。这时只有慧能理解了五祖的意思,于是他在晚上三更的时候去了弘忍的禅房,在那里弘忍向他讲解了《金刚经》这部佛教最重要的经典之一,并传了衣钵给他。然后为了防止神秀的人伤害慧能,让慧能连夜逃走。于是慧能连夜远走南方,隐居10年之后在莆田少林寺创立了禅宗的南宗。而神秀在第二天知道了这件事以后,曾派人去追慧能,但没有追到。后来神秀成为梁朝的护国法师,创立了禅宗的北宗。

在我们小时候,最新被告知的就是这是表、那是电视机,于是我们知道了世界是丰富多彩的。现在我们进入面向对象的世界,所以作为一个新生儿我们要重新审视这个程序世界。我们把可以看得到摸得着的东西统称为对象。对象,不论是具体的还是抽象的,遍布于我们的周围,他们组成了整个世界。典型的现代软件都要模拟现实世界,至少是模拟现实世界的一个片段,通常程序需要模拟现实世界中的对象。传统的程序员能够在面向对象概念中得到很大的好处,因为面向对象的概念提供给他们工作领域的建模。

什么是对象?你现在看到的文档是对象,正在使用的电脑是对象,你现在拿着记笔记的笔也是对象。

思考:还有哪些是对象?请举出例子。

对象就是具有状态和行为的一个特定的个体。例如人的年龄、花的花瓣数量、汽车的牌子、学生的年龄、飞机的高度、骰子的点数„„这些都是对象的状态,它描述一个对象的特征。这些特征是可以量化的而且是可以变化的,我们可以使用之前学习过的数据类型来描述,例如int、float、String,这叫做属性。人可以吃饭、睡觉,汽车可以启动、行驶,飞机可以飞行等,这些体现了对象可以做什么,我们把它行为。

3,使用对象

对象和变量

程序中经常使用时间,尤其是当前系统的时间,如果想要构造一个时间对象,如下所示:

new Date()

这个表达式构造了一个新的以当前时间为默认值的日期对象。也可以把这个对象传递给一个方法:n(new Date())。在这个例子里构造的对象仅使用了一次,通常,我们可以重复使用一个对象,因此需要将对象存放在变量中:

Date birthday = new Date();

对象创建后如下图所示:

在变量和对象之间存在一个很重要的区别,例如语句

Date deadline;

定义了一个对象变量deadline,他可以引用Date类型的对象。但是一定要认识到,变量deadline不是一个对象,实际上也没有引用对象。此时不能将任对象的行为应用于这个变量上。如果执行

String s = ng();

语句将会出现编译错误。必须首先初始化变量deadline。

这里有两个选择:

1.变量引用新创建的对象 deadline = new Date();

2.引用已经存在的对象deadline = birthday,这样两个变量引用到了同一个对象。

一定要认识到,一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。在Java中,任何对象变量都是存储在另外一个地方的一个对象的引用。new操作符的返回值也是一个引用。下列语句:

Date deadline = new Date();

有两个部分。表达式new Date()创造了一个Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量deadline中。

可以显示的将对象变量设置为null,辨明这个对象变量目前没有引用任何对象。 deadline = null;

如果将一个方法应用于一个值为null的变量上,那么就会产生运行错误。

birthday = null;

String s = ng();//运行时异常

变量不会自动的初始化为null,而必须通过调用new或者将它们设置为null进行初始化。

使用对象

前面的例子我们创建了Date类的实例,即表示特定的时间点。尽管在使用Date类的时候不必知道这一点,但时间使用距离一个固定时间点的毫秒数表示的,这个点就是所谓的纪元(epoch),也就是UTC时间1970年1月1日的00:00:00。

在Java中Date类的实例就仅仅表示一个时间点,如果要对日期进行计算,就需要另外一个类了,叫做GregorianCalendar。

现行公历即格里历(英文:Gregorian calendar),亦有译为额我略历、格列高利历,是由意大利医生兼哲学家里利乌斯(Aloysius Lilius)改革儒略历制定的历法,由教皇格列高利十三世在1582年颁行。格里历是阳历的一种,于1912年开始在中国正式采用,取代传统使用的中国历法夏历(农历),而中国传统历法是一种阴阳历,因而格里历在中文中又称阳历、西历、新历。格里历与儒略历一样,格里历也是每四年在2月底置一闰日,但格里历特别规定,除非能被400整除,所有的世纪年(能被100整除)都不设闰日;如此,每四百年,格里历仅有97个闰年,比儒略历减少3个闰年。格里历的历年平均长度为365.2425日,接近平均回归年的365.24219日,即约每3300年误差一日,也更接近春分点回归年的365.24237日,即约每8000年误差一日;而儒略历的历年为365.25日,约每128年就误差一日。到1582年时,儒略历的春分日(3月21日)与地球公转到春分点的实际时间已相差10天。因此,格里历开始实行时,同时规定,原先儒略历1582年10月4日星期四的次日,为格里历1582年10月15日星期五,即有10天被删除,但原有星期的周期保持不变。格里历的纪年沿用儒略历,自传统的耶稣诞生年开始,称为“公元”,亦称“西元”。

GregorianCalendar类包含的方法要比Date类多得多。特别是有几个很有用的构造方法。

newGregorianCalendar()

表示构造一个新对象,用于表示对象构造时的日期和时间。

另外,还可以通过提供年、月、日构造一个表示某个特定日期的日历对象:

newGregorianCalendar(1999,ER,31)

还可以设置时间

new GregorianCalendar(1999,ER,31,23,59,59)

当然常常希望将构造的对象保存在对象变量中:

GregorianCalendar deadline = new GregorianCalendar(1999,ER,31)

GregorianCalendar类封装了属性,这些属性保存着设置的日期信息。不查看源代码不可能知道日期在类中的具体表达方式。当然,这一点并不重要,重要的是要知道类向外界开放的方法。

如何从封装在某个GregorianCalendar对象内部的日期中获得当前的日、月、年呢?如果希望对这些内容做一些修改,又该怎么办呢?要想查询这些设置信息,应该使用GregorianCalendar类的get 方法。为了表达希望得到的项,需要借助于Caldendar类中定义的一些常量

GregorianCalendar now = new GregorianCalendar();

int month = ();

int weekday = (_OF_WEEK);

调用相应的set 方法可以改变对象的状态:

(,2009);

(,);

(_OF_MONTH,15);

在Java 中对于要访问属性变量就是用get 方法,如果对其进行设置就是用set 方法。

什么是类

当我们认识了世界以后,就会被告知在天上飞的我们把它们叫做“鸟”;在水里游的,我们把它叫做“鱼”„„这就是最简单的归类。归类到原则是什么?是把具有相同或者相似特征和行为的对象归为一类,例如“在天上飞”“在水里游”这些特征。通常情况下我们把手机归为一类,虽然他们样子差别很大,颜色、重量、体积都是不一样的,但是他们具有相同的行为,就是可以通话。

类是一个抽象的集合概念,它代表具有相同特征、行为的个体的统称,例如手机、电脑、鼠标,猫、动物等。

思考:请指出下列哪些是对象,哪些是类

猫、加菲猫、诺基亚手机、苹果电脑、爱因斯坦、流浪猫、流浪狗

4,什么是类

在中国一直流传着女娲造人的经典神话传说。

盘古开辟了天地,用身躯造出日月星辰、山川草木。那残留在天地间的浊气慢慢化作虫鱼鸟兽,替这死寂的世界增添了生气。

这时,有一位女神女娲,在这莽莽的原野上行走。她放眼四望,山岭起伏,江河奔流,丛林茂密,草木争辉,天上百鸟飞鸣,地上群兽奔驰,水中鱼儿嬉戏,草中虫之豸跳跃,这世界按说也点缀得相当美丽了。但是她总觉得有一种说不出的寂寞,越看越烦,孤寂感越来越强烈,连自己也弄不清楚这是为什么。 与山川草木诉说心中的烦躁,山川草木根本不懂她的话;对虫鱼鸟兽倾吐心事,虫鱼鸟兽哪能了解她的苦恼。她颓然坐在一个池塘旁边,茫然对池塘中自己的影子。忽然一片树叶飘落池中,静止的池水泛起了小小的涟漪,使她的影子也微微晃动起来。她突然觉得心头的死结解开了,是呀!为什么她会有那种说不出的孤寂感?原来是世界是缺少一种像她一样的生物。

想到这儿,她马上用手在池边挖了些泥土,和上水,照着自己的影子捏了起来。她感到好高兴。

捏着捏着,捏成了一个小小的东西,模样与女娲差不多,也有五官七窍,双手两脚。捏好后往地上一放,居然活了起来。女娲一见,满心欢喜,接着又捏了许多。她把这些小东西叫作“人”。

这些“人”是仿照神的模样造出来的,气概举动自然与别的生物不同,居然会叽叽喳喳讲起和女娲一样的话来。他们在女娲身旁欢呼雀跃了一阵,慢慢走散了。

女娲那寂寞的心一下子热乎起来,她想把世界变得热热闹闹,让世界到处都有她亲手造出来的人,于是不停工作,捏了一个又一个。但是世界毕竟太大了,她工作了很久,双手都捏得麻木了,捏出的小人分布在大地上仍然太稀少。她想这样下去不行,就顺手从附近折下一条藤蔓,伸入泥潭,沾上泥浆向地上挥洒。结果点点泥浆变成一个个小人,与用手捏成的模样相似,这一来速度就快多了。女娲见新方法奏了效,越洒越起劲,大地就到处有了人。

女娲捏出来的个体的人就是对象,这些对象是依照女娲认为的“人”的样子捏出来的。这里的“人”就是指人类,对象被依照在思想里的一个雏形——“人类”被创造出来,但是每个被创造出来的人又不是一样的,因为他们的状态(属性)不同,于是有了丰富的大千世界。

在面向对象的设计程序中,我们就要去充当女娲的角色去创建对象的世界,所以我们得构造很多不同的对象。对象从哪里来呢?类!我们要首先定义出类来,也就是在创造对象出来之前,我们要知道对象应该是什么样子的。就好象盖楼需要有楼房建筑图纸,组装汽车有汽车的设计模型一样,创建对象就得先有对象的类。

我们试着去造一个人。想一下人都有什么样的共同特征:年龄、性别、姓名,这些描述的是这个类——人类的共同特征,就叫做属性,每个人(对象个体)在这些属性上是不一样的。关键字class表示创建一个类,Person是类的名字。在括号中是这个类的实例(对象)的状态(属性)和行为(方法)。例如这里定义了3个属性,分别是年龄、性别和姓名,这三个属性根据类型不一样,数据类型是不同的,姓名是文字,用的是字符串;性别只有男女之分,用的是boolean,非此即彼;年龄是数字,而且不可能是27.321岁,所以采用整数类型。

“人类”的框架设计出来了,怎么捏出人来呢?方法很简单:

Person niRen = new Person();

以上代码表示创建一个人类的实例(对象),叫做niRen。在这里new Person()是创建了一个对象,niRen是一个可以遥控Person类型的对象的引用。 对象跟引用的关系,相当于是电视机跟遥控器的关系。对象是具有自我意识的一个个体,我们想要对他下达命令就是通过使用遥控器来实现的。

思考:设计一个MP3类,并生成这个类的一个对象。

MP3具有的属性有哪些?品牌,型号,容量等。

每个对象之所以区别于别的对象,就在于这个对象具有属性值(状态)跟别的对象不一样,例如两个学生,他们的名字是不一样的。下面我们就得给对象的属性设置上不一样的值,可以使用如下代码:

= “奥巴马”;

= 48;

= true;

在这里要注意的就是根据属性的类型不一样,给不同的属性进行赋值的类型也不一样。在这里有个操作符“.”(小数点),表示“„„的”的含义。比如这里可以看作是niRen的name,这里name是类中定义的对象的一个属性。

思考:如下代码创建了几个对象?这几个对象的结构图是怎样的?

Person zhangFei = new Person();

Person guanYu = new Person();

= “张飞”; = 35; = true;

= “关羽”; = 36; = true;

name:关羽

age:36;

gender:true

name:张飞

age:35

gender:true

通过上图我们可以看出来,创建了2个对象,每个对象都有这个类定义的三个属性的一份拷贝,这样两个对象才能够区别开来。

实例变量的默认初始化

Static的含义

定义方法

什么是方法

一个对象除了有状态之外,最主要的是这个家伙可以做些什么事情。做一件事情是一个过程,在程序中我们都是使用函数去描述一个过程,于是在类中我们引入了方法(函数)。

在类中定义的方法表示这些方法是此类对象共同具有的行为,例如洗衣机类可以定义一个“洗衣”的方法,MP3播放器可以定义“播放音乐”、“收听广播”的方法„„我们把同类对象具有的方法放到类中,这样这个类的对象都可以使用这个方法了。

什么是构造方法

构造方法就是在类中的一个特殊的方法,它在对象被创建的时候调用,用来把对象进行初始化赋值到方法。它具有以下特点:

1. 方法名跟类名一致(大小写也要相同)

2. 没有返回值(返回值不是void)

例如Person类添加构造方法后的代码是:

public class Person{

privateint age;

方法名跟类名相同,没有返回值

在这里注意到是添加了构造方法Person(„„)方法名跟类名一致,同时Person(„„)没有返回值。

如何使用构造方法

按照上面的方法就可以这样使用构造方法创建对象了:

Person zhangFei = new Person(“张飞”,35,true);

上面这段代码什么意思呢?我们来看构造方法中的这段代码:

public Person(String a_name,inta_age,booleana_gender ) {

name = a_name;

age = a_age;

构造方法

gender = a_gender;

}

当调用new Person(“张飞”,35,true)的时候,以上构造方法被执行。“张飞”赋值给a_nanme,35赋值给a_age,true赋值给a_gender。在构造方法中name = a_name,把刚刚接受的值又赋值给了刚创建的对象的name属性,于是三个赋值下来相当于执行了一下3行代码:

= “张飞”;

= 35;

= true; 构造方法就像名字说的那样,在对象构造的时候调用的方法,把传进来的值赋值给对象个各个属性罢了。

THIS关键字

在以上构造方法中

public Person(String a_name,inta_age,booleana_gender ) {

name = a_name;

age = a_age;

构造方法

gender = a_gender;

}

参数变量为了避免跟类的属性重名,特意把名字改成了a_name、a_age、a_gender。如果不会怎样?

试一试:把变量a_name、a_age、a_gender改成name、age、gender会怎样?

在方法中如果局部变量跟属性变量重名了,那么根据就近原则属性变量就会访问不到了,这样我们就用this关键字来实现了。我们把代码改成如下所示:

public class Person{

privateint age;

privateboolean gender;

private String name;

public Person(String name,intage,boolean

gender ) {

= name;

= age;

构造方法

= gender;

}

public void sayHello(){

n(“Hello,I am” +

name);

}

}

在这里this表示当前对象,你可以替换的方式把this替换成正在使用的对象,例如当执行以下代码段的时候调用了构造方法:

Person zhangFei = new Person(“张飞”,35,true );

在构造方法中的this你可以把this等价替换为zhangFei,于是代码就成了了。当碰到this的时候都可以使用类似的方法来处理。

试一试:请根据构造方法的特点写出DICE类的构造方法。

构造方法总结

Person类在添加构造方法之前new Person()又是怎么回事呢?我们也没有写过一个构造方法叫做Person()啊?——这个问题的答案就是:当类中没有添加构造方法的时候,编译器会自动生成一个构造方法,这个构造方法没有参数,没有函数体,类似如下代码:

Person (){}

好了,到此构造方法我们就介绍完了,列出构造方法的几个特点:

1. 构造方法没有返回值

2. 方法名和类名一样

3. 作用是用于在new一个类对象的时候,会总先调用这个类的构造方法

4. 构造方法内部可以作一些变量的初始化或在创建这个类时必须调用的一些方法和运算

5. 他是不用特意去调用的方法,会随着类的创建而自动去调用

6. 在类中如果不写构造方法,编译器会自动产生一个空的构造方法

7. 如果已经给类添加了构造方法,则不会再产生空的构造方法

6,方法重载

为什么使用方法重载

再来回顾一下上面的例子,创建Person类的对象,就要给他传递三个参数,如果只想传递2个参数,其中一个保留不填行不行呢?如果只提供一个参数呢?那能不能提供多个构造方法哪?那就要考虑以下几个问题:

1. 因为构造方法名称跟类名相同,那所有的构造方法就重名啦?

2. 如果方法重名,怎么区别它们呢?

3. 如果有多个构造方法,我们应该用哪一个?

回答1. 因为构造方法名称跟类名相同,这是规定,所以必然要重名了,这叫做方法的重载

回答2. 添加多个构造方法的目的不就是初始化数量不一样的属性吗,那么区别当然是方法参数列表不一样啦。

回答3. 我们需要哪个就用哪个咯。

什么是方法重载

上面的例子,在同一个类中,方法的名字相同,参数的列表不一样就叫做方法的重载。请看下例:

public class Person{

privateint age;

privateboolean gender;

private String name;

重载的构造方法1

public Person(){

= “”;

= 0;

= true;

}

public Person(String name,intage,boolean gender ) {

= name;

= age;

重载的构造方法2

= gender;

}

重载的构造方法3

public Person(String name,int age){

= name;

= age;

= false;

}

public void sayHello(){

n(“Hello,I am” + name);

}

} 思考:现在类中几个构造方法?是否有空的构造方法?

如何使用构造方法

有了这么多构造方法,在程序里面就可以根据创建对象需要初始化的属性数量不一样,我们可以选择不同的构造方法:

Person zhangFei = new Person(“张飞”,35,true);

Person zhuGeLiang = new Person(“诸葛亮”,28);

这段代码中,选择了不同构造方法进行调用,第一行调用了构造方法2,第二行调用了构造方法3。

思考:使用第三个构造方法创建的对象性别是TRUE还是FALSE?

在类中,除了构造方法可以重载之外,其他的方法根据需要也可以重载。

public class Person{

privateint age;

privateboolean gender;

private String name;

public Person(){

= “”;

= 0;

= true;

}

public Person(String name,intage,boolean gender ) {

= name;

= age;

= gender;

}

public Person(String name,int age){

= name;

= age;

= false;

重载的sayHello方法

}

public void sayHello(){ n(“Hello,I am” + name);

}

7,理解包

我可以写一个类叫做Person,别人也可以写一个类叫做Person,我们的类怎么区别呢?想一想假如一个班里有两个人都叫张明,是不是叫起来很不方便?最好还是把他们分到两个班里去比较合适。就像这样,我们给类添加了包的概念,就好象把类放到了不同的包袱里面。在Java中把一个类放到包里的方法就是在类的第一行输入package XXX;

在Java中不同功能的类分别放到不同的包中,有的类是常用的就放在.包中,有点包用来创建GUI界面,放在包中„„这样在不同包中的类就不会应为名字相同而互相冲突啦。

8,继承和多态

类、超类、子类

生活中的继承

什么是继承

在生活中我们都学会了把对象进行归类,比如鱼类、鸟类,对于鱼类又可以分为淡水鱼类和海鱼,淡水鱼又分为观赏鱼、食用鱼„„整个世界都是被归类出来的。上图中是动物的归类,动物归为食草动物和食肉动物,食草动物又分成兔子、羊„„

上面的图体现了继承关系,也就是is-a(是一种)关系。例如狮子is a(是一种)食肉动物、兔子是一种食草动物,食肉动物是一种动物,类似的例子还有很多,例如自行车是一种交通工具,奇幻小说是一类文学作品等。“自行车是一种交通工具”中自行车相对于交通工具来说是子类,交通工具相对于自行车来说是父类,但是相对于“工具”类来说交通工具就是子类啦。

思考:根据父类和子类的关系列出生活中父类和子类的例子。

为什么使用继承

在程序代码中使用继承的关系可是给我们的程序提供很多好处。情况如下代码: 在前面的练习中我们学会了怎样写职员类(Employee),下面的代码就是职员类:

public class Employee{

private float salary;

private String name;

public Employee(String name,float salary){

= name;

= salary;

}

public String getName(){

return ;

}

public float getSalary(){

;

}

public void setName(String name){

= name;

}

public void setSalary(float salary){

= salary;

}

}

在程序中我还要创建一个经理类,经理类和职员类一样,都有姓名、工资,但是不一样的是经理类有一个特殊的属性,就是奖金。经理类在每个月进行考核,完成考核的话可以拿到一笔奖金,考核的方法就是请假的次数不得多于3次/月。

那再写出经理类

public class Manager{

private float salary;

private String name;

private float bonus;

public Manager(String name,float salary){

= name; = salary;

}

public String getName(){

return ;

}

public float getSalary(){

;

}

public void setName(String name){

= name;

}

public void setSalary(float salary){

= salary;

}

public void setBonus(float bonus){

= bonus;

}

public float getBonus(){

;

}

}

以上的两个类对比一下可以看到有百分之90以上的代码是相同的,两个类很相近,写出来重复的代码太多了,可是又不能没有,怎么办呢?

办法是使用继承。通过关键字extends就可以使一个类继承自另一个类,例如由于经理是职员的一种,所以可以设置经理类继承自职员类,代码如下所示:

public class Manager extends Employee{

„„„„„„„„

}

当Manager类通过关键字extends继承自Employee,那么Employee类的非私有属性和方法就被Manager继承下来了,就好象儿子继承了老子的财产一样,除了私有的物品儿子都可以自然的继承到,在子类Manager类(儿子)中就把这些属性(财产)继承到了。查看如下代码: Manager kongMing = new Manager();

= “诸葛亮”;

String name = e();

可以看到子类的对象具有了属性name,这个属性是从父类Employee继承下来的,还可以调用父类的方法getName()得到这个值,同样也是继承下来的。通过这个例子我们发现原来类里面很多相同的代码就不用再写了,仅仅需要把额外添加的方法加入到类里面,如下所示:

public class Manager extends Employee{

private float bonus;

public Manager(String name,float salary){

= name;

= salary;

}

public void setBonus(float bonus){

= bonus;

}

public float getBonus(){

;

}

}

类的代码变少了,但是功能却没有减少,用起来方便了。

何时使用继承

职员类写到这里就比较完整了。现在需要开发一个员工考勤管理系统,要求此系统能够按月把公司所有员工(包括普通员工、经理、总经理等)的考勤统计出来,根据一定的规则把所有人员的工资一起发放,发放的时候要根据不同员工的职务进行发放,把发放的所有结果在控制台上打印出来,格式如下:

姓名基本工资职位奖金请假(天)奖金扣除实发

诸葛亮5000 经理300 1 60 5240

赵云„„职员„„

分析

1. 这个系统是一个控制台应用程序,可以在控制台输入输出,用到了系统的输入输出方法n()和Scanner类。

2. 系统中不同的人是不同的角色,但是都是职员,所以经理类、总经理类都属于职员的一种,同样继承在职员类。

public class Director extends Manager{

„„

}

3. 应该有一个公司类,公司有一个列表存储着所有员工对象。

Employee [] staff= new Employee[20];

staff[0] = new Employee(“张飞”,35);

staff[1] = new Manager(“诸葛亮”,33);

staff[2] = new Director(“刘备”,39);

„„参见例子代码

方法重写

职员类的例子里面看到了,当有经理类扩展职员类就自动继承了父类的所有方法,这是很方便的,另外如果子类Manager有新的方法就可以直接添加在Manager类中,例如Manager类有对bonus类的存取方法get/set Bouns方法,就可以直接添加在Manager类中,这个方法将是Manager类有点,父类Employee是不能使用的。

然而在经理类(Manager)中我们曾经说过,经理的取得薪水的方法是薪水+奖金,这时候获得薪水的方法是用从父类继承下来的只有发薪水而不加奖金,这个方法不适用于Manager,怎么办呢?

有一种动物叫做鲸鱼,曾经是生存在陆地上的哺乳动物,由于体型庞大,在陆地上行走困难,从陆生哺乳动物经过水生的适应过程而回到海洋,此过程大约发生在距今3400万到5500万年的始新世。鲸被认作为掠食者。它们的食物品种分布从微生物到大型鱼类非常广泛。所有的哺乳动物,包括鲸都需要睡眠,但鲸为了呼吸而保持清醒只能使它们一个脑半球处于睡眠状态。所以鲸从不沉睡却能够得到所需的睡眠。鲸大约一天“睡”24小时。鲸鱼听觉非常发达,听力甚至可以达到数公里。鲸鱼为了适应水生的生活,一代一代的在改变种群的体型和行为,经过一代代的进化,他们的捕猎方式,生存方式已经发生了很大的变化,这凸显了种族的变异性。

在Manager类中,发薪水的方法与父类不一样,就是体现了子类迫切需要改变父类行为的要求,于是便有了方法的重写,一种体现子类改变父类行为的变异活动。

重写就是在子类中提供一个方法,这个方法跟父类的方法名相同,参数相同,返回值也相同,但是方法体的内容是不一样的,在子类使用这个方法的时候,不去调用父类的被覆盖掉的方法,而是使用子类中的方法,就好象父类的方法被重新定义了一样,所以这种情况我们称作方法的重写。

public class Manager extends Employee{

private float bonus;

privateintabsentDay;

public Manager(String name,float salary){

= name;

= salary;

}

public void setBonus(float bonus){

= bonus;

}

public float getBonus(){

;

}

public float getPay(){

if(absentDay< 3){

returngetSalary() + getBonus();

}

elset

returngetSalary();

}

}

多态

在公司里有普通职员、经理和总经理,他们在一定程度上都可以算作是职员,体现在代码上就是:

Manager kongMing = new Manager(“诸葛亮”,33);

可以替换成

Employee kongMing = new Manager(“诸葛亮”,33);

以上代码创建了一个叫诸葛亮的经理对象,把这个对象使用职员类的变量引用着,这体现了经理对象的一般性,也就是经理也是职员,所以可以使用职员变量引用,是父类的变量引用着子类的对象,把子类对象看作是父类的对象来处理了,因为子类对象必定具有父类的对象的特征。

思考:可丌可以用经理类的变量引用职员对象呢?

那跟直接赋值给子类引用变量又有什么差别呢?

1. 子类对象添加到方法没有了。在这个例子中,getBonus方法就没有了。虽然诸葛亮是个经理对象,但是我们把他一般化的看成了父类Employee类的对象,那么他的经理类的特有特征就没有了。

2. 重写的方法继续使用子类的方法。虽然把子类对象看作是了父类的对象,但是在执行子类重写的方法时候,仍然保持子类对象的变异性体现子类对象覆盖掉的方法的行为,这就是多态。

请看代码:

Employee [] staff= new Employee[20];

staff[0] = new Employee(“张飞”,35);

staff[1] = new Manager(“诸葛亮”,33);

staff[2] = new Director(“刘备”,39);

上面的代码创建了职员类的对象数组,职员类的对象可以放进数组里,经理类的对象可以当作职员类的对象,可以放在数组里,总经理同理也是,于是就可以把对象都放在Employee类型的数组中了。

foreach(Employee emp : staff){

n(());

}

执行以上代码发现不同类型的对象虽然放进了一个数组里,但是当执行方法的时候却能体现不一样方法,多态的特性就体现在这里了。

阻止继承

继承是好用的面向对象机制可以在写代码的时候非常节省,同时这种机制也要避免滥用。继承要体现is-a关系,就是XX是一种XX。为了避免类被任意继承,我们添加了一个关键字叫做final。

1. 当final加在类前面的时候,这个类就不能被继承了,例如

public class final Employee{„„„„}

如果写

public class Manager extends Employee{}

就会出现编译错误。 2. 如果final加在变量前,那么这个属性就是不可以被改写的,例如:

finalint i = 100;

如果再把i = 10;

那么也会出现编译错误。

OBJECT:所有类的超类

前面的例子我们学习了继承,继承是帮助代码进行重用的一种机制,在Java中,所有的类都有的特性如果能通过继承获得就好了,当然可以。在Java中有个潜规则,就是所有的类的必定继承自Object类,Java中所有类的超类。然而不需要你自己写

public class Employee extends Object{}

因为在Java中,如果你的类没有写继承自哪个父类的话,默认继承自Object。所有类都直接或者间接的继承自Object类,在代码中我们可以使用

Object obj = new Employee(“张飞”,35);

父类的引用指向一切Java类的对象。

在Object类中有很多方法,这些方法都被子类继承下来了,那么这些方法都有哪些呢?

EQUALS方法

equals方法是从object类继承下来的用来测试对象相等的方法。前面我们曾经学习过字符串的相等比较(“abcde”)就是使用的equals方法。equals 方法(是String类从它的超类Object中继承的)被用来检测两个对象是否相等,即两个对象的内容是否相等。==用于比较引用和比较基本数据类型时具有不同的功能:比较基本数据类型,如果两个值相同,则结果为true ,而在比较引用时,如果引用指向内存中的同一对象,结果为true。例如:s1 = new String("sony"); //创建的是字符串对象("sony"); //返回true

trues1 == "sony" //返回false //如果s1 = "sony"; s1 == "sony" //返回true

在程序中如果定义两个Employee相等,我们可以设置多种逻辑,比如认为名字相同就是相等的对象或者名字、薪水都相等才认为是相等的,这些都需要根据程序的需要。那么我们按照第二种写出equals方法

publicboolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != ss())

return false; Employee other = (Employee) obj;

if (name == null) {

if ( != null)

return false;

} else if (!())

return false;

if (oIntBits(salary) != oIntBits())

return false;

return true;

}

这是一种固定的写对象相等比较方法的路数,首先比较传入比较的对象是否为和当前对象是同一个对象(==),如果是同一个对象则必定相等;在测试传入的对象是否是null,为null则必不相等;再判断两个对象是否是同一个类,如果不是同一个类的对象也不相等,如果是同一个类则进行强制转换,比较属性就可以了。

HASHCODE方法

在JDK的规范中规定相对的对象他们的hashCode也要相等。什么是hashcode呢?就是一个对象的唯一标识符,就好象个人的身份证号码一样。既然相等的对象hashcode也要相等,那么重写了equals方法设置了相等的条件就也要把hashcode改变了。

TOSTRING方法

我们执行如下代码:

n(new Employee(“张飞”,35));

会有如下输出:

Employee@420cc980

输出一个对象后的结果并不好看,如果我想看到这个对象的详细内容,比如:“名字:张飞,年龄:35”就更好了。在n()打印一个对象的时候实际上是默认调用了对象的toString()方法,这个方法是从Object类继承下来的,默认的实现就是看到的——“Employee@420cc980”。改变它的方法不就是重写toString方法吗,根据自己的需要写出toString方法就可以随心所欲的按照自己的习惯输出一个对象了,代码如下:

public String toString() {

return "Employee [name=" + name + ", salary=" + salary + "]";

}

抽象类和接口

什么是抽象类 之前的例子我们写了Employee职员类和Manager经理类,依据这个想法,我们可以写出小时工、清洁员等这些职员类。再分析一下这个结构,我们发现Employee都是人,他们都有Person的特性,Person类可以作为Employee的父类,Person类还可以有多个子类,除了职员外还可以有自由职业者(Freelancer)等,Employee是职员,getPay的方法是发薪水得来的,Freelancer是自由职业者,他们可以通过自己的劳动获得收入,他们的getPay方法是另一种方式,但是Person类的getPay方法就没有任何的含义了,getPay方法提供在Person类里就是提供给子类去覆盖掉,本身不做任何有具体含义的事情,只能保证一个空白的方法什么也不写,等待子类去重写它,如下:

public class Person{

int age;

Person类的属性

boolean gender;

String name;

voidsayHello(){

n(“Hello,I am” +

name);

}

voidgetPay(){

//留空

}

}

为了避免出现一个什么也不做仅仅是占位的一个方法,我们把这个方法稍作修改,变成

abstract void getPay();

abstract表示抽象,写在方法的前面表示这个方法的体是抽象的,方法的体暂且先不写,留给这个类的子类继承的时候再覆盖。


发布者:admin,转转请注明出处:http://www.yc00.com/num/1702402157a1213459.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信