【面向对象】Part 1 基本概念

来源:互联网 发布:web excel源码 编辑:程序博客网 时间:2024/06/06 05:03
  • OOP定义
    1 一切都是对象
    2 计算通过对象间相互通信,请求其他对象执行动作来实现。对象间通过发送和接收消息来通信。
    3 每个对象都有自己的内存,其中可能包括了其他的对象。
    4 每一个对象都是某个某个类的实例。类就是一组相似的对象。
    5 是对象相关行为的存储库。也就是说,同一个类的所有对象都能执行同样的动作。
    6 类被组织成有单个根节点的树状结构,被称为继承层次结构。与类实例相关的内存和行为都会被树结构中的后代自动继承

  • 面向对象基本概念
    1 任何事物都是对象,对象有属性方法复杂对象可以由相对简单的对象以某种方式构成。
    2 通过类比发现对象间的相似性,即对象间的共同属性,是构成对象的依据。
    3 对象见的相互联系是通过传递“消息”来完成的。通过对象之间的消息通信驱动对象执行一系列的操作从而完成某一任务。

  • 面向对象主要特点:类、对象、继承、封装、聚合、关联、消息、多态


对象

  • 对象是独立存在的客观事物,它由一组属性和一组操作构成。
    属性操作是对象的两大要素。属性是对象静态特征的描述,操作是对象动态特征的描述。
    属性一般只能通过执行对象的操作来改变。
    操作又称为方法或服务,它描述了对象执行的功能。通过消息传递,还可以为其他对象使用。
  • 对象是指一个实体:能够保存一个状态(又称信息数据);能提供一系列操作(或称行为),这些操作或能检查或影响对象的状态
    能够表示现实或抽象的事物:具有良好定义的责任和良好定义的行为;具有良好定义的接口
  • 对象的性质:封装性、自治性、通信性、暂存性、持久性
  • 对象标识(OID)
    是将一个对象和其他对象加以区别的标识符
    一个对象标识和对象永久结合在一起,不管这个对象状态如何变化,一直到该对象消亡为止
    用变量名充当标识
    可寻址性和标识这两个概念做了混合
    强类型变量,像C++,Java Employ emp = new Employ();
    非强类型的变量 var emp =new Employ()
    直接标识就是变量的值即为要标识的对象
    间接标识指变量的值不是要标识的对象而是该对象的指针
  • 复合对象
    指一个对象的一个属性或多个属性引用了其他对象
    委托:是复合对象的一个特例,在委托方式下可有两个对象参与处理的一个请求,接受请求的对象将责任委托给它的代理者
    组合:一个对象可以由其他对象构造
    聚合:描述对象间具有相互关系的另一种方式
    组合和聚合的区别:
    • 组合:
      语义规则: a-part-of
      整体负责部分
      每个部分对象也仅与一个整体对象联系
      是更强形式的聚合
    • 聚合:
      同质的
      语义规则:has-a
      部分可以脱离整体存在,例如摄影协会和会员的关系
  • 对象持久化
    要长久保存的对象,也就是持久对象。持久对象不随着创建它的进程结束而消亡,在外村中存贮
    不需要长期保存的,被称为暂存对象

  • 就是这些具有相同或相似行为或数据结构的对象的共同描述
    类是若干对象的模板,并且能够描述这些对象内部的构造
    属于同一个类的对象具有相同数据结构行为

  • 类与对象的关系:
    对象按照不同的性质划分为不同的类
    同类对象在数据和操作性质方面具有共性
    把一组对象的共同特性加以抽象并存贮在一个类中
    类是对象之上的抽象,有了类之后,对象则是类的具体化,是类的实例(类是对象的抽象,对象是类的实例)
    类是静态概念,对象是动态概念

  • 类的性质:
    类的名标识一个类
    在同一个系统环境中,类的名能够唯一标识一个类
    类必须有一个成员集合:属性、方法、方法的操作接口
    类的属性的域:基本类、用户定义的类
    支持信息隐藏

  • 类的实例
    一个实例是从一个类创建而来的对象
    属于某个类的对象称为该类的一个实例,每个实例具有一个对象标识
    类和对象间有instance-of关系
    类描述了这个实例的行为(方法)结构(属性)
    每个实例可由该类上定义的操作(方法)来操纵

  • 类及实例的特征
    同一个类的不同实例:

    • 具有相同的值
    • 承受的是同一方法集合所定义的操作,因而具有相同的行为
      同一个类的不同实例可以持有不同的值,因而可以有不同的状态
      实例的初始状态(初值)可以在实例化中确定
  • 类的数据字段/类属性:被一个类的所有实例共享的公共数据字段
    静态数据字段的初始化实在加载类时,执行静态块完成
    静态成员函数

    • 不能访问非静态成员
    • 无this/不能使用this引用
      构造和析构函数不能为静态成员
      Java中的类方法和类变量
    • Java同时也包含无对象(objectless)变量及无对象方法,称为类变量和类方法
    • 类变量的典型使用方式是定义常量
    • 类方法可以被视为可独立于某类的所有对象而进行调用的方法,而非传递给该类的对象的消息
  • 类的使用
    类的使用有两种形式:允引、继承

  • 对象的创建
    对象数组的创建:数组的分配和创建、数组所包含对象的分配和创建
    Java:new仅创建数组,数组包含的对象必须独立创建

  • 构造函数【问老师】
    初始化新创建对象
    优点:确保初始化之前不会被使用,防多次调用
    Java/C++:名称,返回值
    构造函数重载

  • const和final区别
    const 常量,不允许改变
    final 仅断言相关变量不会赋予新值、并不能阻止在对象内部对变量值进行改变(如set方法)

  • 析构函数
    C++,在内存中开始释放对象时自动调用

  • 内存回收
    使用new创建–堆内存

    • 堆内存没有绑定在过程的入口和出口处
    • 内存有限
      内存回收的方式:
      1 在程序中显式指定不再使用的对象,将其使用内存回收
      C++:delete
      Object Pascal:free
      2 垃圾回收机制(Java\C#\Smaltalk)
      时刻监控对象的操作,对象不再使用时,自动回收其所占内存
      通常在内存将要耗尽时工作:方法:1确保动态分配的内存对象都有一个指定的属主; 2 引用计数:引用共享对象的指针的计数值
  • 对象结构
    简单类型的域与引用类型的域(????)
    对象同一:具有相同的标识
    对象相等:两个对象的标识不同,但具有相同的值

  • 克隆(感觉应该是很重要的)
    内存布局(三种)
    1.最小静态空间分配:只分配基类所需的存储空间
    2.最大静态空间分配:无论是基类还是子类,都分配可以用于所有合法地数值的最大的存储空间
    3.动态内存分配: 只分配用于保存一个指针所需的存储空间(),在运行时通过来分配数值所需的存储空间同时将指针设为相应的合适值(存储空间的地址)
    赋值(两种语义):复制、指针
    复制和克隆
    当指向其他对象的变量值进行复制时,有两种可能的方案:
    1.浅复制: 与原来变量共享实例变量,即原有变量和复制产生的变量引用相同的变量值
    2.深复制:建立实例变量的新的副本
    Java中的克隆clone():一是拷贝对象返回的是一个新对象,而不是一个引用;二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些对象的原始信息而不是对象的初始信息(对于非基本类型变量,保存的仅仅是对象的引用,clone后的和原始对象的相应变量指向的是同一个对象,需要注意)
    Java默认的clone方法是浅克隆,只克隆对象的非引用类型成员。
    如果要改成深克隆需要:①让非基本类型变量的类实现Cloneable接口,重载clone()方法②外层为该非基本类型变量执行 clone()方法

  • 类对象
    类本身也是一个对象,这个特殊的对象也有其属性和方法,称之为类属性和类方法,普通对象的属性和方法称作实力属性和实例方法。

  • 元类(metaclass)
    描述类的类,如果将类看作一个对象,该类必定是另一个特殊类的实例,将这个特殊类称之为元类
    每个类一定是某个元类的实例,例如对于类A,它是元类A class 的实例
    引入元类的优点: 只用对象这一概念便可描述系统中的所有成分;使类成为运行时刻一部分,有助于改善程序设计环境;继承的规范化,类与元类的继承采用双轨制
    Class是特殊的类(类的类),具有以下行为:创建实例,返回类名称,返回类实例大小,返回类实例可识别消息列表
    类对象:反射工具都开始于一个对象,该对象是关于一个类的动态(运行时)体现;类对象是更一般的类(Class类)的实例;类对象通常都包括类名称、类实例所占用内存的大小以及创建新实例的能力
    getClass()——获取类对象;getSuperclass()——获取父类;getName()、toString()——字符串类名称;instanceof——检测对象类
    ClassLoader 类加载器,主要用于加载类文件,利用反射(newInstance())生成类实例,如:
    ClassLoader cl = this.getClass.getClassLoader();//获得ClassLoader
    Class cls = cl.loadClass(“com.rain.B);//使用第一步得到的ClassLoader来载入B
    B b = (B)cls.newInstance();//有B的类得到一个B的实例
    反射:反射支持一个组建动态地加载和查询自身信息,因此反射为许多基于组建的编程工具建立了基础。如得到某个对象的属性、静态属性、执行某对象的方法、静态方法、新建实例、判断是否为某个类的实例、得到数组中的某个元素等等

  • 消息传递
    在两个实体间通信,其必要条件是在它们之间至少存在一条通道,并且遵守同一种通信协议
    发送一条消息时,应指明信道或给出信道的决定方法,最常用的是用接收方的标识(如名字)来命名信道:Send To
    发送消息的流程:对象A向对象B发送消息,也可以看作对象A向对象B请求服务。对象A要明确知道对象B提供什么样的服务;根据请求服务的不同,对象A可能需要给对象B一些额外的信息,以使对象B明确知道如何处理该服务;对象B也应该知道对象A是否希望它将最终的执行结果以报告形式反馈回去
    消息传递语法:消息->接收器;响应行为随接收器不同而不同
    aGame.displayCard(aCard,42,27)
    receiver selector arguments
    伪变量this
    this使用时好像在使用同类的实例,this隐含指向调用成员函数的对象,也可作为参数传递


类和继承

  • 继承
    继承是一种使用户得以在一个类的基础上建立新的类的技术
    新的类自动继承旧类的属性和行为特征,并可具备某些附加的特征或某些限制
    新类称作旧类的子类,旧类称作新类的超类
    继承机制的强有力之处还在于它允许程序设计人员可重用一个未必完全符合要求的类,允许对该类进行修改而不至于在该类的其他部分引起有害的副作用
    是其他语言所没有的

  • 继承:在已有的类的基础上建立新的类的方法。重复使用和扩展已有的、经过测试的类,实现重用;可以增强处理的一致性,是一种规范和约束

  • 作用:代码复用;概念复用,共享方法的定义
    可以用“Is-A”检验两个概念是否为继承关系
    超类(superclass)和子类(subclass):已在的类通常称作超类,新的类通常称作子类,子类不仅可以继承超类的方法,也可以继承超类的属性,如果超类中的某些方法不适合于子类,则可以重置这些方法
    继承有传递性
    单继承:如果一个类只有一个直接超类则被称为单继承。单继承构成类之间的关系是一棵树
    多继承:如果一个类有多余一个的直接超类则被称为多继承,多继承构成的类之间的关系是一个网格(需要防止产生二义性如用es.Employee::print()和es.Studnet::Print()来区分两个方法)
    C++规定多继承时直接超类的构造函数的调用次序是:①抽象超类。若有多个抽象超类,按继承说明次序从左到右。②非抽象超类:若有多个非抽象超类,按继承顺序从左到右

  • 泛化和特化:
    泛化:通过抽取及共享共同特征,将这些共性抽取出作为超类放在继承层次的上端,抽取出的超类叫做抽象类(抽象类没有实例,不能创建实例,Bind)。
    特化:新类作为旧类的子类
    接口和抽象类:与类一样,接口可以继承于其他接口,甚至可以继承于多个父接口。

  • 抽象方法:介于类和接口之间的概念,定义方法但不实现。在创建实例前,子类必须实现父类的抽象方法

  • 继承的形式

    • 特化子类化(子类型化) 新类是父类的一种特殊形式,但符合父类的各种规定,显然支持替换原则
    • 规范子类化 父类(抽象规范类)仅仅对行为进行定义,却没有对其实现,而由子类来实现这些行为
    • 构造子类化 需要对一些对应于接口的方法名称进行改变,或者以一种特定的方式对方法参数进行修改,经常违反替换原则。
    • 泛化子类化 子类将父类的行为进行扩展,建立了一种更泛化的对象,通常用于打算基于已存在的类来建立一种不想修改或不能修改的类
    • 扩展子类化 对对象增加新功能。和泛化子类化的区别在于,后者必须至少改写来自父类的一个方法,且子类的功能需要与父类紧密联系,而扩展子类化只是对父类增加新的方法,且子类的功能与父类的联系不那么紧密
    • 限制子类化 用于子类的行为少于或限制于弗雷。如将双向队列改为栈。限制子类化违反替换原则,通过它创建的子类不是子类型,应当尽量避免使用
    • 变体子类化 在两个或多个类需要实现类似的功能,但它们的抽象概念之间似乎不存在层次关系的情况下,可以使用变体子类化。
    • 结合子类化 需要一个子类,可以表示两个或更多的父类的特征的结合。(多重继承)
  • 替换原则:在静态类型语言中父类和子类数据类型的关系
    1.子类实例必须拥有父类的所有数据成员
    2.子类的实例必须至少通过继承实现父类所定义的所有功能。
    3.这样,在某种条件下,如果用子类实例来替换父类实例,那么将会发现子类实例可以完全模拟父类的行为,二者毫无差异
    在替换原则下,对于类A和类B,若B是A的子类,那么在任何情况下都可以用类B来替换类A
    可替换性:变量声明时指定的类型不必与它所容纳的值类型相一致(传统的编程语言不允许,OO的却经常出现)

  • 重置/改写(见后面)
    子类通过改写,来避免继承父类的行为
    语法上,改写需要子类定义一个与父类有相同名称且类型签名相同的方法
    运行时,变量声明为一个类,它所包含的值来自于子类,与给定消息相对应的方法同时出现于父类和子类。
    改写与替换结合时,想要执行的一般都是子类的方法
    (重定义:操作的表示和操作的实现都将改变)
    重置(改写)时子类不改变超类中的已有接口定义,重置机制是基于动态绑定(动态联编)的

  • Java中的实现继承
    实现继承是面向对象编程的最重要特性之一(也被称为子类化)。这个特性显著地增强了类的可重用性并且减少了代码的冗余。
    缺点:读者要追溯执行流程变得非常困难,尤其是在一个具有很多世代的深度继承树之中,其树底层的方法的代码分散在高层的祖先当中;另一个问题就是所有子类都是和父类紧密联系的
    如果某类的子类要正确地实现,就需要将该类的实现公开给子类的设计人员,然而这种公开并不总能得到满足
    指导原则:如果某个类具有一些只对类的部分对象适用的行为,那么不妨考虑将该类分裂为两个互相关联的类,两个类之间要么直接通过继承关联,要么通过一个公共的抽象类或接口关联。
    可以用继承、多态和动态方法调用来避免不优雅的条件判断

  • 软件复用机制:继承、组合
    组合:提供了一种利用已存在的软件组件来创建新的应用程序的方法。委托、非替换、语义A has-a B
    继承:通过继承,新的类可以声明为已存在的类的子类。通过这种方式,与初始类相关的所有数据字段和函数都自动地与新的数据抽象建立联系。语义:A is-a B
    组合和继承的比较:
    继承:is-a关系

    • 优点:子类可以重写父类的方法来实现对父类的扩展;代码简洁性
    • 缺点:①父类的内部细节对子类是可见的;②子类从父类继承的方法在编译时便已确定,所以无法再运行期间改变从父类继承的方法的行为;③子类与父类是一种高耦合,如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。
      组合:has-a 关系
      设计类的时候要把组合的类的对象加入到该类中作为自己的成员变量
    • 优点:当前对象只能通过所包含的那个对象去调用其方法,所包含的对象的内部细节对当前对象不可见;当前对象与包含的对象是低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码;当前对象可以在运行时动态绑定所包含的对象
    • 缺点:①容易产生过多的对象;②为了能够组合多个对象,必须仔细对接口进行定义
      由此可见,组合比继承更具灵活性和稳定性,所以在设计的时候优先使用组合
  • 接口和抽象类:

    • 抽象类:在定义方法时可以只给出方法头,而不必给出方法体(即方法实现的细节),这样的方法被称为抽象方法。
      抽象方法必须使用关键字abstract修饰,包含抽象方法的类必须声明为抽象类。
      抽象类不能被实例化
      JAVA语言规定,子类必须实现其父类中的所有抽象方法,否则该子类也只能声明为抽象类
      抽象类主要是通过继承、再由其子类发挥作用的,其中作用包括两方面:代码重用;规划
      其他特性:
      抽象类中可以不包含抽象方法;
      子类中可以不全部实现抽象父类中的抽象方法,但此时子类也只能声明为抽象类
      父类不是抽象类,但在子类可以添加抽象方法,但子类需声明为抽象类
      抽象类中可以声明static属性和方法
    • 接口:
      接口是特殊的抽象类,一个类只包含静态最终变量和抽象方法,或者是只有抽象方法的时候,抽象类可以等同于一个接口。
      用接口代替抽象类,是因为接口有比抽象类更好的特性:①可以被多继承;②设计和实现完全分离;③更自然的使用多态;④更容易搭建程序框架;⑤更容易更换实现
      特性:
      接口不可以被实例化,常作为类型使用
      实现类必须实现接口的所有方法(抽象类除外)
      实现类可以实现多个接口(Java中的多继承)
      接口中的变量都是静态常量(Java语法规定,接口中的变量默认自动隐含是public static final)
  • 静态类与动态类
    变量的静态类:指用于声明变量的类。静态类在编译时就确定下来,且再也不会改变
    变量的动态类:指与变量所表示的当前数值相关的类。动态类在程序执行时,对变量赋新值时可以改变
    对于静态类型面向对象编程语言,在编译时消息传递表达式的合法性不是基于接收器的当前动态数值,而是基于接收器的静态类来决定的
    Animal pet;
    pet = new Dog();
    pet.bark();//这里会报错,因为Animal类没有bark()这个方法

  • 方法绑定:分为静态方法绑定(在程序执行前方法已经被绑定)和动态方法(在运行时根据具体对象的类型进行绑定)绑定
    响应消息时对哪个方法进行绑定是由接收器当前所包含的动态数值来决定的。


多态和重载

多态是指能够在不同上下文中对某一事物(变量、函数或对象)赋予不同含义或用法
引入多态的概念,是为了得到更为灵活的方式使表示的形式尽可能与所表示的内容无关

  • 多态的三种类型:
    多态一般分为继承多态、重载多态、模板多态
    重载多态和模板多态是静态多态,即多态行为是在编译期决定的
    而继承多态是一种动态多态的形式,即多态行为可以在运行时动态改变

  • 多态变量:
    指可以引用多种对象类型的变量
    这种变量在程序执行过程中可以包含不同类型的数值
    对于动态类型语言,所有的变量都可能是多态的
    对于静态类型语言,则是替换原则的具体表现

  • 四种形式的多态:
    1.重载(overloading):两种,基于类型签名的重载和基于范畴的重载

    • 函数类型签名:是关于函数参数类型、参数顺序和返回值类型的描述,类型签名通常不包括接收器类型
      基于类型签名的重载就是,多个方法允许共享同一名称,且通过该过程所需的参数数目、顺序和类型(对于静态类型语言)来对他们进行区分,即使函数处于同一上下文也是合法的。唯一的要求就是每种实现方式都必须具有不同的类型签名。
      关于重载的解析,是在编译时给予参数值的静态类型完成的。和改写不同,不涉及运行时机制。
    • 范畴:定义了能够使名称有效使用的一段程序,或者能够使名称有效使用的方式
      基于范畴的重载就是,两个不同的函数可以使用相同名称的本地变量,由于它们的范畴之间并不重叠,因此不会产生任何混乱。
      通过继承创建的新类将同时创建新的名称范畴,该范畴是对父类的名称范畴的扩展
      强制、转换和造型
    • 强制是一种隐式的类型转换,它发生在无需显式引用的程序中。
      double x = 2.8;
      int i = 3;
      x =i+x;//这里i会转换成double
    • 转换:程序员进行的显式类型转换,也被称为“造型”
      x = ((double)i)+x;
    • 重载方法匹配算法
      ①找精确匹配(形参实参精确匹配的同一类型),找到则执行,若未找到,进入第二步
      ②找可行匹配(符合替换原则的匹配,即实参所属类是形参所属类的子类),若没找到,报错;只找到一个可行匹配,执行;如果多于1个,则转第三步
      ③多个可行匹配两两比较,逐一作出判断:如果一个方法的类型签名都可以赋值给另一个方法,则后者(类型大者)被排除;重复此操作,直到无法排除为止。
      ④如果只剩一个幸存者,执行。否则报错
      2.重置/改写(overriding)
      发生在有父类和子类关系的上下文中,与替换原则相结合
      3.多态变量(赋值多态):声明和包含不同
      Parent p = new Child();
      多态变量是指可以引用多种对象类型的变量,这种变量在程序执行过程中可以包含不同类型的数值,对动态类型语言,所有的变量都可能是多态的,而对于静态类型语言,多态变量是替换原则的具体表现。
      多态方法(纯多态),实例:StringBuffer中的append方法,一个定义,多种结果。
      4.泛型(模板)
      Template T max(T left,T right)
      {//详细的代码
      }
      名称T是一个参数,但是它是不同于函数的两个参数,在函数的代码体中,T可以用于任何合适的类型
      为什么需要泛型?泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性,与其使用Object,不如暂时不指定类型,而是稍后再决定具体使用什么类型。
  • 重载、重定义、改写的区别:
    重载:有两种,见上文
    重定义:当子类定义了一个与父类具有相同的名称但签名类型不同的方法时,发生重定义。类型签名的变化是重定义区别于改写的主要依据。
    改写:如果子类的方法具有与父类的方法相同的名称和类型签名,我们就说子类的方法改写了父类的方法
    重载与改写:改写可以看成重载的一种特殊情况,但还存在着以下差异:
    1.对于改写。方法所在的类必须符合继承关系,而重载无此要求
    2.如果发生改写,两个方法的类型签名必须匹配
    3.重载方法总是独立的,而对于改写的两个方法,有时会结合起来一起实现某行为
    4.重载通常实在编译时解析的,而改写则是一种运行时机制

  • 动态绑定
    绑定是把一个过程调用和相应这个调用而需要执行的代码加以结合的过程
    在编译时进行的叫静态绑定
    在运行时进行的叫动态绑定。因此,一个给定的过程调用和代码的结合直到调用发生时才得以进行,因而也叫作延迟绑定


方法

定义于某一特定类上的操作与规则
具有同类的对象才可为该类的方法所操作
这组方法表达了该类对象的动态性质,而对于其他类的对象可能无意义,乃至非法
规则,说明了对象的其他特征之间是怎样联系的,或者对象在什么条件下是可行的
方法也称作行为


消息

对另一个对象的操作在于选择一个对象并通知它要做什么
该对象“决定”如何完成这一任务,在其所属类的方法集合中选择合适的方法作用于其身
“操作一个对象“并不意味着直接将某个程序作用于该对象,而是利用传递消息,通知对象自己去执行这一操作,接收到消息的对象经过解释,然后予以相应
发送消息的对象不需要知道接收消息的对象如何对请求予以相应


封装

所有信息(数据及行为)都封装在对象中
影响对象的唯一方式是执行它所属的类的方法即执行作用于其上的操作
信息隐藏
当使用对象时,不必知道对象的属性及行为在内部是如何表示和实现的,只需知道它提供了哪些方法即可。
目的:1.避免重复的代码;2.保护类受到不必要的修改

0 0
原创粉丝点击