zoukankan      html  css  js  c++  java
  • 20145326 《Java程序设计》第4周学习总结

      

    20145326 《Java程序设计》第4周学习总结

    教材学习内容总结

    第六章

    一、何谓继承

    1.继承共同行为

    面向对象中,子类继承父类,避免重复的行为定义。不过并非为了避免重复定义行为就使用继承,滥用继承而导致程序维护上的问题时有所闻。如何正确判断使用继承的时机,以及继承之后如何活用多态,才是学习继承的重点。以书上158页的例子来看,magician中粗体字部分与swordsman中相对应的程序代码重复了,重复在设计上就是不好的信号,如果我们想将name、level、blood改为其他名称,那就要修改swordsman与magician两个类,如果有更多类具有重复的程序代码,那就要修改更多类,造成维护上的不便。如果要改进,可以把相同的程序代码提升为父类(Role)。这里引入了一个新的关键字“extends”。它表示SwordsMan与Magician会扩充Role的行为,也就是继承Role的行为。SwordsMan与Magician不止可以继承Role的行为,还可以扩充原来Role没有的行为。继承就是为了避免多个类间重复而定义共同行为。继承的好处之一就是若你要将name、level、blood改为其他名称,那就只要修改Role.java就可以了,只要是继承Role的子类都无需修改。这里要注意一点,private成员会被继承,只不过子类无法直接存取,必须通过父类提供的方法来存取。

    2.多态与is-a

    在java中,子类只能继承一个父类,继承除了可避免类间重复的行为定义外,还有个重要的关系,那就是子类与父类间会有is-a的关系,中文称之为“是一种”的关系。用前面范例来说,SwordsMan继承了Role,所以SwordsMan是一种Role。但如果这样编写代码就无法通过:SwordsMan swordsman=new Role();编译程序是语法检查器,要知道程序片段为何可以通过编译,为何不可以通过编译,就是将自己当作编译程序,检查语法的逻辑是否正确,方式是从=号右边往左读。那翻译过来就是:“Role是一种SwordsMan”,这显然错误,导致编译失败。编译程序检查这类语法,一次只看一行。 若: Role role1=new SwordsMan(); SwordsMan swordsman=role1;这也会编译失败。第一行没问题,翻译过来是“SwordsMan是一种Role”,正确。第二行中,因为role1为Role声明的名称,所以第二行翻译过来是“Role是一种SwordsMan”,这不一定,所以编译失败。如果想让程序住嘴,可以这样修改: Role role1=new SwordsMan(); SwordsMan swordsman=(SwordsMan)role1; 相当于就是让Role扮演SwordsMan,这段程序代码是可以通过编译的。 但这种情况就不行: Role role2=new Magician(); SwordsMan swordsman=(SwordsMan)role2; 其中role2参考的是Magician,你要让魔术师假扮为剑士,这在执行上会是个错误。使用是一种(is-a)原则,就可以判断何时编译成功,何时编译失败,以及将扮演看作编译程序住嘴语法。书上165页的范例为我们展示了一种多态的写法,这样的写法好处何在?就算有100种角色,只要他们都是继承Role,都可以使用这个方法显示角色的血量,而不需要像前面重载的方式,多态的写法显然具有更高的可维护性。什么叫多态?多态就是使用单一接口操作多种类型的对象,注意这里的接口并不是专指java中的interface,而是指对象上可操作的方法。

    3.重新定义行为

    如果现在有个需求,请设计static()方法,可以播放角色攻击动画,我们首先想到的是用drawfight()方法。但是对于drawfight()方法而言,只知道传进来的会是一种Role对象,所以编译程序也只能检查你调用的方法,Role是不是有定义,显然目前的Role没有定义fight()方法,所以编译错误。仔细观察一下SwordsMan与Magician的fight()方法,它们的方法签署都是public void fight(),也就是说操作接口是相同的,只是操作内容不同,于是我们可以将fight()方法提升至Role类中定义。在Role类中定义了fight方法(),由于实际上角色如何攻击,只有子类才知道,所以这里的fight()方法内容是空的,没有任何程序代码执行,只有当SwordsMan继承Role之后,再对fight()进行定义。在继承父类后,定义与父亲中相同的方法部署,但执行内容不同,这称为重新定义。注意如果传入drawfight()的是SwordsMan,role参考的就是SwordsMan实例(参考书上167范例),操作的就是SwordsMan上的方法定义。如果父类中定义的是fight(),但子类中定义的是Fight(),这就不是重新定义fight()了,而是子类重新定义了一个Fight()方法,这是合法的方法定义,可以通过编译,只不过我们会发现为什么SwordsMan没有挥动剑。在JDK5之后支持标注,其中一个内建的标准标注就是@override,表示要求编译程序检查,该方法是不是真的重新定义了父类中的某个方法,如果不是,就会引起编译错误。

    4.抽象方法,抽象类

    现在有这么一个问题,你没有任何方式强迫或提示子类一定要操作fight()方法,只能口头或在文件上告知,不过如果有人没有传达到、没有看文件,或文件看漏了呢?如果某方法区块中真的没有任何程序代码操作,可以使用abstract表示该方法为抽象方法,该方法不用撰写{}块,直接“;”结束即可。类中意愿若有方法没有操作,并且标示为abstract,表示这个类定义不完整,定义不完整的类就不能用来生成实例。这就好比设计图不完整,不能用来生产成品一样。子类如果继承抽象类,对于抽象方法有两种做法,一种做法是继续标示该方法为abstract,另一种做法就是操作抽象方法,如果两种做法都没有实施,就会引发编译错误。 二、继承语法细节

    1.protected成员

    根据书上170页的例子,定义的toString()在取得名称、等级与血量时不是很方便,因为Role中的name、level与blood被定义为private,所以无法直接在子类中存取,只能通过getName()、getLevel()、getBlood()来取得。将Role中的name、level与blood定义为public,这又会完全开放name、level与blood的访问权限,如果不想这么做,只是想让子类可以直接存取name、level与blood的话,可以定义它们为protected。被声明为protected的成员,相同包的类可以直接存取,不同包中的类可以在继承后的子类直接存取。如果方法中没有同名参数,this可以省略,不过基于程序可读性,多打个this会比较清楚。java中有三个权限字,分别是public、protected、private,虽然只有三个权限字,但是有四个权限范围,因为没有定义权限关键字,默认值就是包范围。权限依小至大来区分,就是private、无关键字、protected、public,如果一开始不知道使用哪个权限,就先使用private,以后视要求再放开权限。

    2.重新定义的细节

    有时候重新定义方法时,并非完全不满意父类中的方法,只是希望在执行父类中方法的前、后做点加工。在java中,如果想取得父类中的方法定义,可以在调用方法前,加上super关键字。可以用super关键字调用的父类方法,不能定义为private!因为这就限定只能在类内使用。重新定义方法要注意,对于父类中的方法权限,只能扩大不能缩小,若原成员为public,子类中重新定义时不可为private或protected!在JDK5之前,重新定义方法时除了可以定义权限较大的关键字外,其他部分必须与父类中方法签署完全一致。在JDK5之后,重新定义方法时,如果返回类型是父亲中方法返回类型的子类,也是可以通过编译的。static方法属于类拥有,如果子类中定义了相同署名的static成员,该成员属于子类所有,而非重新定义,static方法也没有多态,因为对象不会个别用有static成员。

    3.再看构造函数

    如果类有继承关系,在创建子类实例后,会先进行父类定义的初始流程,再进行子类中定义的初始流程,也就是创建子类实例后会先执行父类构造函数定义的流程,再执行子类构造函数定义的流程。构造函数可以重载,父类中可重载多个构造函数,如果子类构造函数中没有指定执行父类中哪个构造函数,默认会调用父类中无参数构造函数(书上174范例)。这里要注意一点,this()与super()只能择一调用,而且一定要在构造函数第一行执行。如果定义了有参数的构造函数,也可以加入无参数构造函数,即使内容为空也无所谓,这是为了日后使用上的弹性。

    4.再看final关键字

    如果在指定变量值后就不想再改变变量值,可以在声明变量时加上final限定,如果后续撰写程序时,自己或别人不经意想修改final变量,就会出现编译错误。如果对象数据成员被声明为final,但没有明确使用=指定值,那表示延迟对象成员值的设定,在构造函数执行流程中,一定要有对该数据成员指定值的动作,否则编译错误。class前也可以加上final关键字,如果class前使用了final关键字定义,那么表示这个类是最后一个了,不会再有子类,也就是不能被继承。定义方法时,也可以限定该方法为final,这表示最后一次定义方法了,也就是子类不能重新定义final方法。

    5.java.lang.Object

    在java中,子类只能继承一个父类,如果定义类时没有使用extends关键字指定继承任何类,那一定是继承java.lang.Object。任何类,追溯至最上层父类,一定是java.lang.Object,也就是java中所有对象,一定“是一种”object。任何类型的对象,都可以使用Object声明的名称来参考。这有什么好处呢,如果有个需求是使用数组收集各种对象,那该声明为什么类型呢?答案是object[]。我们之前见到的toString()方法与equals()方法都是object上定义的方法。java.lang.Object是所有类的顶层父亲,这代表了object上定义的方法,所有对象都继承下来了,只要不是被定义为final方法,都可以重新定义。instanceof运算符,它可以用来判断对象是否由某个类创建,左操作数是对象,右操作数是类,在使用instanceof时,编译程序还会来帮点忙,会检查左操作数是否在右操作数类型的继承架构中,执行时期,并非只有左操作数对象为右操作数类直接实例化才返回true。只要左操作数类型是右操作数类型的子类型,instanceof也会返回true。

    6.关于垃圾收集

    创建对象会占据内存,如果程序执行流程中已无法再使用某个对象,该对象就只是损耗内存的垃圾。对于不再有用的对象,JVM有垃圾收集机制(GC),收集到的垃圾对象所占据的内存空间,会被垃圾收集器释放。执行流程中,无法通过变量参考的对象,就是GC认定的垃圾对象。执行流程具体来说就是线程,目前你唯一接触到的线程就是main()程序进入点开始之后的主线程,关于垃圾收集本身就很复杂,不同的需求也会有不同的方法。

    7.再看抽象类

    撰写程序常常有些看似不合理但又非得完成的需求。有些不合理的需求,本身确实不合理,但有些看似不合理的需求,其实是可以通过设计来解决的。以书上186页的3个例子来说,第一个例子中的类定义不完整,print()、println()与nextInt()都是抽象方法,因为老板还没决定在哪个环境下执行游戏,所以如何显示输出、取得用户输入就不能操作,但我们可以先操作猜数字的流程,虽然是抽象方法,但是go()方法中,还是可以调用。其实只要创建出ConsoleGame的实例,执行go()方法过程中调用到print()、println()与nextInt()等方法时,都是执行ConsoleGame中定义的流程,完整的猜数字游戏就这样操作出来了。

    第七章

    一、何谓接口

    1.接口定义行为

    以书上195页为例,老板今天想开发一个海洋乐园游戏,当中所有东西都会游泳,谈到会游泳的东西,首先就想到鱼,上一章又刚学过继承,也知道继承可以运用多态,所以就会定义Fish类中有个swim()行为。但实际上每种鱼游泳方式都不同,所以将swim()定义为abstract。老板又说为什么都是鱼,人也会游泳啊,于是就再定义Human类继承Fish,但是Human继承Fish?不会很奇怪吗,对,程序不会抱怨什么,就目前为止,程序也可以执行,但是逻辑或设计上有不合理的地方。java中只能继承一个父亲,所以更强化了“是一种”关系的限制性。“所有东西”都会“游泳”,代表了“游泳”这个“行为”可以被所有东西拥有,而不是“某种”东西专属。对于“定义行为”,在java中可以使用interface关键字定义。接口可以用于定义行为但不定义操作,在书上197页的例子中,swim()方法没有操作,直接标示为abstract,而且一定是public,对象若想拥有swimmer定义的行为,就必须操作swimmer接口。类要操作接口,必须使用implements关键字,操作某接口时,对接口中定义的方法有两种处理方式,一是操作接口中定义的方法,二是再度将该方法标示为abstract。以java的语意来说,继承会有“是一种”的关系,操作接口则表示“拥有行为”。

    2.行为的多态

    会使用接口定义行为之后,也要再来当编译程序,看看哪些是合法的多态语法。如:Swimmer swimmer1=new Shark(); 这段代码是对的。判断方式是“右边是不是拥有左边的行为”。所以翻译过来就是“鲨鱼会游泳。”完全正确。而对于如下这种情况: Swimmer swimmer1=new Shark(); Shark shark=(Shark)swimmer;若没有括号里的内容,编译则无法通过。因为有swimmer行为的对象不一定是Shark,所以无法通过编译。括号里的内容就是要让程序闭嘴,有swimmer行为的对象,不一定是Shark,不过我现在就是要让swimmer扮演Shark!,这样可以通过编译,执行时期 确实swimmer也是参考Shark实例,所以没有错误。但如果是这种情况,就不行:Swimmer swimmer1=new Human();Shark shark=(Shark)swimmer;第二行中,swimmer实际上参考的是Human实例,无法通过编译。在你的设计中,会游泳的东西都拥有swimmer的行为,都操作了swimmer接口,只要是操作swimmer接口的对象,都可以使用范例(201页)中的doswim()方法。可维护性显然提高很多。

    3.解决需求变化

    我们都知道写程序要有弹性,要有可维护性。不过这确实是个抽象的问题,这里从最简单的定义开始,如果增加新的需求,原有的程序无须修改,只需针对新需求撰写程序,那就是有弹性,有可维护性的程序。在java中,类可以操作两个以上的类,也就是拥有两种以上的行为。当然需求是无止境的,原有程序框架也许确实可满足某些需求,但有些需求也可能超过了原有架构预留的弹性,一开始要如何设计才会有弹性,则必须靠经验和分析判断。也许你预先假设会遇上某些需求而设计了一个接口,但从程序开发至生命周期结束,该接口从未被操作过,或者仅有一个类操作过该接口,那么该接口也许就不必存在,你事先的假设也许就是过度预测需求。在不好的架构下要修改程序的话,很容易牵一发而动全身。对了!在java中,接口可以继承自另一个接口。 需求不断变化,架构也有可能因此而改变。好的架构在修改时,其实也不会全部的程序代码都被牵动,这就是设计的重要性。

    二、接口语法细节

    1.接口的默认

    在java中,可使用interface来定义抽象的行为与外观,如接口中的方法可声明为public abstract。如:public interface Swimmer{ public abstract void swim();} 接口中的方法没有操作时,一定得是公开且抽象,为了方便,你也可以省略public abstract,编译程序会自动帮你加上public abstract。书上208页下角有个范例特别好,这个代码肯定是无法编译成功的,因为Action中定义的execute()其实默认为public abstract,而some类在操作execute()方法时,没有撰写public,因此就是默认包权限,这等于是将Action中的public方法缩小为包权限,所以编译失败了,必须将some类的execute()改为public才通过编译。interface中还可以定义常数。如public interface Action{public static final int a=1;}java中经常见到在接口中定义这类常数,称为枚举常数。在interface中,也只能定义public static final的枚举常数。为了方便,撰写程序的时候可以省略,系统会自动帮你写上public static final,所以在接口中枚举常数,一定要使用=指定值,否则就会编译错误。要在类中定义枚举常数也是可以的,不过一定要明确写出public static final。类可以操作两个以上的接口,如果有两个接口都定义了某方法,而操作两个接口的类会怎样?程序面上来说,并不会有错误,照样通过编译。但还是要思考一下,如果表示不同的行为,那么在操作时,应该有不同的方法操作,some与other的execute()方法在名称上就得不同。如果表示相同的行为,那可以定义一个父接口,在当中定义execute()方法,而some与other继承该接口,各自定义自己的doSome()与doOther()方法(书上212有例子)。接口可以继承别的接口,也可以同时继承两个以上的接口,同样也是使用extends关键字,这代表了继承父接口的行为。在JDK8之后,如果父接口中定义的方法有操作,也代表了继承父接口的操作。

    2.匿名内部类

    经常会有临时继承某个类或操作某个接口并建立实例的需求,由于这类子类或接口操作类只使用一次,不需要为这些类定义名称,这个时候可以使用匿名内部类来解决这个需求,匿名内部类的方法为: new 父亲()|接口(){//类本体操作};JDK8出现前,如果要在匿名内部类中存取局部变量,则该局部变量必须是final,否则会发生编译错误。局部变量的生命周期往往比对象短,像是方法调用后返回对象,局部变量生命周期就结束了,此时再通过对象尝试存取局部变量会发生错误,java的做法是采用传值,实际上会在匿名内部类的实例中,建立新的变量参考原来的对象。

    3.使用enum枚举常数

    从JDK5之后,新增了enum语法,可用于定义枚举常数。根据书上217页的范例来看,使用enum定义枚举常数的过程中,enum实际上定义了特殊的类,继承java.lang.Enum,不过这是由编译程序处理,直接撰写程序继承Enum类会被编译程序拒绝。范例中的enum定义的Action实际上是个类,而enum中列举的STOP、RIGHT、LEFT、UP、DOWN常数实际是public static final,且为Action实例。你无法撰写程序直接实例化Action,因为构造函数权限设定为private,只有Action类中才可以实化。不过我们可以用这个Action来声明类型。play()方法中的action参数声明为Action类型,所以只接受Action.STOP、Action.RIGHT、Action.LEFT、Action.UP、Action.Down的传入,不像之前的play()方法,可以传入任何int值,case也只能列举Action实例,所以不用像之前范例,必须使用default在执行时检查,编译程序在编译时期会进行类型检查。

    教材学习中的问题和解决过程

    第六章主要讲的是继承与多态,内容其实挺简单,也没那么抽象,继承中的“父与子”的思想和“is-a”的概念我印象尤为深刻,这也是理解的突破口。代码编译也都挺顺利的。 多态就是使用单一接口操作多种类型的对象,注意这里的接口并不是专指java中的interface,而是指对象上可操作的方法。第七章主要讲的是接口,interface关键字是“定义行为”,而implements关键字是“拥有行为”的意思。通过接口拥有某种行为。 这两章概念都差不多掌握了,也将书上的代码都敲了一遍,感觉没什么较大问题,很开心~!代码已托管~

    代码调试中的问题和解决过程

    在敲代码的过程中,大部分都还是挺顺利的,有一小部分由于我的粗心,没有注意大小写等各种原因导致编译失败,但在经过修正后,也顺利通过编译了。将书上的代码都敲了一遍以后,我感觉对这两章的知识体会更深刻了。 代码全都托管成功~!

    其他(感悟、思考等,可选)

    其实java前三章的内容就是一些基本知识概念的介绍,起了个引入的作用。难度也都不是特别大。但从第四章就要开始接触java程序设计的核心知识了。上一周用了相当多的时间才圆满完成任务,虽然累,但很享受,很快乐。也给了我第四周学习的动力。第四周的任务是学习第六章和第七章,内容的难度和第四第五章其实差不多,看第一遍当然觉得有很多地方不懂,很陌生。这个时候就要静下心来,学java不是为了写一篇博客应付老师,完全没必要,老师和学生之间的关系应该是健身教练与健身学员之间的关系,要在学习中细心体会过程,寻找快乐,发现学习的真谛。最终促成一个良性循环那就再好不过了。这也是我最开始学java时所期待的效果。我现在就是先看一遍书,然后把不懂的都做上标记,然后看毕向东老师的视频,然后再看书,不断发现问题,不断解决问题,不断有新的感悟与收获,最终整理思绪,将其记录在博客上,与老师与同学共享。我想这也是娄嘉鹏老师的本意吧~ 这也是对我们的要求与期望! 学习的最高境界就是在快乐中学习,在学习中发现快乐。静下心来,无目的而和目的,继续努力吧!~世上无难事,是怕有心人。任何事情,万事开头难,但只要坚持下来了,一定会受益匪浅!!!

    学习进度条

      代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 3500行 20篇 300小时  
    第一周 120/120 1/1 14/14  
    第二周 340/460 1/2 14/28  
    第三周 200/660 1/3 14/42 学会了托管代码,学会了用构造模型的方法来了解类与对象这部分的知识。
    第四周 320/980 1/4 14/56  
  • 相关阅读:
    React元素渲染
    初识JSX
    微信小程序复制文本到剪切板
    微信小程序报错request:fail url not in domain list
    小程序,通过自定义编译条件,模拟推荐人功能
    积分抵扣逻辑
    微信小程序 switch 样式
    tomcat 配置开启 APR 模式
    tomcat8 传输json 报错 Invalid character found in the request target. The valid characters are defined in RFC 3986
    c++数组初始化误区
  • 原文地址:https://www.cnblogs.com/cxy1616/p/5313775.html
Copyright © 2011-2022 走看看