精华内容
下载资源
问答
  • 2、什么是面向对象语言,这就多说了; 3、什么是动态语言? 动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次...

    1、我们都知道一个程序的运行过程主要分为三个步骤:编译->链接->执行;

    2、什么是面向对象语言,这就不多说了;

    3、什么是动态语言?

    动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。

    4、什么是静态语言?

    静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等。

    5、什么是编译型语言?

    编译型语言在程序执行之前,有一个单独的编译过程,将程序翻译成机器语言,以后执行这个程序的时候,就不用再进行翻译了。例如:C/C++ 等都是编译型语言。

    6、什么是解释型语言?

    解释型语言,是在运行的时候将程序翻译成机器语言,所以运行速度相对于编译型语言要慢。Java,C#等都是解释型语言。


    虽然Java程序在运行之前也有一个编译过程,但是并不是将程序编译成机器语言,而是将它编译成字节码(可以理解为一个中间语言)。

    在运行的时候,由JVM将字节码再翻译成机器语言。

    脚本语言是一种解释性的语言,脚本语言一般都有相应的脚本引擎来解释执行。 他们一般需要解释器才能运行。JAVASCRIPT,ASP,PHP,PERL,Nuva都是脚本语言。

    展开全文
  • go是面向对象语言吗?

    万次阅读 2018-05-05 15:02:41
    转载链接:https://segmentfault.com/a/1190000001832282#articleHeader5原文链接:http://spf13.com/post/is-go-object-oriented前言面向对象的含义引入了对象(object)、类(class)、继承(inheritance)、子类...

    转载链接:https://segmentfault.com/a/1190000001832282#articleHeader5

    原文链接:http://spf13.com/post/is-go-object-oriented

    前言

    面向对象的含义引入了对象(object)、类(class)、继承(inheritance)、子类(subclass)、虚方法(virtual method)、协程(coroutine)等概念。面向对象引入颠覆性的思想——将数据和逻辑完全分离。大部分程序员通过编程语言进行软件开发都遵循着将数据和逻辑完全分离的原则。

    由于面向对象没有标准的定义,为了讨论的方便,接下来我们将提供一个标准的定义。

    面向对象系统将数据和代码通过“对象”集成到一起,而不是将程序看成由分离的数据和代码组成。对象是数据类型的抽象,它有状态(数据)和行为(代码)

    面向对象包括封装、继承、多态、虚派生等特性,接下来我们将看看go语言是怎样处理对象、多态、继承,相信读完接下来的介绍,您会对go是如何处理面向对象有自己的见解。

    go中的对象-封装

    go语言中没有对象(object)这个关键词。对象(object)仅仅是一个单词,重要的是它所表示的封装数据含义。尽管go中没有object这种类型,但是go中的struct有着跟object相同的特性。而在go中,采用了一种算是规定的方法:使用大小写来确定,大写是包外可见的,小写的struct或函数只能在包内使用。

    struct是一种包含了命名域和方法的类型

    让我们从一个例子中来理解它:

    type rect struct {
        width int
        height int
    }
    
    func (r *rect) area() int {
        return r.width * r.height
    }
    
    func main() {
        r := rect{width: 10, height: 5}
        fmt.Println("area: ", r.area())
    }

    (1) 代码的第一块定义了一个叫做rect的struct类型,该struct含有两个int类型的域;

    (2) 接下来定义了一个绑定在rect struct类型上的area方法。严格来说,area方法是绑定在指向rectct struct的指针上。如果方法绑定在rect type而非指针上,则在调用方法的时候需要使用该类型的值来调用,即使该值是空值,本例的空值实际是一个nil值;

    (3) 代码的最后一块是main函数,main函数第一行创建了一个rect类型的值,当然也有其他的方法来创建一个类型的值,这里给出的是一个地道的方法。main函数的最后一行是打印作用在r值上的area方法的返回结果。

    通过上面的描述,可以看出这很像对象的行为,我们可以创建一个结构化的数据类型,然后定义方法和这些数据进行交互。上述的简单例子并没有完成展示面向对象的所有特性,比如继承和多态。需要说明的是go不仅可以在struct上定义方法,在任何命名的类型上同样也可以。比如,可以定义一个名为Counter的新类型,该类型是int型的别名,然后在Counter类型上定义方法。例子详见:http://play.golang.org/p/LGB-2j707c

    继承和多态

    定义对象间的关系的方法有如下几种,它们之间都有一些差别,但目的都是一样的:复用代码
    单继承(Inheritance) 多继承(Multiple Inheritance) 多态(Subtyping/Polymorphism)对象组合(Object composition)
    继承:一个对象基于另外一个对象,使用其实现。有两种不同的继承实现:单继承和多继承。它们的不同在于对象是继承自一个对象还是多个对象。单继承关系是一棵树,而多继承关系是一个格状结构。单继承语言包括PHP、C#、Java、Ruby等,多继承语言包括Perl、Python、C++等

    多态

    多态是is-a的关系,继承是实现的复用。多态定义了两个对象的语义关系,继承定义两个对象的语法关系。

    对象组合

    对象组合是一个对象包含了其他对象,而非继承,它是has-a的关系,而非is-a。

    go语言的继承

    go有意得被设计为没有继承语法。但这并不意味go中的对象(struct value)之间没有关系,只不过go的作者选择了另外一种机制来暗含这种特性。实际上go的这种设计是一种非常好的解决方法,它解决了围绕着继承的数十年的老问题和争论。

    go语言中的多态和组合(最好不要继承)

    go语言严格遵守composition over inheritance principle的原则。go通过在struct和interface上使用组合和多态来实现继承关系。Person和Address之间的关系是这种实现的一个很好的例子:http://play.golang.org/p/LigPIVT2mf

    type Person struct {
       Name string
       Address Address    //匿名字段
    }
    
    type Address struct {
       Number string
       Street string
       City   string
       State  string
       Zip    string
    }
    
    func (p *Person) Talk() {
        fmt.Println("Hi, my name is", p.Name)
    }
    
    func (p *Person) Location() {
        fmt.Println("I’m at", p.Address.Number, p.Address.Street, p.Address.City, p.Address.State, p.Address.Zip)
    }
    
    func main() {
        p := Person{
            Name: "Steve",
            Address: Address{
                Number: "13",
                Street: "Main",
                City:   "Gotham",
                State:  "NY",
                Zip:    "01313",
            },
        }
    
        p.Talk()
        p.Location()
    }

    程序执行结果:


    上面的例子需要注意的是, Address仍然是一个不同的对象,只不过存在于Person中

    go中的伪多态

    我们通过扩展上面的例子来说明go中的伪多态。注意这里“伪”字说明实际上go是没有多态的概念的,只不过伪多态表现得像多态一样。下面的例子中,Person可以说话(Talk),一个Citizen也同时是一个Person,因此他也能说话(Talk)。在上面的例子中加入如下内容,完整代码见:http://play.golang.org/p/eCEpLkQPR3

    type Citizen struct {
       Country string
       Person
    }
    
    func (c *Citizen) Nationality() {
        fmt.Println(c.Name, "is a citizen of", c.Country)
    }
    
    func main() {
        c := Citizen{}
        c.Name = "Steve"
        c.Country = "America"
        c.Talk()
        c.Nationality()
    }

    上面的例子通过引入匿名域(Person)实现了is-a关系。Person是Citizen的一个匿名域(anonymous field),匿名域只给出了对象类型,而不给出类型的名字。通过匿名域,Citizen可以访问Person中的所有属性(域)和方法。程序执行结果如下所示:


    匿名域方法提升

    上述例子,Citizen可以和Person执行一样的Talk()方法。但如果想要Citizen的Talk()表现出不同的行为该怎么做呢?我们只需要在Citizen上定义方法Talk()即可。当调用c.Talk()的时候,调用的则是Citizen的Talk()方法而非Person的Talk()方法,http://play.golang.org/p/jafbVPv5H9

    func (c *Citizen) Talk() {
        fmt.Println("Hello, my name is", c.Name, "and I'm from", c.Country)
    }

    程序执行结果:


    为何匿名域不是合适的多态实现

    有两个原因:

    1. 匿名域仍然能被访问,就好像它们是被嵌入的对象一样。

    这并不是一件坏事,多继承存在的一个问题就是当多个父类具有相同的方法的时候,会产生歧义。然而go语言可以通过访问跟匿名类型同名的属性来访问嵌入的匿名对象。实际上当使用匿名域的时候,go会创建一个跟匿名类型同名的对象。上面的例子中,修改main方法如下,我们能很清楚得看出这一点:

    func main() {
    //    c := Citizen{}
        c.Name = "Steve"
        c.Country = "America"
        c.Talk()         // <- Notice both are accessible
        c.Person.Talk()  // <- Notice both are accessible
        c.Nationality()
    }
    2、真正的多态,派生对象就是父对象
    如果匿名对象能实现多态,则外层对象应该等同于嵌入的对象,而实际上并非如此,它们仍然是不同的存在。下面的例子印证了这一点:

    package main
    
    type A struct{
    }
    
    type B struct {
        A  //B is-a A
    }
    
    func save(A) {
        //do something
    }
    
    func main() {
        b := B
        save(&b);  //OOOPS! b IS NOT A
    }

    程序执行报错:

    prog.go:17: cannot use b (type *B) as type A in function argument
    [process exited with non-zero status]

    go中的真正的多态实现

    正如我们上面提到的,多态是一种is-a的关系。在go语言中,每种类型(type)都是不同的,一种类型不能完全等同于另外一种类型,但它们可以绑定到同一个接口(interface)上。接口能用于函数(方法)的输入输出中,因而可以在类型之间建立起is-a的关系

    go语言定义一个接口并不是使用using关键字,而是通过在对象上定义方法来实现。在Effective Go中指出,这种关系就像“如果某个东西能做这件事,那么就把它应用到这里”(不管黑猫白猫,只要能抓到老鼠,我就养这只猫)。这一点很重要,因为这允许一个定义在package外的类型也能实现该接口。

    我们接着上面的例子,增加一个新函数SpeakTo,然后修改main函数,将该方法应用到Citizen和Person上,http://play.golang.org/p/lvEjaMQ25D

    func SpeakTo(p *Person) {
        p.Talk()
    }
    
    func main() {
        p := Person{Name: "Dave"}
        c := Citizen{Person: Person{Name: "Steve"}, Country: "America"}
    
        SpeakTo(&p)
        SpeakTo(&c)
    }

    程序运行结果出错:类型不一致,因此可借助 接口来实现。

    Running it will result in
    prog.go:48: cannot use c (type *Citizen) as type *Person in function argument
    [process exited with non-zero status]

    跟预期的结果一样,编译失败。Citizen并不是Person类型,尽管他们拥有同样的属性。然而我们定义一个接口(interface)Human,然后将这个接口作为SpeakTo函数的输入参数,上面的例子就可以正常运行了,http://play.golang.org/p/ifcP2mAOnf

    type Human interface {
        Talk()
    }
    
    func SpeakTo(h Human) {
        h.Talk()
    }
    
    func main() {
        p := Person{Name: "Dave"}
        c := Citizen{Person: Person{Name: "Steve"}, Country: "America"}
    
        SpeakTo(&p)
        SpeakTo(&c)
    }
    程序输出结果:
    Hi, my name is Dave
    Hi, my name is Steve

    关于go语言中的多态,有如下两点需要注意。

    1. 可以把匿名域绑定到一个接口,也能绑定到多个接口。接口和匿名域一起使用,可以起到和多态同样的效果。

    2. go提供了多态的能力。接口的使用能使得实现了该接口方法的不同对象都能作为  函数的输入参数  ,甚至作为 返回结果 ,但它们仍然保持了它们自己的类型。这点从上面的例子能看出来,我们不能直接在初始化Citizen对象的时候设置Name值,因为Name不是Citizen的属性,而是Person的属性,因而不能再初始化Citizen的时候设置Name值。

    go,一个没有object和inheritance的面向对象的语言

    如上所述,面向对象的基本概念在go中被很好的实现了,虽然术语上存在差别。

    (1) go把struct作为数据和逻辑的结合。

    (2) 通过组合(composition),has-a关系来最小化代码重用,并且避免了继承的缺陷。

    (3) go使用接口(interface)来建立类型(type)之间的is-a关系。

    通过上面的论述,Go语言通过其语法特性可以实现面向对象编程。欢迎进入无对象的OO编程模型世界!

    讨论

    Join the discussion on hacker news and Reddit-Golang

    深入阅读

    http://nathany.com/good/
    http://www.artima.com/lejava/articles/designprinciples.html
    http://www.goinggo.net/2014/05/methods-interfaces-and-embedded-types.html

    展开全文
  • 面向对象语言的三大特征

    千次阅读 2016-08-12 10:55:55
    面向对象语言的三大特征是:封装 继承 多态 最近感觉,总结一下这个问题还是挺有必要的,所以转发了此篇文章的部分段落。 封装 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装,...

    面向对象语言的三大特征是:封装 继承 多态

    最近感觉,总结一下这个问题还是挺有必要的,所以转发了此篇文章的部分段落。

    封装

    封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。示例代码:

    public class Department
    {
         private string departname;
    
         // 读方法
         public string GetDepartname(){
              return departname;
         }
    
         //写方法
         public void SetDepartname( string a){
              departname=a;
         }
    
    }

    继承

    面向对象编程 (OOP) 语言的一个主要功能就是继承。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

    通过继承创建的新类称为“子类”或“派生类”。

    被继承的类称为基类父类超类

    继承的过程,就是从一般到特殊的过程。

    要实现继承,可以通过继承Inheritance)和组合Composition)来实现。

    在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

     

    继承概念的实现方式有三类:实现继承、接口继承和可视继承。

    ?         实现继承是指使用基类的属性和方法而无需额外编码的能力;

    ?         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

    ?         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

    在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是属于关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

    抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class

    OO开发范式大致为:划分对象抽象类将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

    普通类继承关系示例代码:

    public  class A{
         int test=0;
         public A(){
              test = 5;
              Console.WriteLine("I am A 公有默认构造函数 ,test={0}", test);
         }
    }
    
    public class B : A {
    
    }
    
    public class InheritanceTest1 {
            
         public static void Main(string[] args){
                B b = new B();
                Console.Read();
          }
    }

    多态

    多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

    实现多态,有二种方式,覆盖,重载。

    覆盖,是指子类重新定义父类的虚函数的做法。

    重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    其实,重载的概念并不属于面向对象编程,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_funcstr_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是覆盖。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:不要犯傻,如果它不是晚邦定,它就不是多态。

    那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用家谱中任一类的实例的某一属性时的正确调用。

        public class Animal
        {
            public virtual void Eat()
            {
                Console.WriteLine("Animal eat");
            }
        }
    
        public class Cat : Animal
        {
            public override void Eat()
            {
                Console.WriteLine("Cat eat");
            }
        }
    
        public class Dog : Animal
        {
            public override void Eat()
            {
                Console.WriteLine("Dog eat");
            }
        }
    
        class Tester
        {
            static void Main(string[] args)
            {
                Animal[] animals = new Animal[3];
    
                animals[0] = new Animal();
                animals[1] = new Cat();
                animals[2] = new Dog();
    
                for (int i = 0; i < 3; i++)
                {
                    animals[i].Eat();
                }
            }
        }


    文章会不断修改更新,不是最终版


    参考网站:

    http://www.360doc.com/content/12/0102/15/306774_176667425.shtml

    http://www.cnblogs.com/jiajiayuan/archive/2011/09/09/2172292.html

    http://blog.csdn.net/acmilanvanbasten/article/details/8625097

    http://www.cnblogs.com/jhxk/articles/1644018.html

    展开全文
  • R语言面向对象指南

    千次阅读 2015-09-21 21:53:00
    面向对象指南:这一章主要介绍怎样识别和使用 R 语言面向对象系统(以下简称 OO)。R 语言主要有三种 OO 系统(加上基本类型)。本指南的目的不是让你精通 R 语言的 OO,而是让你熟悉各种系统,并且能够准确地区分...

    原文链接:OO field guide


    面向对象指南:

    这一章主要介绍怎样识别和使用 R 语言的面向对象系统(以下简称 OO)。R 语言主要有三种 OO 系统(加上基本类型)。本指南的目的不是让你精通 R 语言的 OO,而是让你熟悉各种系统,并且能够准确地区分和使用它们。
    OO 最核心的就是类和方法的思想,类在定义对象的行为时主要是通过对象的属性以及它和其它类之间的关系。根据类的输入不同,类对方法、函数的选择也会不同。类的建造是有层次结构的:如果一个方法在子类中不存在,则使用父类中的方法;如果存在则继承父类中方法。

    三种 OO 系统在定义类和方法的时候有以下不同:

    • S3 实现的是泛型函数式 OO ,这与大部分的编程语言不同,像 Java、C++ 和 C# 它们实现的是消息传递式的 OO 。如果是消息传递,消息(方法)是传给一个对象,再由对象去决定调用哪个方法的。通常调用方法的形式是“对象名.方法名”,例如:canvas.drawRect(“blue”) 。而 S3 不同,S3 调用哪个方法是由泛型函数决定的,例如:drawRect(canvas, “blue”)。S3 是一种非正式的 OO 模式,它甚至都没有正式定义类这个概念。
    • S4 与 S3 很相似,但是比 S3 正规。S4 与 S3 的不同主要有两点:S4 对类有更加正式的定义(描述了每个类的表现形式和继承情况,并且对泛型和方法的定义添加了特殊的辅助函数);S4 支持多调度(这意味着泛型函数在调用方法的时候可以选择多个参数)。
    • Reference classes (引用类),简称 RC ,和 S3、S4有很大区别。RC 实现的是消息传递式 OO ,所以方法是属于类的,而不是函数。对象和方法之间用”$”隔开,所以调用方法的形式如:canvas$drawRect(“blue”) 。RC 对象也总是可变的,它用的不是 R 平常的 copy-on-modify 语义,而是做了部分修改。从而可以解决 S3、S4 难以解决的问题。

    还有另外一种系统,虽然不是完全的面向对象,但还是有必要提一下:

    • base types(基本类型),主要使用C语言代码来操作。它之所以重要是因为它能为其它 OO 系统提供构建块。

    以下内容从基本类型开始,逐个介绍每种 OO 系统。你将学习到怎样识别一个对象是属于哪种 OO 系统、方法的调用和使用,以及在该 OO 系统下如何创建新的对象、类、泛型和方法。本章节的结尾也有讲述哪种情况应该使用哪种系统。

    前提:

    你首先需要安装 pryr 包来获取某些函数:install.packages(“pryr”) 。

    问题:

    你是否已经了解本文要讲述的内容?如果你能准确地回答出以下问题,则可以跳过本章节了。答案请见本文末尾的问题答案

    1. 你怎样区分一个对象属于哪种 OO 系统?
    2. 如何确定基本类型(如整型或者列表)的对象?
    3. 什么是类的函数?
    4. S3 和 S4 之间的主要差异是什么? S4 和 RC 之间最主要的差异又是什么?

    文章梗概:


    基本类型:

    基本上每个 R 对象都类似于描述内存存储的 C 语言结构体,这个结构体包含了对象的所有内容(包括内存管理需要的信息,还有对象的基本类型)。基本类型并不是真的对象系统,因为只有 R 语言的核心团队才能创建新的类型。但结果新的基本类型竟然也很少见地被添加了:最近是在2011年,添加了两个你从来没在 R 里面见过的奇异类型(NEWSXP 和 FREESXP),它们能够有效地诊断出内存上的问题。在此之前,2005年为 S4 对象添加了一个特殊的基本类型(S4SXP)。

    Data structures 章节讲述了大部分普通的基本类型(原子向量和列表),但基本类型还包括 functions、environments,以及其它更加奇异的对象,如 names、calls、promises,之后你将会在本书中学到。你可以使用 typeof() 来了解对象的基本类型。但基本类型的名字在 R 中并不总是有效的,并且类型和 “is” 函数可能会使用不同的名字:

    # The type of a function is "closure"
    f <- function() {}
    typeof(f)
    #> [1] "closure"
    is.function(f)
    #> [1] TRUE
    
    # The type of a primitive function is "builtin"
    typeof(sum)
    #> [1] "builtin"
    is.primitive(sum)
    #> [1] TRUE

    你可能听过 mode() 和 storage.mode(),我建议不要使用这两个函数,因为它们只是 typeof() 返回值的别名,而且只使用与 S 语言。如果你想了解它们具体如何实现,可以去看一下它们的源代码。

    不同基本类型的函数一般都是用 C 语言编写的,在调度时使用switch语句(例如:switch(TYPEOF(x)))。尽管你可能没有写过 C 语言,但理解基本类型仍然很有必要,因为其他系统都是在此基础上的:S3 对象可以建立在所有基本类型上,S4 使用一个特殊的基本类型,而 RC 对象是 S4 和 environments(一个特殊的基本类型)的结合体。查看对象是否是一个单纯基本类型(即它不同时含 S3、S4、RC 的行为),使用 is.object(x) ,返回TRUE/FALSSE。


    S3:

    S3 是 R 语言的第一种也是最简单的一种 OO 系统。它还是唯一一种在基础包和统计包使用的 OO 系统,CRAN包中最平常使用的 OO 系统。

    识别对象、泛型函数、方法:

    你遇到的大部分对象都是 S3 对象。但不幸的是在 R 中并没有可以简单检测一个方法是否是 S3 的方法。最接近的方法就是 is.object(x) & !isS4(x),即它是一个对象,但不是 S4 对象。一个更简单的方法就是使用 pryr::otype() :

    library(pryr)
    
    df <- data.frame(x = 1:10, y = letters[1:10])
    otype(df)    # A data frame is an S3 class
    #> [1] "S3"
    otype(df$x)  # A numeric vector isn't
    #> [1] "base"
    otype(df$y)  # A factor is
    #> [1] "S3"

    在 S3,方法是属于函数的,这些函数叫做泛型函数,或简称泛型。S3 的方法不属于对象或者类。这和大部分的编程语言都不同,但它确实是一种合法的 OO 方式。

    你可以调用 UseMethod() 方法来查看某个函数的源代码,从而确定它是否是 S3 泛型。和 otype() 类似,prpy 也提供了 ftype() 来联系着一个函数(如果有的话)描述对象系统。

    mean
    #> function (x, ...) 
    #> UseMethod("mean")
    #> <bytecode: 0x24bfa50>
    #> <environment: namespace:base>
    ftype(mean)
    #> [1] "s3"      "generic"

    有些 S3 泛型,例如 [ 、sum()、cbind(),不能调用 UseMethod(),因为它们是用 C 语言来执行的。不过它们可以调用 C 语言的函数 DispatchGroup() 和 DispatchOrEval()。利用 C 代码进行方法调用的函数叫作内部泛型。可以使用 ?”internal generic” 查看。

    给定一个类,S3 泛型的工作是调用正确的 S3 方法。你可以通过 S3 方法的名字来识别(形如 generic.class())。例如,泛型 mean() 的 Date 方法为 mean.Date(),泛型print() 的向量方法为 print.factor() 。
    这也就是为什么现代风格不鼓励在函数名字里使用 “.” 的原因了。类的名字也不使用 “.” 。pryr::ftype() 可以发现这些异常,所以你可以用它来识别一个函数是 S3 方法还是泛型:

    ftype(t.data.frame) # data frame method for t()
    #> [1] "s3"     "method"
    ftype(t.test)       # generic function for t tests
    #> [1] "s3"      "generic"

    你可以调用 methods() 来查看属于某个泛型的所有方法:

    methods("mean")
    #> [1] mean.Date     mean.default  mean.difftime mean.POSIXct  mean.POSIXlt 
    #> see '?methods' for accessing help and source code
    methods("t.test")
    #> [1] t.test.default* t.test.formula*
    #> see '?methods' for accessing help and source code

    (除了在基础包里面定义的一些方法,大多数 S3 的方法都是不可见的使用 getS3method() 来阅读它们的源码。)

    你也可以列出一个给出类中包含某个方法的所有泛型:

    methods(class = "ts")
    #>  [1] aggregate     as.data.frame cbind         coerce        cycle        
    #>  [6] diffinv       diff          initialize    kernapply     lines        
    #> [11] Math2         Math          monthplot     na.omit       Ops          
    #> [16] plot          print         show          slotsFromS3   time         
    #> [21] [<-           [             t             window<-      window       
    #> see '?methods' for accessing help and source code

    你也可以从接下来的部分知道,要列出所有的 S3 类是不可能的。

    定义类和创建对象:

    S3 是一个简单而特殊的系统,它对类没有正式的定义。要实例化一个类,你只能拿一个已有的基础对象,再设置类的属性。你可以在创建类的时候使用 structure(),或者事后用 class<-():

    # Create and assign class in one step
    foo <- structure(list(), class = "foo")
    
    # Create, then set class
    foo <- list()
    class(foo) <- "foo"

    S3 对象的属性通常建立在列表或者原子向量之上(你可以用这个属性去刷新你的内存属性),你也能把函数转成 S3 对象,其他基本类型要么在 R 中很少见,要么就是该语义不能很好地在属性下运行。
    你可以通过 class() 把类看作任意的对象,也可以通过 inherits(x, “classname”) 来查看某个对象是否继承自某个具体的类。

    class(foo)
    #> [1] "foo"
    inherits(foo, "foo")
    #> [1] TRUE

    S3 对象所属于的类可以被看成是一个向量,一个通过最重要的特性来描述对象行为的向量。例如对象 glm() 的类是 c(“glm”, “lm”),它表明着广义线性模型的行为继承自线性模型。类名通常是小写的,并且应该避免使用 “.” 。否则该类名将会混淆为下划线形式的 my_class,或者 CamelCase 写法的 MyClass。

    大多数的 S3 类都提供了构造函数:

    foo <- function(x) {
      if (!is.numeric(x)) stop("X must be numeric")
      structure(list(x), class = "foo")
    }

    如果它是可用的,则你应该使用它(例如 factor() 和 data.frame())。这能确保你在创造类的时候使用正确的组件。构造函数的名字一般是和类名是相同的。

    开发者提供了构造函数之后,S3 并没有对它的正确性做检查。这意味着你可以改变现有对象所属于的类:

    # Create a linear model
    mod <- lm(log(mpg) ~ log(disp), data = mtcars)
    class(mod)
    #> [1] "lm"
    print(mod)
    #> 
    #> Call:
    #> lm(formula = log(mpg) ~ log(disp), data = mtcars)
    #> 
    #> Coefficients:
    #> (Intercept)    log(disp)  
    #>      5.3810      -0.4586
    
    # Turn it into a data frame (?!)
    class(mod) <- "data.frame"
    # But unsurprisingly this doesn't work very well
    print(mod)
    #>  [1] coefficients  residuals     effects       rank          fitted.values
    #>  [6] assign        qr            df.residual   xlevels       call         
    #> [11] terms         model        
    #> <0 rows> (or 0-length row.names)
    # However, the data is still there
    mod$coefficients
    #> (Intercept)   log(disp) 
    #>   5.3809725  -0.4585683

    如果你在之前使用过其他的 OO 语言,S3 可能会让你觉得很恶心。但令人惊讶的是,这种灵活性带来的问题很少:虽然你能改变对象的类型,但你并不会这么做。R 并不用提防自己:你可以很容易射自己的脚,只要你不把抢瞄在你的脚上并扣动扳机,你就不会有问题。

    创建新的方法和泛型:

    如果要添加一个新的泛型,你只要创建一个叫做 UseMethod() 的函数。UseMethod() 有两个参数:泛型函数的名字和用来调度方法的参数。如果第二个参数省略了,则根据第一个参数来调度方法。但是没有必要去省略 UseMethod() 的参数,你也不应该这么做。

    f <- function(x) UseMethod("f")

    没有方法的泛型是没有用的。如果要添加方法,你只需要用 generic.class 创建一个合法的函数:

    f.a <- function(x) "Class a"
    
    a <- structure(list(), class = "a")
    class(a)
    #> [1] "a"
    f(a)
    #> [1] "Class a"

    用同样的方法可以对已有的泛型添加方法:

    mean.a <- function(x) "a"
    mean(a)
    #> [1] "a"

    如你所看到的,它并没有确保类和泛型兼容的检查机制,它主要是靠编程者自己来确定自己的方法不会违反现有代码的期望。

    方法调度:

    S3 的方法调度比较简单。UseMethod() 创建一个向量或者一个函数名字(例如:paste0(“generic”, “.”, c(class(x), “default”))),并逐个查找。default 类作为回落的方法,以防其他未知类的情况。

    f <- function(x) UseMethod("f")
    f.a <- function(x) "Class a"
    f.default <- function(x) "Unknown class"
    
    f(structure(list(), class = "a"))
    #> [1] "Class a"
    # No method for b class, so uses method for a class
    f(structure(list(), class = c("b", "a")))
    #> [1] "Class a"
    # No method for c class, so falls back to default
    f(structure(list(), class = "c"))
    #> [1] "Unknown class"

    组泛型方法增加了一些复杂性,组泛型为一个函数实现复合泛型的多个方法提供了可能性。它们包含的四组泛型和函数如下:

    • Math: abs, sign, sqrt, floor, cos, sin, log, exp, …
    • Ops: +, -, *, /, ^, %%, %/%, &, |, !, ==, !=, <, <=, >=, >
    • Summary: all, any, sum, prod, min, max, range
    • Complex: Arg, Conj, Im, Mod, Re

    组泛型是相对比较先进的技术,超出了本章的范围。但是你可以通过 ?groupGeneric 查看更多相关信息。区分组泛型最关键的是要意识到 Math、Ops、Summary 和 Complex 并不是真正的函数,而是代表着函数。注意在组泛型中有特殊的变量 .Generic 提供实际的泛型函数调用。

    如果你有复数类模板的层次结构,那么调用“父”方法是有用的。要准确定义它的意义的话有点难度,但如果当前方法不存在的话它基本上都会被调用。同样的,你可以使用 ?NextMethod 查看相关信息。

    因为方法是正规的 R 函数,所以你可以直接调用它:

    c <- structure(list(), class = "c")
    # Call the correct method:
    f.default(c)
    #> [1] "Unknown class"
    # Force R to call the wrong method:
    f.a(c)
    #> [1] "Class a"

    不过这种调用的方法和改变对象的类属性一样危险,所以一般都不这样做。不要把上膛了的枪瞄在自己的脚上。使用上述方法的唯一原因是它可以通过跳过方法调用达到很大的性能改进,你可以查看性能章节查看详情。

    非 S3 对象也可以调用 S3 泛型,非内部的泛型会调用基本类型的隐式类。(因为性能上的原因,内部的泛型并不会这样做。)确定基本类型的隐式类有点难,如下面的函数所示:

    iclass <- function(x) {
      if (is.object(x)) {
        stop("x is not a primitive type", call. = FALSE)
      }
    
      c(
        if (is.matrix(x)) "matrix",
        if (is.array(x) && !is.matrix(x)) "array",
        if (is.double(x)) "double",
        if (is.integer(x)) "integer",
        mode(x)
      )
    }
    iclass(matrix(1:5))
    #> [1] "matrix"  "integer" "numeric"
    iclass(array(1.5))
    #> [1] "array"   "double"  "numeric"

    练习:

    1. 查阅 t() 和 t.test() 的源代码,并证明 t.test() 是一个 S3 泛型而不是 S3 方法。如果你用 test 类创建一个对象并用它调用 t() 会发生什么?
    2. 在 R 语言的基本类型中什么类有 Math 组泛型?查阅源代码,该方法是如何工作的?
    3. R 语言在日期时间上有两种类,POSIXct 和 POSIXlt(两者都继承自 POSIXt)。哪些泛型对于这两个类是有不同行为的?哪个泛型共享相同的行为?
    4. 哪个基本泛型定义的方法最多?
    5. UseMethod() 通过特殊的方式调用方法。请预测下列代码将会返回什么,然后运行一下,并且查看 UseMethod() 的帮助文档,推测一下发生了什么。用最简单的方式记下这些规则。
    y <-1
    g <-function(x) {
      y <-2UseMethod("g")
    }
    g.numeric <-function(x) y
    g(10)
    
    h <-function(x) {
      x <-10UseMethod("h")
    }
    h.character <-function(x) paste("char", x)
    h.numeric <-function(x) paste("num", x)
    
    h("a")
    1. 内部泛型不分配在基类类型的隐式类。仔细查阅 ?”internal generic”,为什么下面例子中的 f 和 g 的长度不一样?哪个函数可以区分 f 和 g 的行为?
    f <- function() 1
    g <- function() 2
    class(g) <- "function"
    
    class(f)
    class(g)
    
    length.function <- function(x) "function"
    length(f)
    length(g)


    S4:

    S4 工作的方式和 S3 比较相似,但它更加正式和严谨。方法还是属于函数,而不是类。但是:

    • 类在描述字段和继承结构(父类)上有更加正式的定义。
    • 方法调用可以传递多个参数,而不仅仅是一个。
    • 出现了一个特殊的运算符——@,从 S4 对象中提取 slots(又名字段)。

    所以 S4 的相关代码都存储在 methods 包里面。当你交互运行 R 程序的时候这个包都是可用的,但在批处理的模式下则可能不可用。所以,我们在使用 S4 的时候一般直接使用 library(methods) 。
    S4 是一种丰富、复杂的系统,并不是一两页纸能解释完的。所以在此我把重点放在 S4 背后的面向对象思想,这样大家就可以比较好地使用 S4 对象了。如果想要了解更多,可以参考以下文献:

    • S4 系统在 Bioconductor 中的发展历程
    • John Chambers 写的《Software for Data Analysis》
    • Martin Morgan 在 stackoverflow 上关于 S4 问题的回答

    识别对象、泛型函数和方法:

    要识别 S4 对象 、泛型、方法还是很简单的。对于 S4 对象:str() 将它描述成一个正式的类,isS4() 会返回 TRUE,prpy::otype() 会返回 “S4” 。对于 S4 泛型函数:它们是带有很好类定义的 S4 对象。
    常用的基础包里面是没有 S4 对象的(stats, graphics, utils, datasets, 和 base),所以我们要从内建的 stats4 包新建一个 S4 对象开始,它提供了一些 S4 类和方法与最大似然估计:

    library(stats4)
    
    # From example(mle)
    y <- c(26, 17, 13, 12, 20, 5, 9, 8, 5, 4, 8)
    nLL <- function(lambda) - sum(dpois(y, lambda, log = TRUE))
    fit <- mle(nLL, start = list(lambda = 5), nobs = length(y))
    
    # An S4 object
    isS4(fit)
    #> [1] TRUE
    otype(fit)
    #> [1] "S4"
    
    # An S4 generic
    isS4(nobs)
    #> [1] TRUE
    ftype(nobs)
    #> [1] "s4"      "generic"
    
    # Retrieve an S4 method, described later
    mle_nobs <- method_from_call(nobs(fit))
    isS4(mle_nobs)
    #> [1] TRUE
    ftype(mle_nobs)
    #> [1] "s4"     "method"

    用带有一个参数的 is() 来列出对象继承的所有父类。用带有两个参数的 is() 来验证一个对象是否继承自该类:

    is(fit)
    #> [1] "mle"
    is(fit, "mle")
    #> [1] TRUE

    你可以使用 getGenerics() 来获取 S4 的所有泛型函数,或者使用 getClasses() 来获取 S4 的所有类。这些类包括 S3 对 shim classes 和基本类型。另外你可以使用 showMethods() 来获取 S4 的所有方法。

    定义类和新建对象

    在 S3,你可以通过更改类的属性就可以改变任意一个对象,但是在 S4 要求比较严格:你必须使用 setClass() 定义类的声明,并且用 new() 新建一个对象。你可以用特殊的语法 class?className(例如:class?mle)找到该类的相关文档。
    S4 类有三个主要的特性:

    • 名字:一个字母-数字的类标识符。按照惯例,S4 类名称使用 UpperCamelCase 。
    • 已命名的 slots(字段),它用来定义字段名称和允许类。例如,一个 person 类可能由字符型的名称和数字型的年龄所表征:list(name = "character", age = "numeric")
    • 父类。你可以给出多重继承的多个类,但这项先进的技能增加了它的复杂性。

    slotscontains,你可以使用setOldClass()来注册新的 S3 或 S4 类,或者基本类型的隐式类。在slots,你可以使用特殊的ANY类,它不限制输入。
    S4 类有像 validity 方法的可选属性,validity 方法可以检验一个对象是否是有效的,是否是定义了默认字段值的 prototype 对象。使用?setClass查看更多细节。
    下面的例子新建了一个具有 name 字段和 age 字段的 Person 类,还有继承自 Person 类的 Employee 类。Employee 类从 Person 类继承字段和方法,并且增加了字段 boss 。我们调用 new() 方法和类的名字,还有name-values这样成对的参数值来新建一个对象。

    setClass("Person",
      slots = list(name = "character", age = "numeric"))
    setClass("Employee",
      slots = list(boss = "Person"),
      contains = "Person")
    
    alice <- new("Person", name = "Alice", age = 40)
    john <- new("Employee", name = "John", age = 20, boss = alice)

    大部分 S4 类都有一个和类名相同名字的构造函数:如果有,可以直接用它来取代 new()
    要访问 S4 对象的字段,可以用 @ 或者 slot()

    alice@age
    #> [1] 40
    slot(john, "boss")
    #> An object of class "Person"
    #> Slot "name":
    #> [1] "Alice"
    #> 
    #> Slot "age":
    #> [1] 40

    @$ 等价,slot()[] 等价)
    如果一个 S4 对象继承自 S3 类或者基本类型,它会有特殊的属性 .Data

    setClass("RangedNumeric",
      contains = "numeric",
      slots = list(min = "numeric", max = "numeric"))
    rn <- new("RangedNumeric", 1:10, min = 1, max = 10)
    rn@min
    #> [1] 1
    rn@.Data
    #>  [1]  1  2  3  4  5  6  7  8  9 10

    因为 R 是响应式编程的语言,所以它可以随时创建新的类或者重新定义现有类。这将会造成一个问题:当你在响应式地调试 S4 的时候,如果你更改了一个类,你要知道你已经把该类的所有对象都更改了。

    新建方法和泛型函数

    S4 提供了特殊的函数来新建方法和泛型。setGeneric() 将产生一个新的泛型,或者把已有函数转成泛型。

    setGeneric("union")
    #> [1] "union"
    setMethod("union",
      c(x = "data.frame", y = "data.frame"),
      function(x, y) {
        unique(rbind(x, y))
      }
    )
    #> [1] "union"

    如果你要重新创建了一个泛型,你需要调用 standardGeneric() :

    setGeneric("myGeneric", function(x) {
      standardGeneric("myGeneric")
    })
    #> [1] "myGeneric"

    S4 中的 standardGeneric() 相当于 UseMethod()


    测试的答案

    1. 要确定一个对象属于哪种面向对象系统,你可以用排除法,如果 !is.object(x) 返回 TRUE,那么它是一个基本对象。如果 !isS4(x) 返回 TRUE,那么它是一个 S3 。如果 !is(x, "refClass") 返回 TRUE, 那么它是一个 S4 ,否则它是 RC 。
    2. typeof() 来确定基本类型的对象。
    3. 泛型函数调用特殊方法的时候主要是通过它的参数输入来确定的,在 S3 和 S4 系统,方法属于泛型函数,不像其他编程语言那样属于类。
    4. S4 比 S3 更加正式,并且支持多重继承和多重调度,RC 对象的语义和方法是属于类的,而不属于函数。
    展开全文
  • Java是纯面向对象语言吗?

    千次阅读 2016-10-14 09:07:48
    Java——是否确实的 “纯面向对象”?让我们深入到Java的世界,试图来证实它。 在我刚开始学习 Java 的前面几年,我从书本里知道了 Java 是遵循 “面向对象编程范式(Object Oriented Programming paradigm)”的...
  • 1) main函数在任何类里面,因为这点,所以做到完全面向对象,下面其他的都是可以解决的 2)基本的数据类型也不是对象(从这点上看JAVA也不是完全面向对象的) 3)友元会破坏封装性 4)支持独立的函数,数据...
  • 首先必须申明的是,本文所讲述的观点仅适应于基于传统... 总的来说,学好面向对象语言的目的就是能够更好地进行面向对象的分析与设计。在这里或许会有这样的疑问,面向对象的分析与设计是设计层面上的东西,它使得系统
  • 面向对象语言的多态性问题

    千次阅读 2014-08-01 09:40:47
    一直以来在我们所见到的概念中,面向对象语言有三大特性:封装性、继承和多态。 封装性: 在面向对象的语言中存在有类的概念,因此实现了数据和行为(方法)的封装,在外部表现为统一的类或对象。在类的外部要访问...
  • 汉语——世界上唯一的面向对象语言

    千次阅读 多人点赞 2017-04-17 16:35:02
    严谨地说,标题应该是“汉语是一种面向对象的高级语言”。在网上看到过很多有关汉语和英语比较的文章,他们写的都很不错,并且列举了大量的事例来证明汉语或者英语是优秀的。不过,我想用计算机软件设计的观点,来...
  • Go语言面向对象编程

    千次阅读 2014-01-26 09:31:31
    前段时间接触Go语言,感觉有很多新的理念,今天先转载一篇文章,以后...Go 语言面向对象编程(OOP)非常简洁而优雅。说它简洁,简介之处在于,它没有了OOP中很多概念,比如:继承、虚函数、构造函数和析构函数、隐藏的
  • Java — 面向对象的编程语言

    千次阅读 2020-02-14 18:35:52
    本篇文章将对 Java 中面向对象的一些概念做出详细讨论。 一、为什么要面向对象? 老是在说面向对象面向对象中的这个对象到底是什么呢? 1、什么是对象? 对象是人们可以进行研究的一切事物,从最简单的一支铅笔到...
  • 面向对象语言主要有哪些

    万次阅读 2019-01-07 11:07:52
    面向对象程序设计(Object Oriented Programming,OOP)的实质是选用一种面向对象程序设计语言(OOPL),采用对象、类及其相关概念所进行的程序设计。主要有Smalltalk、Eiffel、C++、Java、PHP等。 1、Smalltalk 一种...
  • JavaScript是一种面向对象语言

    千次阅读 2018-11-02 11:36:44
    JavaScript是一种面向对象语言严格的讲,这意味着我们不用全局的定义函数去操作不同类型的值。 数据本身可以定义方法来使用值。 如:a = [5,1,2,4,3]  要对a数组进行排序,不必要将a传入sort()函数,而是调用...
  • 面向对象语言技术特点

    千次阅读 2018-06-30 20:24:31
    具有支持类和对象概念的定义与实现机制 具有实现继承的语言机制 具有实现属性和服务的机制 具有参数化类 提供类型检查 提供类库 提供持久对象的保存 提供封装与打包 提供可视化开发环境 ...
  • Java和C++谁是更纯的面向对象语言

    千次阅读 2014-12-31 18:17:42
    Java和c++这两个语言面向对象方面,谁更纯方面,人们的认识是一致的。大家都知道,c++是一个混合、型的语言,而Java是一个纯面向对象语言。c++是一个面向过程与面向对象的混合型语言。但是Java是不是100%纯面向...
  • Java到底是不是一种纯面向对象语言

    万次阅读 2017-05-26 14:30:26
    Java——是否确实的 “纯面向对象”?让我们深入到Java的世界,试图来证实它。 在我刚开始学习 Java 的前面几年,我从书本里知道了 Java 是遵循 “面向对象编程范式(Object Oriented Programming paradigm)”的...
  • TOC \o "1-3" \n \h \z 第1章 Java语言面与向对象的程序设计 1.1 Java语言简介 1.1.1 Java 语言出现的背景影响及应用前景 1.1.2 Java 的特点 1.1.3 Java和CC++ 1.2 面向对象程序设计 1.2.1 面向对象概述 1.2.2 对象...
  • Java——一种彻底的面向对象语言

    千次阅读 2016-03-08 20:44:57
    也就意味着 Java 是一种彻底的面向对象语言,彻底对应着everything。 使用 Java 无论想实现何种代码逻辑,首先需要定义类,而对象是类的实例化。没有类与对象,一切寸步难行。程序逻辑依赖对象的创建。类的抽象和...
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...
  • 转载本文章请标明作者和出处 本文出自《Darwin的程序空间》 ...完成内容:python面向对象和文件操作内容 博客目的:总结归纳,当然要能帮助其他刚学习Python的童鞋,不胜荣幸 人物:一个心血来潮学习Pyt...
  • Java是一门面向对象的程序设计语言

    千次阅读 2019-04-13 10:07:11
    将一个问题抽象出来,并且用编程语言将这个问题解决,这就是程序设计。而人们能够解决的问题的复杂性直接取决于抽象的类型和质量,类型指的是抽象的是什么?质量指的是抽象的程度。 抽象出要解决的问题,通过编程...
  • 面向对象语言概论 (一)

    千次阅读 2002-10-25 16:24:00
    面向对象语言概论 (译自Martin Abadi, Luca Cardelli的对象理论一书的第一部分)译者前言这本书是我们上面向对象类型理论的教材。当时上这门课时,心里满不以为然,觉得自己的C++和OO已经颇有造纸,C++和Java的类型...
  • 什么是面向对象,以及什么是类和对象

    千次阅读 多人点赞 2019-08-11 19:04:04
    什么是面向对象,以及什么是类和对象 ​ 1.1面向过程与面向对象的区别 ​ 面向对象是一种现在最为流行的程序设计方法 。将面向过程与面向对象进行比较。 ​ 首先这两个种都是一种编程思维, 在一些简单的动作...
  • 面向对象思想————不看后悔! ————转载自:https://zhidao.baidu.com/question/751474259711412524.html 前言: 整理这份资料的目的是为了帮助朋友们能够更直观的理解面向对象的编程。让后来者能够少走一些...
  • C++面试题-面向对象-面向对象概念

    千次阅读 2019-01-11 00:16:27
    而C++是面向对象语言,主要通过类的形式来实现程序功能。 使用C++编写的面向对象应用程序比C语言编写的程序更容易维护、扩展性更强。 C++多用于开发上层应用软件,而C语言代码体积小、执行效率高,多编写系统软件...
  • 面向对象

    千次阅读 2018-03-21 14:23:06
    面向对象 一封装 二多态 三继承面向对象一、封装封装是对象和类概念的主要特性。它是隐藏内部实现,稳定外部接口,可以看作是“包装”。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 153,823
精华内容 61,529
关键字:

不属于面向对象的语言是