第3章面向对象上补充案例.docx
《第3章面向对象上补充案例.docx》由会员分享,可在线阅读,更多相关《第3章面向对象上补充案例.docx(37页珍藏版)》请在冰豆网上搜索。
第3章面向对象上补充案例
第三章补充案例
案例3-1定义学生类
一、案例描述
1、考核知识点
编号:
00103002
名称:
类和对象
2、练习目标
Ø掌握类定义的方式
Ø掌握如何在类中定义成员变量和成员方法
3、需求分析
在面向对象的思想中最核心就是对象,在程序中创建对象的前提是需要定义一个类。
为了让初学者掌握类的定义方式,本案例将设计一个表示学生的类,该类具有表示姓名的属性name和表示年龄的属性age,同时还具有表示说话行为的方法speak(),用于输出学生的姓名和年龄。
4、设计思路(实现原理)
1)使用class关键字定义一个表示学生类型的类,类名为Student。
2)在Student类中定义两个成员变量name和age,分别用来表示姓名和年龄。
其中,name的数据类型为String,变量age的数据类型为int。
3)在Student类中定义一个表示说话行为的speak()方法,用于输出学生的姓名和年龄。
二、案例实现
classStudent{
Stringname;
intage;
voidspeak(){
System.out.println("我的名字是"+name+",今年"+age+"岁");
}
}
三、案例总结
1、Java语言严格区分大小写,class和Class是不同的,在定义类时只能使用class关键字
2、在Student类中,成员变量name是String类型,String表示一个字符串,后面的章节会详细讲解
3、思考一下:
自己定义一个手机(Phone)类,在类中定义品牌(brand)和价格(price)属性,定义打电话的call()方法,代码如下所示
publicclassPhone{
Stringbrand;
doubleprice;
voidcall(){
System.out.println("hi,howareyoudoing");
}
}
案例3-2同一对象被多个变量引用
一、案例描述
1、考核知识点
编号:
00103003
名称:
对象创建与使用
2、练习目标
Ø掌握如何创建类的对象
Ø掌握如何使用两个或者多个变量引用同一个实例对象。
3、需求分析
在程序中,一个对象可能在多处使用,这样就可能需要有多个变量来引用这个对象。
为了让初学者更好地掌握对象的创建和使用,本案例将基于案例3-1,创建三个学生对象,它们的引用变量分别是s1、s2和s3,首先分别使用s1和s2引用,为name和age赋值,然后调用speak()方法,最后将s2变量赋值给s3,s3也调用speak()方法。
4、设计思路(实现原理)
1)编写Example01类
2)在main()方法中,创建Student类的第一个对象,其引用变量为s1,使用s1调用name和age变量分别为它们赋值为“张三”和“19”,然后调用speak()方法。
3)创建Student类的第二个对象,其引用变量为s2,使用s2分别为name和age赋值为“李四”和“20”,然后调用speak()方法。
4)创建Student类的第三个对象,其引用变量为s3,将s2的值赋给s3,然后使用s3调用speak()方法。
二、案例实现
publicclassExample01{
publicstaticvoidmain(String[]args){
Students1=newStudent();
s1.name="张三";
s1.age=19;
s1.speak();
Students2=newStudent();
s2.name="李四";
s2.age=20;
s2.speak();
Students3=newStudent();
s3=s2;
s3.speak();
}
}
运行结果如图3-1所示。
图3-1运行结果
三、案例总结
1、Students3=s2这句代码的作用是将s2引用的内存地址赋值给s3,换句话说,就是使变量s3和s2引用了同一个Student对象,因此s3.speak()方法和s2.speak()方法打印的结果相同。
为了更加深刻地了解这句代码的含义,下面通过一张内存图来演示,具体如图3-2所示。
图3-2内存图
2、可以使用两个或者多个变量引用同一个实例对象,只要通过其中一个变量对该对象的属性进行修改,使用其它引用变量访问时,访问的都是修改后的属性。
案例3-3类的封装
一、案例描述
1、考核知识点
编号:
00103004
名称:
类的封装
2、练习目标
Ø了解为什么要对类进行封装
Ø了解如何实现类的封装
3、需求分析
在案例3-2中,s1对象的年龄是可以随便赋值的,如果将age的值赋值为-30,显然违背了事实。
为了解决这类问题,我们需要对类进行封装,防止外界对类中的成员变量随意访问。
为了让初学者更好地掌握类的封装,本案例将使用private关键字对成员变量name和age进行私有化,同时分别提供一个setName(Stringn)和setAge(inta)方法用于外界的访问,其中setAge(inta)中需要对age进行判断。
4、设计思路(实现原理)
1)编写测试类Example02,将属性age的值设为-30,演示不合理现象。
2)对Student类进行修改,将name和age属性使用private修饰,然后定义getName()、setName(Stringn)、getAge()和setAge(inta)四个对外访问name和age的方法。
3)在setAge(inta)方法中对传入的参数进行检查,如果输入值为负数,则打印出“设置的年龄不合法”,如果不为负数,才将其设置为age属性的值。
4)对Example02类进行修改,在main()方法中创建Student类的实例对象,通过调用对象的setName(Stringn)和setAge(inta)方法来设置的name属性和age属性值,并调用speak()方法。
二、案例实现
1、定义Example02类,代码如下所示:
publicclassExample02{
publicstaticvoidmain(String[]args){
Students1=newStudent();
s1.name="小新";
s1.age=-30;
s1.speak();
}
}
运行结果如图3-3所示。
图3-3运行结果
从上图可以看出,当将age的值设置为-30后,程序不会报错,但却违背了现实。
2、对Student类进行封装,其代码如下:
classStudent{
privateStringname="张三";
privateintage=19;
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringn){
name=n;
}
publicintgetAge(){
returnage;
}
publicvoidsetAge(inta){
//对传入的参数进行检查
if(a<0){
System.out.println("设置的年龄不合法");
}else{
age=a;
}
}
voidspeak(){
System.out.println("我的名字是"+name+",今年"+age+"岁");
}
}
publicclassExample02{
publicstaticvoidmain(String[]args){
Students1=newStudent();
s1.setName("小新");
s1.setAge(-30);
s1.speak();
}
}
运行结果如图3-4所示。
图3-4运行结果
三、案例总结
1、Student的name和age属性使用private关键字修饰为私有后,在Example02类中不能再使用s1.name和s1.age的方式访问这两个属性,只能通过public类型的setName(Stringn)和setAge(inta)方法进行访问。
在上面的代码中,调用setAge(inta)方法时的传入参数为-30,由于参数小于0,会打印出“设置的年龄不合法”,并不会将负数赋值给age属性。
由此可见,只要实现了封装就能对外界的访问进行控制,避免对私有变量随意修改而引发问题。
2、思考一下:
定义一个Division类(除法),类中定义两个int类型的私有成员变量dividend(被除数)和
divisor(除数),默认值都为1。
定义四个公有方法setDividend(intmDividend)、getDividend()、setDivisor(intmDivisor)和getDivisor(),用于对私有属性进行设置和访问。
在setDivisor(intmDivisor)方法中对传入的参数进行检查,如果传入值为零,则打印“除数不能为零”,如果不为零,才将其设置为divisor属性的值。
定义Example03类,在类的main()方法中创建Division对象,分别调用setDividend(intmDividend)和setDivisor(intmDivisor)方法给两个私有属性dividend和divisor赋值,然后打印出dividend和divisor的结果。
(1)定义Division类,代码如下所示:
publicclassDivision{
privateintdividend=1;
privateintdivisor=1;
publicvoidsetDividend(intmDividend){
dividend=mDividend;
}
publicintgetDividend(){
returndividend;
}
publicvoidsetDivisor(intmDivisor){
if(mDivisor==0){
System.out.println("除数不能为零");
}else{
divisor=mDivisor;
}
}
publicintgetDivisor(){
returndivisor;
}
}
(2)定义Example03类,代码如下所示:
publicclassExample03{
publicstaticvoidmain(String[]args){
Divisiondivision=newDivision();
division.setDividend(10);
division.setDivisor(0);
intdividend=division.getDividend();
intdivisor=division.getDivisor();
System.out.println(dividend/divisor);
}
}
运行结果如图3-5所示。
图3-5运行结果
从运行结果可以看出,由于实现了Division类的封装,在setDivisor(intmDivision)方法中对传入的值进行检查,从而避免程序中出现除数为0的错误。
案例3-4定义有参的构造方法
一、案例描述
1、考核知识点
编号:
00103005
名称:
构造方法的定义
2、练习目标
Ø掌握有参构造方法的定义方式
Ø理解系统会自动分配无参构造方法的情况
3、需求分析
如果希望在创建对象的时候直接为其属性赋值,可以定义有参的构造方法。
有参构造方法指的是在初始化对象时,接受外部传入的值并赋给对象的属性。
为了让初学者掌握有参构造方法的用法,本案例将演示如何使用有参构造方法完成对象属性的初始化。
4、设计思路(实现原理)
1)定义一个Student类,该类有一个age属性,在类中定义一个有参数的构造方法,该参数用于为age属性赋值。
2)编写一个测试类Example04,在main()方法中通过有参构造方法创建一个对象。
3)打印该对象age属性的值。
二、案例实现
1、对Student类进行修改,代码如下所示:
classStudent{
intage;
publicStudent(intmAge){
age=mAge;
}
}
2、定义Example04类,代码如下所示:
publicclassExample04{
publicstaticvoidmain(String[]args){
Students1=newStudent(20);
System.out.println("age属性的值为:
"+s1.age);
}
}
运行结果如图3-6所示。
图3-6运行结果
三、案例总结
1、从运行结果可以看出,newStudent(20)语句调用了有参的构造方法Student(intmAge),动态地将20传递给了age属性。
和普通方法一样,构造方法中同样可以接收多个参数,只要在使用new关键字创建对象时,传入数量相同和类型一致的参数,就可以自动地调用对应的构造方法。
2、思考一下:
在Example04的main()方法中是否能够使用newStudent()创建对象呢?
答案是否定的,因为newStudent()会调用无参的构造方法,而本案例的Student类中并没有定义无参的构造方法。
有些同学肯定会问,之前的Student类都没有定义无参的构造方法,却能使用newStudent()创建对象,本案例为什么不行呢?
这是因为一个类中如果没有定义构造方法,系统会默认为其分配一个方法体为空的无参构造方法,而一旦定义了构造方法,系统就不再提供默认的构造方法。
本案例中由于我们定义了一个有参的构造方法,所以系统不会默认分配无参的构造方法,此时如果通过newStudent()去调用无参的构造方法,程序就会发生错误。
案例3-5构造方法的重载
一、案例描述
1、考核知识点
编号:
00103006
名称:
构造方法重载
2、练习目标
Ø掌握如何在类中定义重载的构造方法
3、需求分析
和普通方法一样,构造方法也可以重载。
不同的构造方法,可以为不同的属性进行赋值。
本案例将通过创建对象的方式演示不同构造方法的使用方式,并根据构造方法的输出结果对构造方法的重载进行学习。
4、设计思路(实现原理)
1)对Student类进行修改,在类中定义三个重载的构造方法,包括无参的构造方法,接收一个String类型参数的构造方法,接收String类型和int类型两个参数的构造方法。
2)编写测试类Example05,在main()方法中,分别使用三个重载的构造方法创建三个Student对象。
二、案例实现
1、对Student类进行修改,代码如下所示:
classStudent{
publicStudent(){
System.out.println("无参的构造方法");
}
publicStudent(Stringname){
System.out.println("一个参数的构造方法");
}
publicStudent(Stringname,intage){
System.out.println("两个参数的构造方法");
}
}
2、定义Example05类,代码如下所示:
publicclassExample05{
publicstaticvoidmain(String[]args){
Students1=newStudent();
Students2=newStudent("Rose");
Students3=newStudent("Rose",23);
}
}
运行结果如图3-7所示。
图3-7运行结果
三、案例总结
一个类中可以定义多个重载的构造方法,在创建对象时,根据传入参数的不同会调用相应的构造方法。
案例3-6this关键字访问构造方法
一、案例描述
1、考核知识点
编号:
00103007
名称:
this关键字的使用
2、练习目标
Ø掌握如何在构造方法中使用this关键字访问重载的构造方法
3、需求分析
如果一个类中定义了多个重载的构造方法,为了避免在重载的构造方法中重复书写代码,可以在一个构造方法中使用this关键字调用其它的构造方法。
为了让初学者掌握this关键字访问构造方法的用法,本案例将演示如何使用this关键字调用其他的构造方法。
4、设计思路(实现原理)
1)在Student类中创建多个重载的构造方法,包括无参的构造方法和一个参数的构造方法,以及两个参数的构造方法。
2)在一个参数的构造方法中使用this关键字调用无参构造方法,在两个参数的构造方法中调用一个参数的构造方法。
3)编写测试类Example06,在main()方法中,调用两个参数的构造方法创建对象,演示构造方法的执行顺序。
二、案例实现
1、对Student类进行修改,代码如下所示:
classStudent{
publicStudent(){
System.out.println("无参的构造方法");
}
publicStudent(Stringname){
this();
System.out.println("一个参数的构造方法");
}
publicStudent(Stringname,intage){
this(name);
System.out.println("两个参数的构造方法");
}
}
2、定义Example06类,代码如下所示:
publicclassExample06{
publicstaticvoidmain(String[]args){
Students1=newStudent("Jack",22);
}
}
运行结果如图3-8所示。
图3-8运行结果
三、案例总结
1、从运行结果可以看出,三个构造方法都被调用了,为了更加清楚地了解三个构造方法的执行顺序,下面通过一张图例进行说明,如图3-9所示。
图3-9构造方法的执行顺序
2、在构造方法中,使用this调用重载构造方法的代码必须放在第一行,否则程序不能通过编译,这就限定了在一个构造方法中只能调用一次重载的构造方法。
3、在构造方法中可以通过this.方法名([参数…])的方式调用普通的成员方法,但是在普通的成员方法中不能使用this([参数…])的方式来调用构造方法。
案例3-7垃圾回收机制
一、案例描述
1、考核知识点
编号:
00103008
名称:
垃圾回收机制
2、练习目标
Ø掌握垃圾回收机制的特点
Ø掌握垃圾回收相关的方法
3、需求分析
垃圾对象会占用一定的内存空间,当垃圾对象积累到一定程度后,Java虚拟机会自动进行垃圾回收。
但是,如果希望程序可以及时通知Java虚拟机回收垃圾对象,可以通过System.gc()方法强制启动垃圾回收器回收垃圾。
为了让初学者熟悉垃圾回收机制,本案例将演示如何通过System.gc()方法强制启动垃圾回收器回收垃圾。
4、设计思路(实现原理)
1)对Student类进行修改,在类中对finalize()方法进行重写。
2)编写测试类Example07,创建若干个Student对象,然后调用System.gc()方法通知垃圾回收期回收垃圾,为了确保可以看到垃圾回收的过程,可以在类中编写一个简单的循环语句,延长程序执行时间。
二、案例实现
1、对Student类进行修改,代码如下所示:
classStudent{
publicvoidfinalize(){
System.out.println("垃圾对象被回收了");
}
}
2、定义Example07类,代码如下所示:
publicclassExample07{
publicstaticvoidmain(String[]args){
newStudent();
newStudent();
System.gc();
for(inti=0;i<1000000;i++){
//延长程序结束时间
}
}
}
运行结果如图3-10所示。
图3-10运行结果
三、案例总结
1、从运行结果可以看到,两个Student对象的finalize()方法都被调用了,这表示两个对象作为垃圾被回收了。
如果把System.gc()这行代码注释,会发现命令行窗口不会打印任何内容,这说明对象在变成垃圾后不会被立即回收,同时也验证了System.gc()方法的作用。
2、由于System.gc()方法只是通知Java虚拟机尽快进行垃圾回收,这意味着垃圾回收器也可能不会马上运行,因此,在程序的最后使用了一个for循环来延长程序运行的时间,从而确保能够看到垃圾对象被回收的过程。
3、Student类中定义的finalize()方法其签名必须是public(protected)voidfinalize()[throwThrowable]{},这样做的原因会涉及到后面的一些知识,比如类的继承、Object类、方法的重写、异常等等,同学们在学完这些内容后就会明白其中的道理。
案例3-8静态变量
一、案例描述
1、考核知识点
编号:
00103009
名称:
静态变量
2、练习目标
Ø了解静态变量的作用
Ø掌握静态变量的定义和使用方式
3、需求分析
当多个对象存储的数据相同时,可以使用静态变量的方式存储。
例如,有一个Student类具有name、className属性,请根据该类创建出三个Student对象,并将这些对象的className值都设置为“三年级二班”。
4、设计思路(实现原理)
1)定义Student类,并在类中定义name和className属性。
2)编写测试类Example08,在main()方法中创建三个学生对象,并分别为这些对象的name和className属性赋值,然后输出这些对象的name和className值。
3)对Student类进行修改,将className定义为静态变量。
4)修改测试类,在main()方法中使用Student.className=“三年级二班”语句为静态变量className进行赋值,然后输出这些对象的name和className值。
为了更好地理解Student类中静态变量className和Student实例对象的关系