【考点 Equals】
==
是引用等价性 ;而equals()
是对象等价性。==
比较的是索引。更准确的说,它测试的是指向相等(referential equality)。如果两个索引指向同一块存储区域,那它们就是==的。对于我们之前提到过的快照图来说,==
就意味着它们的箭头指向同一个对象。equals()
操作比较的是对象的内容,换句话说,它测试的是对象值相等(object equality)。e在每一个ADT中,quals操作必须合理定义。
- 基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
- 他们之间的比较,应用双等号(==),比较的是他们的值。
- 复合数据类型(类)
- 当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。
- JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
- 对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
- HahCode:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。
当我们当向集合中插入对象时,就可以使用hashcode,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了。
- ⋅ Java中的equals与hashCode方法(判断插入容器的内容是否重复)
- ⋅ String和equals()、hashCode()
- ⋅ java中对象的equals和hashcode覆盖原则
- ⋅ HashMap中的equals和hashCode
- ⋅ java的equals、hashcode和Clone方法
- ⋅ Java中的equals和hashCode方法详解
- ⋅ 学习笔记-JAVA-考点10-什么情况下需要重写equals和hashcode()两个方法?
【考点 函数规约】 requires与effects
声明式规约更有价值 ; 内部实现的细节不在规约里呈现,而放在代码实现体内部注释里呈现,例:
static String join(String delimiter, String[] elements) effects: returns concatenation of elements in order, with delimiter inserted between each pair of adjacent elements // Declarative specs
更强的规约包括更轻松的前置条件和更严格的后置条件;
方法前的注释也是一种规约,但需人工判定其是否满足。
- 参数由@param 描述
- 子句和结果用 @return 和 @ throws子句 描述
- 尽可能的将前置条件放在 @param 中
- 尽可能的将后置条件放在 @return 和 @throws 中
【考点 ADT的四种类型】
- Creators(构造器):
- 创建某个类型的新对象,⼀个创建者可能会接受⼀个对象作为参数,但是这个对象的类型不能是它创建对象对应的类型。可能实现为构造函数或静态函数。(通常称为工厂方法)
- t* -> T
- 栗子:Integer.valueOf(Object obj):object → integer
- Producers(生产器):
- 通过接受同类型的对象创建新的对象。
- T+ , t* -> T
- 栗子:String.concat( ) :String x String → String
- Observers(观察器):
- 获取抽象类型的对象然后返回一个不同类型的对象/值。
- T+ , t* -> t
- 栗子:List.size( ) : List → int
- Mutators(变值器):
- 改变对象属性的方法 ,
- 变值器通常返回void,若为void,则必然意味着它改变了对象的某些内部状态;当然,也可能返回非空类型
- T+ , t* -> t || T || void
- 栗子:List.add( ) :List x int → List
【考点 ADT的 AF与 RI】
在研究抽象类型的时候,先思考一下两个值域之间的关系:
- 表示域(rep values)里面包含的是值具体的实现实体。一般情况下ADT的表示比较简单,有些时候需要复杂表示。
- 抽象域(AF)里面包含的则是类型设计时支持使用的值。这些值是由表示域“抽象/想象”出来的,也是使用者关注的。
R->A的映射特点:
- 每一个抽象值都是由表示值映射而来 ,即满射:每个抽象值被映射到一些rep值
- 一些抽象值是被多个表示值映射而来的,即未必单射:一些抽象值被映射到多个rep值
- 不是所有的表示值都能映射到抽象域中,即未必双射:并非所有的rep值都被映射。
在描述抽象函数和表示不变量的时候,注意要清晰明确:
- 对于RI(表示不变量),仅仅宽泛的说什么区域是合法的并不够,你还应该说明是什么使得它合法/不合法。
- 对于AF(抽象函数)来说,仅仅宽泛的说抽象域表示了什么并不够。抽象函数的作用是规定合法的表示值会如何被解释到抽象域。作为一个函数,我们应该清晰的知道从一个输入到一个输入是怎么对应的。
【考点:黑盒、白盒框架】
- 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
- 为了增加代码的复用性,可以使用委派和继承机制。同时,在使用这两种机制增加代码复用的过程中,我们也相应地在不同的类之间增加了关系(委派或继承关系)。而对于一个项目而言,各个不同类之间的依赖关系就可以看做为一个框架。一个大规模的项目可能由许多不同的框架组合而成。
【白盒框架】
- 白盒框架是基于面向对象的继承机制。之所以说是白盒框架,是因为在这种框架中,父类的方法对子类而言是可见的。子类可以通过继承或重写父类的方法来实现更具体的方法。
- 虽然层次结构比较清晰,但是这种方式也有其局限性,父类中的方法子类一定拥有,要么继承,要么重写,不可能存在子类中不存在的方法而在父类中存在。
- 软件构造课程中有关白盒框架的例子:
【黑盒框架】
- 黑盒框架时基于委派的组合方式,是不同对象之间的组合。之所以是黑盒,是因为不用去管对象中的方法是如何实现的,只需关心对象上拥有的方法。
- 这种方式较白盒框架更为灵活,因为可以在运行时动态地传入不同对象,实现不同对象间的动态组合;而继承机制在静态编译时就已经确定好。
- 黑盒框架与白盒框架之间可以相互转换,具体例子可以看一下,软件构造课程中有关黑盒框架的例子,更改上面的白盒框架为黑盒框架:
【两者对比】
- 白盒框架利用subclassing:
- 允许扩展每一个非私有方法
- 需要理解父类的实现
- 一次只进行一次扩展
- 通常被认为是开发者框架
- 黑盒框架使用委派中的组合composition:
- 允许在接口中对public方法扩展
- 只需要理解接口
- 通常提供更多的模块
- 通常被认为是终端用户框架,平台
【LSP】
- 里氏替换原则的主要作用就是规范继承时子类的一些书写规则。其主要目的就是保持父类方法不被覆盖。子类的规约变强!
- LSP依赖于以下限制:
- 前置条件变弱或者不变
- 后置条件变强或者不变
- 不变量要保持或增强
- 子类型方法参数:逆变(规约变强,前置变弱 反着变)
- 子类型方法的返回值:协变(规约变强,前置变强 协同变化)
- 异常类型:协变
- 数组是协变的,向上转型是成立的
Fruit[] apples=new Apple[size];
- 泛型是类型不变的(泛型不是协变的)。举例来说
ArrayList<String>
是List<String>
的子类型List<String>
不是List<Object>
的子类型
- 类型擦除的结果: <T>被擦除 T变成了Object
【Throwable】
- 健壮性:输入错误,给出确定的正常的输出,倾向于容错;正确性:出现错误报出错误,倾向于error。
- 对外的接口,倾向于健壮性;对内的实现,倾向于正确性。
- 运行时异常:由程序员处理不当造成,如空指针、数组越界、类型转换
- 其他异常:程序员无法完全控制的外在问题所导致的,通常为IOE异常,即找不到文件路径等
- checked异常:
- checked exception是需要强制catch的异常,你在调用这个方法的时候,你如果不catch这个异常,那么编译器就会报错,比如说我们读写文件的时候会catch IOException,执行数据库操作会有SQLException等。
- unchecked异常:
- 这种异常不是必须需要catch的,你是无法预料的,比如说你在调用一个 list.szie()的时候,如果这个list为null,那么就会报NUllPointerException,而这个异常就是 RuntimeException,也就是UnChecked Exception
- 常见的unchecked exception:JVM抛出,如空指针、数组越界、数据格式、不合法的参数、不合法的状态、找不到类等