面向对象编程(Object-Oriented Programming)——将ADT的接口和其实现分离开来,在Java之中通过Java的接口来实现。并且通过接口来定义ADT,然后在用具体的类去实现ADT。
一. 面向对象的标准
一个面向对象编程的语言应当有着一个有关类的中心理论。这样的编程语言应当使其类和特征具有断言(assertion)(规约,前提,后置和不变量)和异常处理,并且能依靠工具来针对这些断言产生文档,还能在运行时可选地进行监视。
· 这些帮助开发一个可靠的软件
· 这些提供了一个系统的文档
· 这是对于面向对象的软件进行测试和debug的主要工具
静态类型(Static typing):一个定义良好的类型系统应该通过执行一些类型声明和兼容性规则,确保它所接受的系统的运行时类型安全。
泛型(Genericity):“适应变化”并且是“复用性设计”
继承(Inheritance):应该可以定义类之间的继承关系,以此来控制由此产生的潜在的复杂性
多态(Polymorphism):在基于继承的类型系统的控制下,应该可以将实体(表示软件文本中的运行时对象的名称)与各种可能类型的运行时对象相关联。
动态分派/绑定(Dynamic dispatch/binding):在实体上调用一个特性总是应该引起与相关联的运行时对象类型相对应的特性,这在不同的调用执行过程中不一定相同。
(这英语看的我头疼)
二. 基本的理论:对象(object)、类(class)、属性(attribute)、方法(method)
对象:现实世界中的任何事物都有两种特征——状态(state)和行为(behavior)。如何区分现实世界的状态和行为会帮助我们更好的理解OOP
· 狗有很多种状态(名字、颜色、品种、饥饿程度)和行为(吠、摇尾巴等)
因此我们在运用OOP时需要知道有哪些状态和行为。
一个对象就是不同的状态和行为的集合体(Bundle),在编程之中。
· 状态——对象内部的数据,在java之中表现为域(fields)
· 行为——对象所支持的操作,在java之中称之为方法(method)
类:
每一个对象都有一个类
· 类定义了方法和域
· 方法和域统称为成员
类不仅仅定义了类型(type),也定义了实现方式(implementation)
· 类型 ≈ 对象的使用地点
· 实现方式 ≈ 对象能做些什么
宽泛地讲,一个类的方法即它的应用程序编程接口(API,Application Programming Interface)
类成员变量(Class variable):是一个与类相关联的变量而不是与类的实例相关联。我们也可以将类中的方法与类相关联——称之为类方法(Class method)。(Static)
· 为了引用类成员变量和类方法,我们可以通过类的名字和所引用的东西之间通过’.’来调用
e.g. String.valueOf()
那些不是类的成员变量和方法的称之为实例方法(instance method)和实例变量(instance variable)。
· 为了引用实例方法和变量,必须要通过类的实例来调用
总结:类成员变量和类方法是与类相关联的,并且只在类之中出现一次,并且不需要生成相应对象;而实例变量和方法在每个类的实例之中都会出现一次。
三. 接口(Interface)
Java的接口是一种设计和实现ADT的实用的语言表达机制,它的具体实现是通过一个类来实现。
· Interface和Class:定义和实现ADt
· 接口之间可以继承
· 一个类可以实现多个接口;同样的,一个接口可以有多个实现
e.g.
接口与类:
接口:确定ADT的规约 类:实现ADT
注意:类确实可以定义ADT,但是更加倾向于接口
· 公共的成员变量可能会导致一些错误
· 接口之中的变量和参数的使用是建立在一个实现能满足的情况下
· 接口会支持实现方式的改变
· 接口能使实现的细节具有独立性
final关键字:
final 域:防止一个域在初始化之后再被重新赋值
final 方法:防止重写
final 类:防止被继承
方法的重写:是一种语言的特征,它允许子类能针对在父类之中出现过的方法用另外一种特定方式去重新实现。
· 重写的函数:完全一样函数名、返回值、参数
· 实际执行时调用哪个方法,在运行时决定。如果是父类的对象调用了方法,那么就使用父类之中的那个版本;如果是子类的对象调用了那个方法,就使用子类之中的版本。
(2)抽象类(Abstract Class)
抽象方法(Abstract Method):一个方法只有一个声明,但是没有具体的实现方式;由关键字abstract定义
抽象类(Abstract Class):一个至少含有一个抽象方法的类
接口:一个只含有抽象方法的抽象类
具体类 -> 抽象类 -> 接口
(3)重载
重载方法让程序员在同一个类之中复用同样的一个方法, 但是他们的参数会有所不同(有时候返回值也会不同)。重载会让客户端的调用更加方便,因为客户单可以使用不同的参数列表来调用同样的函数
功能性重载(Function Overloading):同一个函数名有着多种不同的实现方式。
静态多态(static polymorphism):重载是一种静态多态。
· 调用函数会根据参数列表进行最佳匹配
· 会执行静态类型检查
· 在编译阶段时就会决定要执行哪个方法
注意:重写(Override)是在运行时才进行动态检查(Dynamic checking)。
重载的规则:
· 不同的参数列表
· 相同/不同的返回值
· 相同/不同的访问修饰符(private, protected, public)
· 相同/不同的异常
· 可以在同一个类之内进行重载,也可以在子类之中进行重载。
e.g.
重写 vs 重载
· 泛型类:其定义中包含了类型变量(泛型接口、泛型方法同理)
泛型接口
– 非泛型的实现类
e.g.
Java泛型的一些细节:
· 可以有多个类型参数 e.g.
· 泛型类型的信息会被擦去(仅限在编译时),因此不能使用instanceOf()来检查泛型
· 不能创建泛型数组
e.g.
子类型的好处:代码的复用,模块化的灵活性
子类型的另一种解释:
“B是A的子类型”意味着“每一个B都是A”;如果用规约来描述,就是“每一个B都满足A的规约”
· 如果B的规约至少是和A的规约一样强的话,那么B就是A的子类型
· 当我们用一个类去实现一个接口时,Java的编译器会自动强调一个要求:每个接口中的方法都在B中出现了,并且具有兼容性的函数头。
· 子类型的规约不能弱化父类型的规约
子类型多态:不同类型的对象可以统一处理而无需区分,从而隔离了“变化”。
LSP(里氏替换原则)
类型转换:有时候我们会使用强制类型转换,但是对于子类型之间的转换要注意避免抛出异常。
在父类型与子类型之间的向下转型(downcast)是不被允许的
七. 动态分配(Dynamic dispatch)
动态分配是一个对于一个多态在运行时来选择何种操作去执行的一个过程;静态分配是在编译阶段就确定要具体执行哪个操作。
方法的动态分派过程:
1.(编译阶段)决定将要进入哪个类
2.(编译阶段)决定将要执行哪个函数名
– 找到所有匹配、可调用的方法
3.(运行阶段)确定接收器的动态类别
4.(运行阶段)从动态类之中定位到要调用的方法
– 在第2步所找到的函数之中寻找对应方法
– 没找到就去父类之中寻找
动态分配不等同于推迟绑定(late binding/ Dynamic binding)
– 绑定:将调用的名字与实际的方法的名字联系起来(可能有很多个);分派:具体执行哪个方法(early binding -> static dispatch)
– 动态分派:编译阶段可能绑定到多态操作,运行阶段决定执行哪个(重写和重载也是如此)
– 推迟绑定:编译阶段不知道类型,一定是动态分派(重写是推迟绑定,重载是提早绑定)
八. Java的Object类之中的一些重要方法
equals() – 如果两个对象“相同”就为真
hashCode() – 哈希表所用的哈希码
toString() – 输出表示
toString()——一般要重写,因为原本的输出太垃圾了,除非不需要输出
e.g.

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!