精华内容
下载资源
问答
  • 面向对象(OOP)的三个特性: 封装性(Encapsulation),继承性和多态性。 面向对象(OOP)的五个原则:单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP...

    面向对象(OOP)的三个特性: 封装性(Encapsulation),继承性和多态性。

    面向对象(OOP)的五个原则: 单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)。

    1.  面向对象的三个特性

    面向对象的三个特性: 封装性(Encapsulation),继承性和多态性。

    1.1 封装性

    封装性:将数据和对数据的操作封装起来,对象状态(属性)由这个对象自己的行为(方法)来读取和改变,隐藏细节部分。

    Java 中 属性的封装,无特殊情况都是用的 private 修饰符, 用 private 修饰符声明该属性为私有属性,只允许类的内部直接访问该属性。对于私有属性,要给一对 setter() 和 getter() 方法操作,外部访问私有属性需要通过setter() 和 getter() 方法。 setter() 和 getter() 方法 IDE(idea、eclipse) 可快速生成 。

    在类中可以使用 this 关键字访问本类的私有属性,this 关键字只能用于类的方法体中。如this.id,去访问本类的私有属性 id。

    良好的封装能够减少耦合;保证属性的操作安全性。 

    访问权限修饰符还有如下几种:

    访问修饰符同一个类同一个包不同包中的子类工程的任意位置
    private可访问   
    缺省(default)可访问可访问  
    protected可访问可访问可访问 
    public可访问可访问可访问可访问

    1.2 继承性

    继承性: 把多种有着共同特性的多的类事物抽象成一个类,这个类就是多个类的父类。父类的意义在于可以抽取多个类的共性,代码复用,减少代码量。

    例:三个类,Pupil类(小学生),MiddleSchoolStudent类(中学生类),CollegeStudent类(大学生类),他们有一个特性,都要有名字,学号,班级等等属性,都要去上课,考试等行为。

    我们可以写一个父类 为 student类(学生),在Student类中实现名字,学号,班级等属性,上课,考试等行为,Pupil类、 MiddleSchoolStudent类、CollegeStudent类 继承 Student 类之后,就不需要自己去实现这些方法,可以直接去访问父类的。上课这个方法你本来要写三遍,继承之后你只用实现一次, 就实现了代码复用,减少代码量。 

    Java 只能单继承 ,即一个类只能有一个父类(A继承B之后,不能在继承其他的类);可以多层继承(A继承B,B继承C);多个类继承一个类(B和C都继承A)。

    子类继承的关键字:extends。 A 类  extends B 类,A是B的子类,B是A的父类。

    子类可以访问父类的 public 和 protected成员,四种访问属性在和子类中的含义如下。

    父类成员访问属性在父类中的含义在子类中的含义
    public对所有人开放对所有人开放
    protected只有包内其它类、自己和子类可以访问只有包内其它类、自己和子类可以访问
    缺省(default)只有包内其它类可以访问

    如果子类与父类在同一个包内:只有包内其它类可以访问

    否则:相当于private,不能访问

    private只有自己可以访问不能访问

    继承的缺点:提高了类之间的耦合性。

    1.3 多态性

    多态性: 程序中定义的引用变量 “所指向的具体类型” 和 “通过该引用变量发出的方法调用” 在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

    静态多态(编译时多态):方法的重载实现 。重载:方法名相同,方法参数个数和类型不同

    动态多态(运行时多态): 子类中对父类方法的重写、接口、抽象类和抽象方法。

    接下来举几个动态多态的例子。

    1.3.1  子类中对父类方法的重写

    父类为 Student , 子类为 pupil 。

     Student.java :

    package duotai;
    
    public class Student {
        public void play() {
            System.out.println("play");
        }
    }
    

     pupil.java :

    package duotai;
    
    public class Pupil extends Student{
        public void play() {
            System.out.println("唱儿歌");
        }
        public static void main(String args[]) {
            Student student = new Student();
            Pupil pupil = new Pupil();
            Student student1 = new Pupil();
            student.play();
            pupil.play();
            student1.play();
        }
    }
    

    执行   pupil.java 中的 main 方法,运行截图如下:

    1.3.2  接口实现多态

    List 接口的两个实现类,ArrayList,LinkedList。

    public class ListStudy {
        public static void main(String args[]) {
            List<String> list = new ArrayList<>();
            List<String> list1 = new LinkedList<>();
        }
    }

    2. 面向对象的五个基本原则

    2.1 单一职责原则(SRP)

    一个类应该只有一项工作。

    比如在职员类里,将工程师、销售人员、销售经理这些情况都放在职员类里考虑,其结果将会非常混乱,在这个假设下,职员类里的每个方法都要if else判断是哪种情况,从类结构上来说将会十分臃肿,并且上述三种的职员类型,不论哪一种发生需求变化,都会改变职员类!这个是大家所不愿意看到的!

    2.2 开放封闭原则(OCP)

    对象或实体应该对扩展开放,对修改封闭。

    更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!

    2.3 里氏替换原则(LSP)

    子类可以替换父类并且出现在父类能够出现的任何地方,这个原则也是在贯彻GOF倡导的面向接口编程!

    在这个原则中父类应该尽量使用抽象类和接口。

    2.4 依赖倒置原则(DIP)

    高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是抽象依赖于实现。

    可以这样理解,上面我举例子的时候先说了兔子和绵羊,然后才推出食草动物。但如果我们继续认识了牛、马等食草动物,我们会发现我们需要不断调整食草动物的描述,这样程序会变得僵化,所以我们不应该让子类依赖于实体,不应该让父类模块依赖于子类模块。所以我们需要将食草动物设计为抽象类,即抽象类或接口。这样下层只需要实现相应的细节而不会影响父类。

    2.5 接口隔离原则(ISP)

    不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!

    比如,为了减少接口的定义,将许多类似的方法都放在一个接口中,最后会发现,维护和实现接口的时候花了太多精力,而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护!

    参考文献:

     

    展开全文
  • 学java的都知道,java语言有三个特性,也可以说是三个优点,就是 1.简单性: java语言相对于C语言,C++来说比较简单。 2.跨平台:java语言编写的代码可以在不同的操作系统运行,即windows、android 、ios、linux等等...

    什么是面向对象(面向对象的三大特性)

    简介

    学java的都知道,java语言有三个特性,也可以说是三个优点,就是
    1.简单性: java语言相对于C语言,C++来说比较简单。
    2.跨平台:java语言编写的代码可以在不同的操作系统运行,即windows、android 、ios、linux等等,也是软件、计算机硬件和软件的桥梁、可以调用硬件资源。
    3.纯面向对象:这个就是今天要记录的重点,编程语言发展到C++的时候已经具备了面向对象的特性,而java相对于C++面向对象更加的纯粹。

    什么是面向对象

    有关于面向对象这个特性,网上的说法众说纷纭,百度一查,各路神仙各抒己见,说法不一,在这里我仅发表一下个人看法。

    首先,面向对象简称OOP(Object Oriented Programming),翻译过来是面向对象程序设计,顾名思义,所谓"对象"在显式支持面向对象的语言中,一般是指类在内存中装载的实例,具有相关的成员变量和成员函数(也称为:方法)。面向对象的程序设计完全不同于传统的面向过程程序设计,它大大地降低了软件开发的难度,使编程就像搭积木一样简单,是当今电脑编程的一股势不可挡的潮流。

    面向对象是一种编程思想,java程序员在编写代码的时候,将功能和业务逻辑封装在方法中,以创建对象的方式,调用对象中的方法来完成某项功能的思想就是面向对象。OOP为软件分层提供了前提条件,控制层调用业务层方法,业务层调用持久层方法,使得项目结构更加清晰,类与类之间实现了高内聚,低耦合,程序员分工合作,各司其职,从这个角度来讲,OOP就是一种纵向的编程思想。

    面向对象的三大特性

    1.封装

    在说封装之前,我们先要看一下java的四大权限修饰符!!

    修饰符释义作用范围
    public公开的本类中,同一个包下,不同包需要导包
    protected受保护的用来保护子类,本类中,同一包下
    默认的本类中,同一包下
    private私有的本类中

    封装起到一个保护的作用,能提高程序的安全性,对类中的属性进行封装。
    具体实现方式:
    私有化属性
    private(私有的) 可以写在属性前面,用来修饰属性,被修饰的属性称之为私有属性,只能在本类中使用。
    提供get/set方法

    				set方法  ==》赋值
    				get方法  ==》取值
    				get方法  ==》 参数表为空
    				set方法  ==》 参数表不为空
    				get方法  ==》 返回值类型不能是void
    				set方法  ==》 返回值类型是void
    

    2.继承

    继承,顾名思义,意思就是一个类继另外一个类的功能,继承其他类的类称为子类,被继承的类称为父类(也叫超类或者基类)。
    继承的作用:

    ①减少冗余代码
    ②继承的时候,子类可以使用父类和子类中所有的方法,但是private修饰的方法除外
    ③单继承,一个子类只能有一个父类,但是一个父类可以有多个子类

    说道继承这儿,就涉及到了方法的重载(overload)和方法的重写(override)
    方法的重载(overload):在同一个类中,方法名一样,参数表不同的两个方法称之为重载。
    方法的重写(override):子类继承父类,子类相对于父类满足以下条件:
    方法名,参数表,返回值都相同,权限修饰符相同或者更广。
    调用方法的时候,如果子类和父类中都有这个方法,优先调用子类中的。

    3.多态

    说到多态,得先引入一张图
    在这里插入图片描述
    通过图中可以看出,多态的特性就是:
    父类的引用指向了子类对象,即子类对象的地址赋值给了父类的引用。大白话就是,创建了一个子类的对象,但是对象类型是父类类型的。
    多态有三个重要的性质
    ①对象类型不变
    ②对于引用,只能调用其引用类型中声明过的方法
    ③实际调用的时候,调用的是子类覆盖之后的方法

    4.创建对象的过程

    说到创建对象的过程,这个时候要区分情况对待,首先得看这个类有没有继承关系。
    没有有继承关系的时候
    ① 分配内存空间,给类中的属性赋默认值值;
    ②初始化属性,给类中的属性赋初始值;
    ③调用构造方法,这个时候往往会给属性赋第三次值。

    有继承关系的时候
    跟没有继承关系的时候步骤差不多
    ① 给子类父类所有的属性分配内存空间,赋默认值;
    ②初始化子类属性,调用子类构造方法;
    ③初始化父类属性,调用父类构造方法。

    ok,到这里面向对象就记录完了,其实面向对象就是一种编程思想,为了我们完成客户所需求的功能而产生的,剖析到最后,最初的想法还是为了满足不同的人的不同需求。最后说一下,以上仅代表个人观点,如有错误还请指出,有不同观点请在下方评论。看都看了,码字不易,点个赞再走呗!!!!!
    展开全文
  • C#面向对象三特性

    万次阅读 多人点赞 2016-11-23 21:02:09
    想要学好面向对象编程,除了掌握语法结构外最重要的就是熟悉面向对象三特性,这特性不是相互独立的而是相互关联、你中有我我中有你的关系,想要真正了解特性必须把这部分当做一整体来考虑。...

    想要学好面向对象编程,除了掌握语法结构外最重要的就是熟悉面向对象三大特性,这三大特性不是相互独立的而是相互关联、你中有我我中有你的关系,想要真正了解三大特性必须把这三部分当做一个整体来考虑。

    封装

    封装就是通过给类及类中的成员变量、属性和方法设置不同的访问修饰符(public、protected、internal、protected internal、private)来控制它们的作用范围,以达到封装的目的。
    各访问修饰符作用范围如下:

    访问修饰符说明
    public公有访问。不受任何限制。
    protected保护访问。只限于本类内部和子类内部可访问,实例不能访问。
    internal内部访问。只限于本项目内访问,其他不能访问。(相当于项目内的public)
    protected internal内部保护访问。只限于本项目和子类访问,其他不能访问。(protected和internal的权限之和)
    private私有访问。只限于本类成员内访问,子类、实例都不能访问。

    protected类型解释:
    若被引用的程序集中有子类A:B,那A中public类型的属性、方法和B中public类型的属性、方法都既可以通过A的实例或类名来调用,又可以在A内部直接使用,但A、B中protected类型的属性、方法只能在A内部直接使用,不能通过A的实例或类名来调用;
    若引用程序集的一方C继承了A,那A、B、C中public类型的属性、方法都既可以通过C的实例或类名来调用,又可以在C内部直接使用,但A、B、C中protected类型的属性和方法只能在C内部直接使用,不能通过的实例或类名来调用。

    默认访问修饰符

    在命名空间内部的所有类型(class、struct、abstract class、interface、delegate、enum)的访问修饰符默认是internal,可以人为改成public类型,不能改成protected、protected internal、private类型。
    1、命名空间内部各类型使用默认访问修饰符
    这里写图片描述
    ConsoleTest项目引用Encapsulation.dll后
    这里写图片描述

    2、命名空间内部各类型使用public访问修饰符
    这里写图片描述
    ConsoleTest项目引用Encapsulation.dll后
    这里写图片描述
    3、类内部的所有成员,默认均为private类型。
    4、抽象类的所有成员,默认均为private类型,但抽象方法不能用private修饰。
    5、接口的所有成员,默认均为public类型,而且不能手动添加访问修饰符。
    6、结构的所有成员,默认均为private类型,而且只能是public、internal、private这三种类型。

    继承

    1、被继承的类成为父类、基类、超类,而主动继承的类成为子类或派生类。子类继承父类的状态和行为,同时也有自己的特性。
    2、System.Object是所有类型的基类
    3、C#中继承的写法,class A:B{ }
    4、继承具有传递性,由A:B B:C=>A:C
    5、构造方法不能继承
    6、C#只允许单继承,一个类只能继承于一个父类
    7、密封类不能被继承,例如:sealed class Animal{ }
    8、子类不仅继承了父类的公有成员,同时继承了父类的私有成员,只是父类的私有成员在子类中不可被访问。
    9、class SubClass:MainClass{ }

    SubClass subClass = new SubClass();
    subClass.MethodTwo();
    subClass.MethodThree();

    代码解释:
    创建subClass对象时会先执行父类的构造方法,再执行子类的构造方法;
    创建subClass对象时默认调用父类的无参构造方法,我们可以显示调用父类的含参构造方法;
    subClass.MethodTwo();会直接执行MethodTwo()方法;
    subClass.MethodThree();会直接执行MethodThree()方法;
    如果MainClass还有父类C,那创建subClass对象时就要首先调用C的构造方法,其次调用MainClass的构造方法,最后调用SubClass的构造方法。
    10、当父类和子类中有名称相同的方法时,父类中的方法会被隐藏。
    假设父类和子类的重名方法为SayHi,发生隐藏时子类中的写法为public new void SayHi(){ },其实子类的方法不加new也可以,因为发生隐藏时系统默认会自动加上。
    11、A:B,A和B都有方法SayHi(),当B中的SayHi()是一个虚方法(virtual)时,才能在子类A中被重写/覆写,通过override关键字重写。
    重写是对继承自父类的SayHi()方法进行修改,隐藏与重写的本质是完全不同的。
    包含关键字virtual、abstract、override的方法才能被重写,密封方法(sealed)不能被重写。
    12、里氏替换原则:子类对象可以赋值给父类变量,反之不成立。
    A:B,A和B中都有方法SayHi(),当B b = new A();,b.SayHi();默认情况下(SayHi()没有在A中被重写)根据就近原则调用父类B中的SayHi()方法。如果方法SayHi()在子类中被重写(override),那b.SayHi()调用的就是子类A中的SayHi()方法。
    实例解释:
    Person p = new Chinese();
    p.SayHi();
    子类Chinese没有重写SayHi()时,p.SayHi()调用的是父类中的SayHi()方法;
    子类Chinese重写了SayHi()时,pSayHi()调用的是子类中的SayHi()方法。
    Person p = new Person();
    p.SayHi();
    无论子类Chinese是否重写了SayHi()方法,p.SayHi()调用的都是Person类中的SayHi()方法。

    多态

    多态有两种形式:重载、重写/覆写。
    1、重载
    同一个类中有多个方法的名称相同、参数个数不同或类型不同,则属于重载。
    同一个类中有多个方法名称相同、参数个数相同、参数类型相同、返回值类型不同,这样不属于重载,编译时会报错。
    这里写图片描述
    这里写图片描述
    2、重写/覆写
    实现重写有三种情况:
    普通类中的virtual方法可在子类中被重写;
    抽象类中的abstract方法和abstract属性必须直接或间接在子类中被重写;
    接口中的所有成员都必须被子类实现。

    第一种情况:
    (1)当类为普通类时,只有父类的虚方法才能被子类重写,子类实现重写要用关键字override,如果不加override就会隐藏父类的方法,隐藏需要加上关键字new,例如:public new static void a(){}或public static new void a(){}。
    (2)静态类中只能有静态成员,非静态类中可以有静态成员和非静态成员。
    (3)静态方法不能被重写,但可以被隐藏。
    (4)静态成员不能带override、abstract、virtual。

    第二种情况:
    (1)抽象类中可以有抽象属性、抽象方法和成员实例,继承抽象类的子类必须直接或间接重写所有的抽象方法,而且参数和返回值类型必须完全一致,成员实例可以不被重写。
    (2)非抽象类中只能有成员实例。
    (3)抽象类不能直接实例化,但是有构造方法,可以利用里氏替换原则把子类对象赋值给抽象的父类变量。
    这里写图片描述
    这里写图片描述
    AbsSubClass absSubClass = new AbsSubClass();
    AbsClass absClass = new AbsSubClass();
    创建absSubClass对象或absClass对象时,仍然是先执行父类的构造方法,再执行子类的构造方法。
    (4)抽象方法必须用abstract修饰,而且不能有方法体。
    (5)抽象方法不能用private修饰。

    第三种情况:
    (1)C#中类是单继承,接口是多继承,如果同时继承类和接口的话,接口必须在基类的后面,否则编译会出错。例如:class SubClass:MainClass,ISay,IEat{ }
    (2)接口不能直接实例化,没有构造方法,可以利用里氏替换原则把子类对象赋值给接口类型的父类变量。
    这里写图片描述

    这里写图片描述
    (3)接口中的成员没有访问修饰符,默认是public类型的,而且不能手动添加任何访问修饰符。
    (4)实现接口时不需要override,实现接口的类中的方法的参数和返回值类型必须与接口中定义的类型完全一致。
    (5)接口命名一般以I开头,表示某种功能,实现某一接口表示具备了某一功能。

    三大特性之间的关系

    1、继承时父类中的内容需要封装,例如:父类中的某些内容不希望被子类使用,那就需要使用private访问修饰符。
    2、重写形式的多态必须依靠继承,不继承是无法实现重写的,这无须多说。
    3、继承具有封装功能,继承分普通类、抽象类、接口三种情况,下面举例说明。

    普通类继承封装功能实例:
    (1)父类截图
    这里写图片描述
    (2)子类截图
    这里写图片描述
    (3)测试父类变量可调用的方法
    由于MainClass中public类型的方法只有A和B,因此通过MainClass的实例对象只能调用A和B。
    这里写图片描述
    (4)测试被子类对象赋值的父类变量可调用的方法
    虽然SubClass中public类型的方法有A、B、MethodTwo、MethodThree,但是被子类对象赋值的mainClass,只能调用A和B,相当于封装了子类SubClass。
    这里写图片描述
    (5)测试子类变量可调用的方法
    如你所料SubClass类及其父类MainClass中所有的public方法都可以通过SubClass对象调用。
    这里写图片描述

    抽象类继承封装功能实例:
    (1)父类截图
    这里写图片描述
    (2)子类截图
    这里写图片描述
    (3)测试被子类对象赋值的抽象的父类变量可调用的方法
    虽然AbsSubClass中public类型的方法有A、B、MethodTwo,但是被子类对象赋值的absSubClass,只能调用A和B,相当于封装了子类AbsSubClass。
    这里写图片描述
    (4)测试子类变量可调用的方法
    如你所料AbsSubClass类及其父类AbsClass中所有的public方法都可以通过AbsSubClass对象调用。
    这里写图片描述

    接口继承封装功能实例:
    (1)父类截图
    这里写图片描述
    (2)子类截图
    这里写图片描述
    (3)测试被子类对象赋值的接口类型的父类变量可调用的方法
    虽然Person中public类型的方法有SayChinese、SayChinese、MethodOne,但是被子类对象赋值的person,只能调用SayChinese和SayChinese,相当于封装了子类Person。
    这里写图片描述
    (4)测试子类变量可调用的方法
    如你所料Person类及其父类ISay中所有的public方法都可以通过Person对象调用。
    这里写图片描述

    展开全文
  • 再谈面向对象特性

    千次阅读 2016-12-12 17:01:57
    面向对象特性:封装、继承和多态。这是任何一本面向对象设计的书里都会介绍的,但鲜有讲清楚的,新手看了之后除了记住几概念外,并没真正了解他们的意义。前几天在youtune上看了Bob大叔讲解的SOLID原则,...

    面向对象的三大特性:封装、继承和多态。这是任何一本面向对象设计的书里都会介绍的,但鲜有讲清楚的,新手看了之后除了记住几个概念外,并没真正了解他们的意义。前几天在youtube上看了Bob大叔讲解的SOLID原则,其中有一段提到面向对象的三大特性,收获很多,但是我并不完全赞同他的观点,这里谈谈我的想法:

    封装

    『封装』第一层含义是信息隐藏。这是教科书里都会讲解的,把类或模块的实现细节隐藏起来,对外只提供最小的接口,也就是所谓的『最小知识原则』。有个共识,正常的程序员能理解的代码在一万行左右。这是指在理解代码的实现细节的情况下,正常的程序员能理解的代码的规模。比如一个文件系统,FAT、NTFS、EXT4和YAFFS2等,它们的实现是比较复杂的,少则几千行代码,多则几万行,要理解它们的内部实现是很困难的,但是如果屏蔽它们的内部实现细节,只是要了解它们对外的接口,那就非常容易了。

    关于『封装』的这一层含义,Bob大叔提出了惊人的见解:『封装』不是面向对象的特性,面向过程的C语言比面向对象的C++/Java在『封装』方面做得更好!证据也是很充分:C语言把函数的分为内部函数和外部函数两类。内部函数用static修饰,放在C文件中,外部函数放在头文件中。你完全不知道内部函数的存在,即使知道也没法调用。而像在C++/Java中,通过public/protected/private/friend等关键字,把函数或属性分成不同的等级,这把内部的细节暴露给使用者了,使用者甚至可以绕过编译器的限制去调用私有函数。所以在信息隐藏方面,『封装』不但不是面向对象的特性,而且面向对象减弱了『封装』。

    『封装』的第二层含义是把数据和行为封装在一起。我觉得这才是面向对象中的『封装』的意义所在,而一般的教科书里并没提及或强调。面向过程的编程中,数据和行为是分离的,面向对象的编程则是把它们看成一个有机的整体。所以,从这一层含义来看,『封装』确实是面向对象的『特性』。

    面向对象是一种思维方式,而不是表现形式。在C语言中,可以实现面向对象的编程,事实上,几乎所有C语言开发的大型项目,都是采用了面向对象的思想开发的。把C语言说成面向过程的语言是不公平的,是不是面向对象的编程主要是看指导思想,而不是编程语言。你用C++/Java可以写面向过程的代码,也可以用C语言写面向对象的代码。

    继承

    类就是分类的标准,也就是一类事物,一类具有相同属性和行为对象的抽象。比如动物就是一个类,它描述了所有具有动物这个属性的事物的集合。狗也是一个类,它具有动物所有的特性,我们说狗这个类继承了动物这个类,动物是狗的父类,狗是动物的子类。在C语言中也可以模拟继承的效果,比如:

    struct Animal {
    ...
    };
    struct Dog {
        struct Animal animal;
        ...
    }
    struct Cat {
        struct Animal animal;
        ...
    }

    因为C语言也可以实现『继承』,所以Bob大叔认为『继承』也不算不上是面向对象的『特性』。但是我觉得,C语言中实现『继承』的方式,需要用面向对象的思维来思考才能理解,否则纯粹从数据结构的方式来看上面的例子,理解起来就会大相径庭:animal是Dog的一个成员,所以Animal可以看成是Dog的一部分!Is a 变成了has a。只有在面向对象的思想中,说『继承』才有意义,所以说『继承』是面向对象的『特性』并不牵强。

    在C语言里实现多重继承更是非常麻烦了,记得glib里实现了接口的多重继承,但是用起来还是挺别扭的,对新手来说更是难以理解。多重继承在某些情况下,会对编译器造成歧义,比菱形继承结构:A是基类,B和C是它的两个子类,D从B和C中继承过来,如果B和C都重载了A的一个函数,编译器此时就没法区分用B的还是C的了(当然这是可以解决的)。

    像Bob大叔说的,Java没有实现多重继承,并不是多重继承没有用。而是为了简化编译器的实现,C#没有实现多重继承,则是因为Java没有实现多重继承:)

    除了接口多重继承是必不可少的,类的多重继承在现实中也是很常见的。比如:狼和狗都是狗科动物的子类,猫和老虎都是猫科动物的子类。狗科动物和猫科动物都是动物的子类。但是猫和狗都是家畜,老虎和狼都是野生动物。猫不但要继承猫科动物的特性,还继承家畜的特性。类就是分类的标准,而混用不同的分类标准是多重继承的主要来源。多重继承可以用其他方式实现,比如traits和mixin。

    不管是普通继承,接口继承,还是多重继承,在面向对象的编程语言中,实现起来要更加容易和直观,在面向过程的语言中,虽然可以实现,但是比较丑陋,而且本质是面向对象的思考方式。所以『继承』应该称得上是面向对象的『特性』了。介于继承带来的复杂性,现代面向对象的设计中,都推荐用组合来代替继承实现重用。

    多态

    『多态』本来是面向对象思想中最重要的性质(当然也算不上是特有的性质),但是教科书里都只是介绍了『多态』的表现形式,而没有介绍它用途和价值。『多态』一般表现为两种形式:

    • 允许不同输入参数的同名函数存在。这个性质会带来一定的便利,特别是对于构造函数和操作符的重载。但这种『多态』是在编译时就确定了的,所以只能算成一种语法糖,并没有什么特别的意义。

    • 子类可以重载父类中函数原型完全相同的同名函数。如果只看它的表现形式,在父类中存在的函数,在不同的子类中可以被重新实现,这看起来是吃饱了撑着。但是这种『多态』却是软件架构的基础,几乎所有的设计模式和方法都依赖这种特性。

    隔离变化是软件架构设计的基本目标之一,接口正是隔离变化最重要的手段。我们经常说分离接口与实现,针对接口编程,主要是因为接口可以隔离变化。如果没有第二种『多态』,就没有真正意义上的接口。面向对象中的接口,不仅是指模块对外提供的一组函数,而且特指在运行时才绑定具体实现的一组函数,在编译时根本不知道这组函数是谁提供的。我们先把接口简单的理解为,在基类中定义一组函数,但是基类并没有实现它们,而在具体的子类中去实现。这不就是『多态』的第二种表现形式么。

    接口怎么能够隔离变化呢?Bob大叔举了一个非常好的例子:

    #include <stdio.h>
    
    int main() {
        int c;
    
        while((c = getchar()) != EOF) {
            putchar(c);
        }
    
        return 0;
    }

    这个程序和Hello world是一个级别的,你从键盘输入一个字符,它就显示一个字符。但是它却蕴含了『多态』最精妙的招式。比如说输入吧,getchar是从标准输入(STDIN)读入一个字符,键盘输入是缺省的标准输入,但是键盘输入只是众多标准输入(STDIN)中的一种。你可以从任何一个IO设备读取数据:从网络、文件、内存和串口等等,换成任何一种输入,这个程序都不需要任何改变。

    具体实现变了,调用者不需要修改代码,而且它根本不用重新编译,甚至不用重启应用程序。这就是接口的威力,也是『多态』的功劳。

    上面的程序是如何做到的呢?IO设备的驱动是一套接口,它定义了打开、关闭、读和写等操作。对实现者来说,不管数据从哪里来,要到哪里去,只要实现接口中定义的函数即可。对使用者来说,完全不同关心它具体的实现方式。

    『多态』不但是隔离变化的基础,也是代码重用的基础。公共函数的重用是有价值的,在面向过程的开发中也很容易做到这种重用。但现实中的重用没那么简单,就连一些大师也感叹重用太难。比如,你可能需要A这个类,你把它拿过来时,发现它有依赖B这个类,B这个类有依赖C这个类,搞到最后发现,它还依赖一个看似完全不相关的类,重用的念头只好打住。如果你觉得夸张了,你可以尝试从一个数据库(如sqlite)中,把它的B+树代码拿出来用一下。

    在『多态』的帮助下,情况就会大不相同了。A这个类依赖于B这个类,我们可以把B定义成一个接口,让使用A这个类的使用者传入进来,也就是所谓的依赖注入。如果你想重用A这个类,你可以为它定制一个B接口的实现。比如,我最近在一个只有8K内存的硬件上,为一块norflash写了一个简单的文件系统(且看作是A类),如果我直接去调用norflash的API(且看作是B类),就会让文件系统(A类)与norflash的API(B类)紧密耦合到一起,这就会让文件系统的重用性大打折扣。

    我的做法是定义了一个块设备的接口(即B接口):

    typedef unsigned short block_num_t;
    
    struct _block_dev_t;
    typedef struct _block_dev_t block_dev_t;
    
    typedef block_num_t (*block_dev_get_block_nr_t)(block_dev_t* dev);
    typedef bool_t (*block_dev_read_block_t)(block_dev_t* dev, block_num_t block_num, void* buff);
    typedef bool_t (*block_dev_write_block_t)(block_dev_t* dev, block_num_t block_num, const void* buff);
    typedef void   (*block_dev_destroy_t)(block_dev_t* dev);
    
    struct _block_dev_t {
        block_dev_get_block_nr_t   get_block_nr;
        block_dev_write_block_t    write_block;
        block_dev_read_block_t     read_block;
        block_dev_destroy_t        destroy;
    };

    在初始化文件系统时,把块设备注入进来:

    bool_t sfs_init(sfs_t* fs, block_dev_t* dev);

    这样,文件系统只与块设备接口交互,不需要关心实现是norflash、nandflash、内存还是磁盘。而且带来几个附加好处:

    • 可以在PC上做文件系统的单元测试。在PC上,用内存模拟一个块设备,文件系统可以正常工作了。

    • 可以通过装饰模式为块设备添加磨损均衡算法和坏块管理算法。这些算法和文件系统都可以独立重用。

    『多态』让真正的重用成为可能,没有『多态』就没有各种框架。在C语言中,多态是通过函数指针实现的,而在C++中是通过虚函数,在Java中有专门的接口,在JS这种动态语言中,每个函数是多态的。『多态』虽然不是面向对象的『特有的』属性,但是面向对象的编程语言让『多态』更加简单和安全。

    展开全文
  • 面向对象特性

    万次阅读 2018-08-11 18:31:57
    封装是把客观事物抽象成类,并且把自己的属性和方法让可信的类或对象操作,对不可性的隐藏。 继承 继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 ...
  • Java-面向对象编程-特性之多态

    万次阅读 多人点赞 2017-02-01 18:14:48
    我们前面已经介绍了面向对象编程的特性之二,今天就介绍最后一个特性-多态。 什么叫多态?从字面上理解就是多种形态,即对同一客体,可以有多种不同的形式。就好像糖一样,有多种口味,你想吃什么口味的就...
  • 面向对象编程及其特性

    万次阅读 2019-06-11 11:39:23
    编程语言分为面向过程编程、函数式编程和面向对象编程。其实python就是一种面向对象编程,那么我们先了解一下它们的特点和优缺点以及它们的区别是什么。 面向过程编程:“面向过程”(Procedure Oriented)是一种以...
  • 面向对象的理解及特性总结

    万次阅读 多人点赞 2018-10-06 11:45:32
    面向对象三特性小总结 面向对象OOP 是基于面向过程而言,面向对象简单来说就是将功能封装到对象(数据和操作结合)里,我们面向对象,让对象去完成这些功能。 一切皆对象。 了解特性:封装,继承,多态 封装...
  • C++面向对象特性

    千次阅读 2013-07-30 17:22:46
    面向对象三个基本特征是:封装、继承、多态。 封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只...
  • 面向对象三个基本特征

    万次阅读 多人点赞 2019-06-10 17:17:23
    面向对象三个基本特征 面向对象三个基本特征是:封装、继承、多态。 封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的...
  • Java面向对象编程之特性

    万次阅读 多人点赞 2018-11-21 14:02:26
    一:面向对象编程特性 (1)继承 (2)封装 (3)多态 二:接口,内部类,抽象类 :常见关键字:this,super,static,final
  • 面向对象三特性之一 多态

    千次阅读 2017-03-11 01:28:23
    通俗的讲,就是同一东西表现出多种状态,在面向对象的描述中就是同一函数接口,实现多种不同的表现方式。【静态多态】:编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要...
  • 面向对象最重要的三个基本特征

    千次阅读 2016-03-08 23:01:41
    面向对象三个基本特征是:封装、继承、多态。
  • 面向对象编程的特性详解

    千次阅读 2020-02-22 22:42:59
    封装、多态和继承是面向对象编程的特性。 封装(Encapsulation) 封装的目的是为了保证变量的安全性,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员 如果不进行封装,类中的实例变量可以直接...
  • 1.面向对象三特性六大原则

    千次阅读 2018-04-04 12:48:17
    JAVA是一门面向对象的语言,那么其面向对象主要有以下几个特性和原则:面向对象三特性六大原则面向对象特性是"封装、"多态"、"继承",五大原则是"单一职责原则"、"...
  • 本文会结合虚拟机对引用和对象的不同处理来介绍特性的原理。 具体代码在我的GitHub中可以找到: https://github.com/h2pl/MyTech 文章首发于我的个人博客: ...
  • 面向对象三个基本特征(讲解)

    千次阅读 2018-12-04 09:26:00
    面向对象三个基本特征(讲解)面向对象三个基本特征是:封装、继承、多态。   封装封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以...
  • 三个基本元素: 1. 封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口...
  • 讲 Java语言中的面向对象特性

    千次阅读 2004-09-10 17:35:00
    面向对象编程的特性有哪三个?它们各自又有哪些特性? 3. 你知道java语言在面向对象编程方面有何独特的特点吗?难点: 1. 理解方法重载和方法重写,不要混淆了两者的使用。 2. 类变量和类方法的使用。 3. ...
  • 面向对象编程的三个基本特征

    万次阅读 2016-04-22 13:50:51
    三个基本特征面向对象三个基本特征是:封装、继承、多态。封装封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让...
  • 面向对象编程特性------封装、继承、多态

    万次阅读 多人点赞 2016-06-07 11:41:12
    本文是对面向对象编程特性(封装、继承、多态)的一学习总结。 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的...
  • Java语言中的面向对象特性 【课前思考】  1. 什么是对象?什么是类?什么是包?什么是接口?什么是内部类?  2. 面向对象编程的特性有哪三个?它们各自又有哪些特性?  3...
  • Vue-render函数的三个参数

    千次阅读 2018-11-20 16:00:29
    render函数的第一个参数 第一个参数必选. 可选类型 string:html标签 object:一个含有数据选项的对象 function:返回一个含有数据选项的对象 Vue.component('child', { props: ['level'], render: function ...
  • 面向对象三个基本特征是:封装、继承、多态   封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的...
  • JAVA面向对象三特性之一:多态

    千次阅读 2018-08-04 10:07:06
    多态是继封装、继承之后,面向对象的第特性 生活中,比如交通工具的种类可以分为飞机、汽车、轮船 再比如交通工具的运行方式飞机运行方式是飞在天上、汽车是在马路上开、轮船是在海上行驶 可见,同一行为,...
  • 《HTML5 WebSocket权威指南》学习笔记&3WebSocket方法的对象特性1. WebSocket方法a. send方法send方法用于在WebSocket连接建立后,客户端向服务端发送消息。可分为发送两种消息,一种是普通文本信息,一种是二进制...
  • 三个基本元素:  封装、继承和多态。 1. 封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 503,638
精华内容 201,455
关键字:

对象特性的三个参数