精华内容
下载资源
问答
  • Java类和对象 详解(一)

    万次阅读 多人点赞 2016-10-06 20:48:02
    一、面向对象简述面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计...

    一、面向对象简述

    面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,在70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计思路,才形成C++,而由C++产生了Java这门面向对象的编程语言。

    但是在面向对象设计之前,广泛采用的是面向过程,面向过程只是针对于自己来解决问题。面向过程的操作是以程序的基本功能实现为主,实现之后就完成了,也不考虑修改的可能性,面向对象,更多的是要进行子模块化的设计,每一个模块都需要单独存在,并且可以被重复利用,所以,面向对象的开发更像是一个具备标准的开发模式。

    在面向对象定义之中,也规定了一些基本的特征:
    (1)封装:保护内部的操作不被破坏;
    (2)继承:在原本的基础之上继续进行扩充;
    (3)多态:在一个指定的范围之内进行概念的转换。

    对于面向对象的开发来讲也分为三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。

    二、类与对象的基本概念

    类与对象时整个面向对象中最基础的组成单元。

    :是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
    对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。

    可以一句话来总结出类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。

    三、类与对象的定义和使用

    在Java中定义类,使用关键字class完成。语法如下:

    class 类名称 {
             属性 (变量) ;
             行为 (方法) ;
    }

    范例:定义一个Person类

    class Person {     // 类名称首字母大写
        String name ;
        int age ;
        public void tell() {        // 没有static
              System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }

    类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:

    (1)格式一:声明并实例化对象

    类名称 对象名称 = new 类名称 () ;

    (2)格式二:先声明对象,然后实例化对象:

    类名称 对象名称 = null ;
    对象名称 = new 类名称 () ;

    引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。

    当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
    对象.属性:表示调用类之中的属性;
    对象.方法():表示调用类之中的方法。

    范例:使用对象操作类

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = new Person() ;// 声明并实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    姓名:张三,年龄:30

    以上完成了一个类和对象的操作关系,下面换另外一个操作来观察一下:

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = null;//声明对象
                per = new Person() ;//实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    姓名:张三,年龄:30

    那么,问题来了,以上两种不同的实例化方式有什么区别呢?
    我们从内存的角度分析。首先,给出两种内存空间的概念:
    (1)堆内存:保存对象的属性内容。堆内存需要用new关键字来分配空间;
    (2)栈内存:保存的是堆内存的地址(在这里为了分析方便,可以简单理解为栈内存保存的是对象的名字)。

    1

    任何情况下,只要看见关键字new,都表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。

    于是,上面两种对象实例化对象方式内存表示如下:
    这里写图片描述

    两种方式的区别在于①②,第一种声明并实例化的方式实际就是①②组合在一起,而第二种先声明然后实例化是把①和②分步骤来。

    另外,如果使用了没有实例化的对象,结果如何?
    如下:

    package com.wz.classandobj;
    
    class Person { 
        String name ;
        int age ;
        public void get() {
            System.out.println("姓名:" + name + ",年龄:" + age);
        }
    }
    
    public class TestDemo {
            public static void main(String args[]) {
                Person per = null;//声明对象
                //per = new Person() ;//实例化对象
                per.name = "张三" ;//操作属性内容
                per.age = 30 ;//操作属性内容
                per.get() ;//调用类中的get()方法
            }
    }

    运行结果:

    Exception in thread "main" java.lang.NullPointerException
        at com.wz.classandobj.TestDemo.main(TestDemo.java:15)

    此时,程序只声明了Person对象,但并没有实例化Person对象(只有了栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只要是应用数据类型都有可能出现。

    四、对象引用传递初步分析

    引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。

    下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。

    我们来看一个范例:

    class Person {     
             String name ;
             int age ;
             public void tell() {        
                       System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }
    public class TestDemo {
             public static void main(String args[]) {
                       Person per1 = new Person() ;         // 声明并实例化对象
                       per1.name = "张三" ;
                       per1.age = 20 ;
                       Person per2 = per1 ;  // 引用传递
                       per2.name = "李四" ;
                       per1.tell() ;
             }
    }

    对应的内存分配图如下:

    11

    再来看另外一个:

    class Person {
             String name ;
             int age ;
             public void tell() {
                       System.out.println("姓名:" + name + ",年龄:" + age) ;
             }
    }
    public class TestDemo {
             public static void main(String args[]) {
                       Person per1 = new Person() ;         // 声明并实例化对象
                       Person per2 = new Person() ;
                       per1.name = "张三" ;
                       per1.age = 20 ;
                       per2.name = "李四" ;
                       per2.age = 30 ;
                       per2 = per1 ;// 引用传递
                       per2.name = "王五" ;
                       per1.tell() ;
             }
    }

    对应的内存分配图如下:
    12

    垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。

    未完待续。。。

    展开全文
  • 除了前面到的简单数据类型,Elasticsearch还支持JSON 的null ,数组,和对象. 空域 字段取值可以为空,当然,数组也可以为空。 然而, Lucene 中是不能存储 null 值的,所以我们认为存在 null ...

    复杂数据类型

    除了前面说到的简单数据类型,Elasticsearch还支持JSON 的null ,数组,和对象.

    空域

    字段取值可以为空,当然,数组也可以为空。 然而,在 Lucene 中是不能存储 null 值的,所以我们认为存在 null 值的域为空域。

    下面三种域被认为是空的,它们将不会被索引:

    "null_value":               null,
    "empty_array":              [],
    "array_with_null_value":    [ null ]
    

    数组域

    很多时候,我们希望 tag 域 包含多个标签。我们可以以数组的形式索引标签:

    { "tag": [ "search", "nosql" ]}
    

    对于数组,没有特殊的映射需求。任何域都可以包含0、1或者多个值,就像全文域分析得到多个词条。

    这暗示 数组中所有的值必须是相同数据类型的 。你不能将日期和字符串混在一起。如果你通过索引数组来创建新的域,Elasticsearch 会用数组中第一个值的数据类型作为这个域的 类型 。

    注意:
    当你从 Elasticsearch 得到一个文档,每个数组的顺序和你当初索引文档时一样。你得到的 _source 域,包含与你索引的一模一样的 JSON 文档。

    但是,数组是以多值域 索引的—可以搜索,但是无序的。 在搜索的时候,你不能指定 “第一个” 或者 “最后一个”。 更确切的说,把数组想象成 装在袋子里的值 。

    多层级对象

    内部对象 经常用于嵌入一个实体或对象到其它对象中。例如,与其在 tweet 文档中包含 user_name 和 user_id 域,我们也可以这样写:

    {
        "tweet":            "Elasticsearch is very flexible",
        "user": {
            "id":           "@johnsmith",
            "gender":       "male",
            "age":          26,
            "name": {
                "full":     "John Smith",
                "first":    "John",
                "last":     "Smith"
            }
        }
    }
    

    内部对象的映射

    Elasticsearch 会动态监测新的对象域并映射它们为 对象 ,在 Elasticsearch 6之前properties 属性下列出内部域:

    {
      "gb": {
        "tweet": 
          "properties": {
            "tweet":            { "type": "string" },
            "user": {
              "type":             "object",
              "properties": {
                "id":           { "type": "string" },
                "gender":       { "type": "string" },
                "age":          { "type": "long"   },
                "name":   { 
                  "type":         "object",
                  "properties": {
                    "full":     { "type": "string" },
                    "first":    { "type": "string" },
                    "last":     { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    }
    

    user 和 name 域的映射结构与 tweet 类型的相同。事实上, type 映射只是一种特殊的 对象 映射,我们称之为 根对象 。除了它有一些文档元数据的特殊顶级域,例如 _source 和 _all 域,它和其他对象一样。

    注:
    elasticsearch6以后没有object类型了,默认嵌套处理了。elasticsearch6以后映射查询结果如下:

    {
      "test": {
        "mappings": {
          "test": {
            "properties": {
              "tweet": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "user": {
                "properties": {
                  "age": {
                    "type": "long"
                  },
                  "gender": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  },
                  "id": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  },
                  "name": {
                    "properties": {
                      "first": {
                        "type": "text",
                        "fields": {
                          "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                          }
                        }
                      },
                      "full": {
                        "type": "text",
                        "fields": {
                          "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                          }
                        }
                      },
                      "last": {
                        "type": "text",
                        "fields": {
                          "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    

    内部对象的索引

    Lucene 不支持内部对象。 Lucene 文档是由一组键值对列表组成的。为了能让 Elasticsearch 有效地索引内部类,它把我们的文档转化成这样:

    {
        "tweet":            [elasticsearch, flexible, very],
        "user.id":          [@johnsmith],
        "user.gender":      [male],
        "user.age":         [26],
        "user.name.full":   [john, smith],
        "user.name.first":  [john],
        "user.name.last":   [smith]
    }
    

    内部域 可以通过名称引用(例如, first )。为了区分同名的两个域,我们可以使用全 路径 (例如, user.name.first ) 或 type 名加路径( tweet.user.name.first )。

    在前面简单扁平的文档中,没有 user 和 user.name 域。Lucene 索引只有标量和简单值,没有复杂数据结构。

    内部对象数组

    假设我们有个 followers 内部对象数组:

    {
        "followers": [
            { "age": 35, "name": "Mary White"},
            { "age": 26, "name": "Alex Jones"},
            { "age": 19, "name": "Lisa Smith"}
        ]
    }
    

    这个文档会像我们之前描述的那样被扁平化处理,结果如下所示:

    {
        "followers.age":    [19, 26, 35],
        "followers.name":   [alex, jones, lisa, smith, mary, white]
    }
    

    但是这里有一个问题,{age: 35}{name: Mary White}之间的相关性已经丢失了,因为每个多值域只是一包无序的值,而不是有序数组。这足以让我们问,“有一个26岁的追随者?”

    但是我们不能得到一个准确的答案:“是否有一个26岁 名字叫 Alex Jones 的追随者?”

    嵌套对象

    考虑到上面内部对象数组的问题,我们看下面的例子:

    由于在 Elasticsearch 中单个文档的增删改都是原子性操作,那么将相关实体数据都存储在同一文档中也就理所当然。 比如说,我们可以将订单及其明细数据存储在一个文档中。又比如,我们可以将一篇博客文章的评论以一个 comments 数组的形式和博客文章放在一起:

    PUT /my_index/blogpost/1
    {
      "title": "Nest eggs",
      "body":  "Making your money work...",
      "tags":  [ "cash", "shares" ],
      "comments": [ 
        {
          "name":    "John Smith",
          "comment": "Great article",
          "age":     28,
          "stars":   4,
          "date":    "2014-09-01"
        },
        {
          "name":    "Alice White",
          "comment": "More like this please",
          "age":     31,
          "stars":   5,
          "date":    "2014-10-22"
        }
      ]
    }
    

    如果我们依赖字段自动映射,那么 comments 字段会自动映射为 object 类型。

    由于所有的信息都在一个文档中,当我们查询时就没有必要去联合文章和评论文档,查询效率就很高。

    但是当我们使用如下查询时,上面的文档也会被当做是符合条件的结果:

    GET /my_index/blogpost/_search
    {
      "query": {
        "bool": {
          "must": [
            { "match": { "name": "Alice" }},
            { "match": { "age":  28      }} 
          ]
        }
      }
    }
    

    Alice实际是31岁,不是28!
    注:
    elasticsearch6之后的版本没有整个问题,被解决了,6之后的查询结果没有命中,结果如下:

    {
      "took": 0,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": 0,
        "max_score": null,
        "hits": []
      }
    }
    

    正如我们在 对象数组 中讨论的一样,出现上面这种问题的原因是 JSON 格式的文档被处理成如下的扁平式键值对的结构。

    {
      "title":            [ eggs, nest ],
      "body":             [ making, money, work, your ],
      "tags":             [ cash, shares ],
      "comments.name":    [ alice, john, smith, white ],
      "comments.comment": [ article, great, like, more, please, this ],
      "comments.age":     [ 28, 31 ],
      "comments.stars":   [ 4, 5 ],
      "comments.date":    [ 2014-09-01, 2014-10-22 ]
    }
    

    Alice 和 31 、 John 和 2014-09-01 之间的相关性信息不再存在。虽然 object 类型 (参见 内部对象) 在存储 单一对象 时非常有用,但对于对象数组的搜索而言,毫无用处。

    嵌套对象 就是来解决这个问题的。将 comments 字段类型设置为 nested 而不是 object 后,每一个嵌套对象都会被索引为一个 隐藏的独立文档 ,举例如下:

    { #第一个 嵌套文档
      "comments.name":    [ john, smith ],
      "comments.comment": [ article, great ],
      "comments.age":     [ 28 ],
      "comments.stars":   [ 4 ],
      "comments.date":    [ 2014-09-01 ]
    }
    { #第二个 嵌套文档
      "comments.name":    [ alice, white ],
      "comments.comment": [ like, more, please, this ],
      "comments.age":     [ 31 ],
      "comments.stars":   [ 5 ],
      "comments.date":    [ 2014-10-22 ]
    }
    { #根文档 或者也可称为父文档
      "title":            [ eggs, nest ],
      "body":             [ making, money, work, your ],
      "tags":             [ cash, shares ]
    }
    

    在独立索引每一个嵌套对象后,对象中每个字段的相关性得以保留。我们查询时,也仅仅返回那些真正符合条件的文档。

    不仅如此,由于嵌套文档直接存储在文档内部,查询时嵌套文档和根文档联合成本很低,速度和单独存储几乎一样。

    嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身。

    嵌套对象映射

    设置一个字段为 nested 很简单 —  你只需要将字段类型 object 替换为 nested 即可:

    
    PUT /my_index
    {
      "mappings": {
        "blogpost": {
          "properties": {
            "comments": {
              "type": "nested", 
              "properties": {
                "name":    { "type": "string"  },
                "comment": { "type": "string"  },
                "age":     { "type": "short"   },
                "stars":   { "type": "short"   },
                "date":    { "type": "date"    }
              }
            }
          }
        }
      }
    }
    
    展开全文
  • COM组件的接口和对象

    万次阅读 2017-02-07 17:21:04
    一、 前言 COM规范中,最基本的两个要素就是对象与接口,因为COM...COM规范的核心内容就是对接口的定义,甚至可以COM中接口就是一切”。组件与组件之间、组件与客户之间都要通过接口进行交互。接口成员函数

    一、 前言
    在COM规范中,最基本的两个要素就是对象与接口,因为COM就是由这两者来共同实现的。COM对象在组件中是被封装起来的,客户代码只能通过接口来访问COM对象并享受其服务,由于客户与COM直接打交道的是COM接口,所以COM接口是COM最关键的要素。COM规范的核心内容就是对接口的定义,甚至可以说“在COM中接口就是一切”。组件与组件之间、组件与客户之间都要通过接口进行交互。接口成员函数将负责为客户或其他组件提供服务。与标识COM对象的CLSID类似,每一个COM接口也使用一个GUID来进行标识,该标识也被称为IID(interface identifier,接口标识符)。

    二、 COM接口
    COM接口通常是一组以函数的逻辑集合,继承IUnKnown接口。COM对象可以提供多个COM接口,每个接口提供不同的服务,因此COM接口与COM对象一样,都是用GUID来标识的,客户通过GUID来获取接口指针,再通过接口指针获取对应的服务。
    COM接口实际限定了组件与使用该组件的客户程序或其他组件所能进行的交互方式,任何一个具备相同接口的组件都可对此组件进行相对于其他组件透明的替换。只要接口不发生变化,就可以在不影响整个由组件构成的系统的情况下自由的更换组件。通常在程序设计阶段需要将接口设计的尽可能完美,以减少在开发阶段对COM接口的更改。尽管如此,在实际应用中是很难做到这一点的,往往需要在现有接口基础上对其做进一步的发展。与C++中对类的继承有些类似,对COM接口的发展也可以通过接口继承来实现。但是COM接口的继承只能是单继承而不允许从多个基接口进行派生,而且派生接口只是继承了对基接口成员函数的说明而没有继承其实现。

    对C++程序员来说,接口定义就是一组纯虚函数,按逻辑捆绑在一起,作为结构体的成员。同时,在C++中,结构体和类几乎是相同的,因此可以这样从一个接口派生另一个接口:

    interface IMath : public IUnknown
    {
    virtual HRESULT __stdcall Add (int a, int b, int* pResult) = 0;
    virtual HRESULT __stdcall Subtract (int a, int b, int* pResult) = 0;
    }
    关键字interface是结构体的别名。
    对于接口,通常是采用抽象基类来定义,并利用类的多重继承来实现该组件。所谓的抽象基类是只包含一个或多个虚函数声明而未包括虚函数的具体实现的类。抽象基类不能被实例化,而只能用作基类使用,并要求其派生类完成其所有虚函数的实现。

    由抽象基类指定的内存结构是符合COM规范的,因此抽象基类IMath可以认为是一个COM接口,但这还不是一个严格意义上的COM接口。对于一个真正意义上的COM接口,在设计时应遵循以下几个规则:
      1) 接口必须直接或间接地从IUnknown继承。
    接口IUnknown的定义:
    可以看出IUnknown实质上就是一个含有纯虚函数的抽象类。
    interface IUnknown{
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) = 0;
    virtual ULONG __stdcall AddRef() = 0;
    virtual ULONG __stdcall Release() = 0;
    };
    2) 接口必须具有唯一的标识(IID)。
    3) 一旦分配和公布了IID,有关接口定义的任何因素都不能被改变。
    4) 接口成员函数应具有HRESULT类型的返回值。
    5) 接口成员函数的字符串参数应采用Unicode类型。
    IUnknown接口是COM的核心,因为所有其他的COM接口都必须从IUnknown继承。它包含三个接口函数:QueryInterface、AddRef和Release,其中QueryInterface用于接口查询,从COM对象的一个接口获得另一个接口,一个对象可能实现了多个接口,这样就可以通过QueryInterface在对象多个接口之间跳转从而获得多个接口提供的服务;AddRef与Release则用于管理COM对象的生命周期,当COM对象不再使用时需要释放,因此COM使用了引用计数的方法来对对象进行管理,当有一个用户获得接口指针后调用AddRef将引用计数加1,相反,当一个用户用完接口指针后就调用Release来使引用计数减1,这样当引用计数为0时,COM对象就可以从内存中释放。由于IUnknown提供了接口查询与生命周期控制两个功能,因此COM的每个接口都应该继承于它。
    COM对象的接口原则
    为了规范COM的接口机制,微软向COM开发者发布了COM对象的接口原则。
    (1)IUnknown接口的等价性
    当我们要等到两个接口指针,我如何判断它们从属于一个对象呢。COM接口原则规定,同一个对象的Queryinterface的IID_IUnknown查询出来的IUnknown指针值应当相等。也就是说,每个对象的IUnknown指是唯一的。我们可以通过判断IUnknown指针是否相等来判断它们是否指向同一个对象。
    IUnknown *pUnknown1 = NULL, *pUnknown2 = NULL;
    pObjectA->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
    pObjectB->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
    if (pUnknown1 == pUnknown2)
    {
    cout << “I am sure ObjectA is ObjectB.”;
    }
    else
    {
    cout << “I am sure ObjectA is not ObjectB.”;
    }

    (2)接口自反性,对一个接口来说,查询它本身应该是允许的。
    设pPsychics是已赋值IPsychics的接口。
    那么pPsychics->QueryInterface(IID_IPsychics,(void **) &XXX);应当成功。
    (3)接口对称性,当我们从一个接口查询到另一个接口时,那么我们再从结果接口还可以查询到原来的接口。
    例如:
    IPsychics *pSrcPsychics = …something, *pTarget = NULL;
    IDynamics *pDynamics = NULL;
    如果pSrcPsychics->QueryInterface(IID_IDynamics,(void **) &pDynamics);成功的话。
    那么pDynamics->QueryInterface(IID_IPsychics,(void **) &pTarget);也相当成功。
    (4)接口传递性。如果我们从第一个接口查询到了第二个接口,又从第二个接口查询到了第三接口。则我们应该能够从第三个接口查询到第一个接口。其它依此类推。
    (5)接口查询时间无关性。当我们在某时查询到一个接口,那么在任意时刻也应该查询到这个接口。

    三、COM对象
    COM对象其实就类似于C++中的对象,也就是说某个类的实例,包含了一组数据和操作。在COM模型中,COM对象的位置对于客户来说是透明的,即客户代码不需要直接初始化一个COM对象,而是COM库通过一个全局标识码GUID去对其进行初始化工作。GUID是一个128位的标识符,基本保证了COM对象的唯一性,另外COM接口也是用GUID来标识
    在开发描述COM对象的C++类时,接口定义的是一组纯虚函数,开发COM类时可以充分利用这一点。例如,可以这样实现IMath的类:
    class CComClass : public IMath
    {
    protected:
    long m_lRef; // Reference count
    public:
    CComClass ();
    virtual ~CComClass ();
    // IUnknown methods
    virtual HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);
    virtual ULONG __stdcall AddRef ();
    virtual ULONG __stdcall Release ();
    // IMath methods
    virtual HRESULT __stdcall Add (int a, int b, int* pResult);
    virtual HRESULT __stdcall Subtract (int a, int b, int* pResult);
    };
    现在,假设要用CComClass实现两个COM接口。可以使用多重继承,从IMath和另一个接口派生CComClass。例如:
    class CComClass : public IMath, public ISpelling
    {
    protected:
    long m_lRef;
    public:
    CComClass ();
    virtual ~CComClass ();
    // IUnknown methods
    virtual HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);
    virtual ULONG __stdcall AddRef ();
    virtual ULONG __stdcall Release ();
    // IMath methods
    virtual HRESULT __stdcall Add (int a, int b, int* pResult);
    virtual HRESULT __stdcall Subtract (int a, int b, int* pResult);
    // ISpelling methods
    virtual HRESULT __stdcall CheckSpelling (wchar_t* pString);
    };

    这种方法有两个优点。首先,它很简单,为了声明一个包含n个接口的类,可以在该类的基类列表中包含n个接口。其次,只需要实现IUnknown一次。如果单独实现每一个接口,就必须为每一个接口实现QueryInterface、AddRef和Release。但是,使用多重继承,所有接口都支持的方法合并到同一个实现。
    使用多重继承编写COM类,更有意思的一点发生在客户调用QueryInterface获取接口指针时。假设用户需要获取IMath指针:
    ppv = (IMath) this;
    如果用户要获取ISpelling指针,则要强制转换为ISpelling*类型:
    ppv = (ISpelling) this;
    如果忽略强制转换,虽然可以正常编译,但是使用时就会发生问题。因为,通过多重继承产生的类会包含多个虚表和多个虚表指针,如果不进行强制类型转换,就无法知道this指针引用哪个虚表。换句话说,上面两个语句虽然使用相同的this指针,但是返回不同的值。如果客户需要一个ISpelling指针,却返回一个纯this指针,而this刚好指向IMath的虚表,客户就会使用IMath虚表调用ISpelling方法。因此,使用多重继承的COM类要进行强制转换,获取正确的虚表指针。

    4.嵌套类
    如果两个接口的方法没有使用相同的名字和标记,使用多重继承就不会有什么问题。如果IMath和ISpelling都包含Init方法,它们的参数列表相同,但是需要单独实现,这时,就不能使用多重继承定义一个类实现它们。因为,使用多重继承时,成员函数的名字必须是唯一的。
    由于这个限制,MFC使用嵌套类实现COM接口。可以在一个C++类中实现COM接口的任意组合,与接口的特性无关。
    假设CComClass实现IMath和ISpelling,并且它们都包含一个名为Init的方法,不含参数:
    virtual HRESULT __stdcall Init () = 0;
    这时不能用多重继承,因为C++不支持在一个类中有两个相同的函数。可以定义两个子类,每个实现一个接口:
    class CMath : public IMath
    {
    protected:
    CComClass* m_pParent; // Back pointer to parent
    public:
    CMath ();
    virtual ~CMath ();
    // IUnknown methods
    virtual HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);
    virtual ULONG __stdcall AddRef ();
    virtual ULONG __stdcall Release ();
    // IMath methods
    virtual HRESULT __stdcall Add (int a, int b, int* pResult);
    virtual HRESULT __stdcall Subtract (int a, int b, int* pResult);
    virtual HRESULT __stdcall Init () = 0;
    };
    class CSpelling : public ISpelling
    {
    protected:
    CComClass* m_pParent; // Back pointer to parent
    public:
    CSpelling ();
    virtual ~CSpelling ();
    // IUnknown methods
    virtual HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);
    virtual ULONG __stdcall AddRef ();
    virtual ULONG __stdcall Release ();
    // ISpelling methods
    virtual HRESULT __stdcall CheckSpelling (wchar_t* pString);
    virtual HRESULT __stdcall Init () =0;
    };
    为了创建CMath和CSpelling嵌套类,先在CComClass内部声明它们。然后,在CComClass内包含一对CMath和CSpelling对象:
    class CComClass : public IUnknown
    {
    protected:
    long m_lRef; // Reference count
    class CMath : public IMath
    {
    […]
    };
    CMath m_objMath;
    class CSpelling : public ISpelling
    {
    […]
    };
    CSpelling m_objSpell;
    public:
    CComClass ();
    virtual ~CComClass ();
    // IUnknown methods
    virtual HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);
    virtual ULONG __stdcall AddRef ();
    virtual ULONG __stdcall Release ();
    };
    CComClass只派生于IUnknown。如果用户调用QueryInterface获取IMath指针,CComClass就传递一个指向CMath对象的指针:
    ppv = (IMath) &m_objMath;
    如果要获取ISpelling指针,CComClass就返回一个指向m_objSpell的指针:
    ppv = (ISpelling) &m_objSpell;
    理解嵌套类方法的关键是,子对象必须把对它们IUnknown方法的调用委派给父类中等效的方法。注意到,每一个嵌套类中没有引用计数,取而代之的是CComClass指针。该指针是一个反向指针,指向父对象。通过该反向指针调用CComClass中的IUnknown实现委派操作。通常,父类的构造函数这样初始化反向指针:
    CComClass::CComClass ()
    {
    […] // Normal initialization stuff goes here.
    m_objMath.m_nParent = this;
    m_objSpell.m_pParent = this;
    }
    嵌套类的IUnknown的实现如下:
    HRESULT __stdcall CComClass::CMath::QueryInterface (REFIID riid, void** ppv)
    {
    return m_pParent->QueryInterface (riid, ppv);
    }
    ULONG __stdcall CComClass::CMath::AddRef ()
    {
    return m_pParent->AddRef ();
    }
    ULONG __stdcall CComClass::CMath::Release ()
    {
    return m_pParent->Release ();
    }
    这种类型的委派是必要的。如果客户在一个子对象实现的接口中调用AddRef或者Release,会修改父对象的引用计数,而不是子对象的引用计数。其次,如果客户调用子对象中的QueryInterface,父对象就必须接管调用,因为只有父对象知道存在哪个嵌套类,以及该嵌套类实现了哪个接口。

    展开全文
  • json数组对象和对象数组 一、Json的简单介绍 从结构上看,所有的数据最终都可以分成三种类型: 第一种类型是scalar(标量),也就是一个单独的string(字符串)或数字(numbers),比如“北京”这个单独的词。 ...
    
    

    一、Json的简单介绍
    从结构上看,所有的数据最终都可以分成三种类型:
    第一种类型是scalar(标量),也就是一个单独的string(字符串)或数字(numbers),比如北京这个单独的词。
    第二种类型是sequence(序列),也就是若干个相关的数据按照一定顺序并列在一起,又叫做array(数组)或List(列表),比如北京,东京
    第三种类型是mapping(映射),也就是一个名/值对(Name/value),即数据有一个名称,还有一个与之相对应的值,这又称作hash(散列)或dictionary(字典),比如首都:北京
    JSON(JavaScript Object Notation)
    是一种轻量级的数据交换格式,它的规则非常简单并且是有趣的:
    1
    并列的数据之间用逗号()分隔。
    2
    映射用冒号()表示。
    3
    并列数据的集合(数组)用方括号("[]")表示。
    4
    映射的集合(对象)用大括号(“{}”)表示。
    按照这个规则可以作以下理解:
    1.
    数组用“[]”创建,对象用“{}”创建,并且使用Json基本都是用[]或者{}创建的数组或对象,否则一个普通的字符串是没有意义的;
    2.
    无论是数组还是对象,之间的元素都用隔开;
    3.
    对象内部,(属性的)名称和值用隔开,并且必须要用隔开,不可单独存在属性名或者值;
    4.
    对象和数组可以互相嵌套,即数组中的一个元素可以是一个对象也可以是一个数组,同理对象中的一个属性的值可以是一个对象也可以是一个数组。
    二、事例
    1.
    var china= {beijing:{name:"
    北京",area:"16000",haidian:{name:"海淀区"}},
    shanghai:{name:"
    上海",area:"10000",minhang:{name:"闵行区"}}};
    alert(china.beijing.haidian.name);
    alert(china.shanghai.minhang.name);
    分别弹出海淀区闵行区
    2.
    var ourcountry=[["
    北京市"],["上海市"],["合肥市","芜湖市","蚌埠市"]];
    alert(ourcountry[2][1]);
    弹出芜湖市
    3.
    var zhongguo={provinces:[{name:"
    北京",cities:[{name:"北京市",quxian:["海淀区","朝阳区","东城区","西城区"]}]},
    {name:"
    安徽省",cities:[{name:"芜湖市",quxian:["繁昌县","芜湖县","南陵县","三山区"]},{name:"合肥市",quxian:["肥西县","蜀山区","庐阳区"]}]},
    "
    湖北省"
    ]};
    var str = "
    中国:{\n";
    for(var i = 0; i < zhongguo.provinces.length; i++)
    {
    if(zhongguo.provinces.cities != null)
    {
    str += zhongguo.provinces.name + "{";
    for(var j = 0; j < zhongguo.provinces.cities.length; j++)
    {
    if(zhongguo.provinces.cities[j] != null)
    {
    str += zhongguo.provinces.cities[j].name + "{";
    for(var k = 0; k < zhongguo.provinces.cities[j].quxian.length; k++)
    {
    str += zhongguo.provinces.cities[j].quxian[k];
    if(k != zhongguo.provinces.cities[j].quxian.length - 1)
    {
    str += ",";
    }
    }
    str += "}";
    }
    }
    str += "}\n";
    }
    }
    str += "}";
    alert(str);
    弹出
    中国:{
    北京{北京市{海淀区,朝阳区,东城区,西城区}}
    安徽省{芜湖市{繁昌县,芜湖县,南陵县,三山区}合肥市{肥西县,蜀山区,庐阳区}}
    }

    三、JsonAjax中的应用
    客户端可以给服务器端通过地址栏或者post很容易的提交数据,但是服务器端处理完数据之后,将计算的结果信息回传给客户端时就存在了一定的难度,特别是数据量较大时。这个时候数据的格式成了关键,按照某种格式可以很方便的进行数据的组装,然后可以很方便的进行解析。使用Json便是一种很好的策略。在服务器端,按照Json的格式拼装好一个字符串,响应给客户端。客户端如何进行解析呢?一般有两种策略(两种策略的名称是自己给的名字,不一定很合理,但是思路应该是没有问题的):
    1.
    直接解析
    var json = eval('(' + result + ')');
    通过上面这个表达式,就完成了将服务器端响应给客户端的Json格式的字符串解析成了一个Json(格式的)对象,名称为“json”,通过“json.”或者“json[]”的方式便可进行数据访问。
    2.
    间接解析
    var json = "r=" + result;
    eval(json);
    当然上面行代码可以合并为:eval("r=" + result);
    通过上面的计算,也可以将服务器端响应给客户端的Json格式的字符串解析成了一个Json(格式的)对象,但是该对象名称为“r”,通过“r.”或者“r[]”的方式可进行数据访问。
    总结:Json是一种简单的数据交换格式,它几乎可以很好的代替xml让服务器之间灵活的交换数据。
    四、JavaScript中的数组和对象
    JavaScript中,通常用[]创建的数据格式称为数组,用{}创建的东西称为对象。
    有一个数组a=[1,2,3,4],还有一个对象a={0:1,1:2,2:3,3:4},运行alert(a[1]),两种情况下的运行结果是相同的!这就是说,数据集合既可以用数组表示,也可以用对象表示,那么到底该用哪一种呢?
    其实数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象。
    当然,数组和对象的另一个区别是,数组中的数据没有名称name),对象中的数据有名称name)。但是问题是,很多编程语言中,都有一种叫关联数组associativearray)的东西。这种数组中的数据是有名称的。比如在javascript中,可以这样定义一个对象:
    var a={"
    城市":"北京","面积":16800,有趣,"人口":1600};
    但是,也可以定义成一个关联数组:
    var a = new Array();
    a["
    城市"]="北京";
    a["
    面积"]=16800;
    a["
    人口"]=1600;
    这样一来好像数组和集合就没有区别了,其实不是,在Javascript语言中,关联数组就是对象,对象就是关联数组。通过第二种方式创建的数组和通过[]方式创建的数组是有很大的区别的,在第二个方式创建的数组中,也可以和第一种方式类似,通过“a.城市来得到北京“a.人口来得到“1600”,但是它和第一种方式创建的对象又是有区别的,通过第一种方式定义的a是没有length属性的,二通过第二种方式定义的a有,可是值为0可见里面的差别还是有的,要想具体搞清楚,恐怕得看看底层的一些实现代码了。

    eval 返回的 json 值时,提示 Error: Invalid Label摘要: 我们经常会在服务器端返回 json 格式的值,这样可以直接在脚本中当作完整的对象来使用,但是,许多新手通常都会遇到一个错误提示:Invalid Label,这个问题通常会让人苦恼不堪,因为明明正确的 json格式,却提示错误。如果你遇到此问题,那赶快看看本文的内容吧!

    我们经常会在服务器端返回 json格式的值,这样可以直接在脚本中当作完整的对象来使用,但是,许多新手通常都会遇到一个错误提示: Invalid Label,这个问题通常会让人苦恼不堪,因为明明正确的 json格式,却提示错误。


    假如你从服务器端返回的是 json格式的字符串:
    >
    当你在脚本中用 eval的方式运行:
    >
    这时会提示 Error: Invalid Label
    实际上,我们的 json格式并没有错误,只是在 eval的时候,要把你的 json值用 “()” 括号括起来:
    > var result = eval("(" + o.responseText + ")"); help001

     

    展开全文
  • JSON数组对象和对象数组

    千次阅读 2017-11-22 09:59:07
    Json的简单介绍 从结构上看,所有的数据最终都可以...第二种类型是sequence(序列),也就是若干个相关的数据按照一定顺序并列在一起,又叫做array(数组)或List(列表),比如“北京,东京”。 第三种类型是mapping(映射
  • json数组对象和对象数组

    万次阅读 2013-03-04 21:35:12
    一、Json的简单介绍  ...第二种类型是sequence(序列),也就是若干个相关的数据按照一定顺序并列在一起,又叫做array(数组)或List(列表),比如“北京,东京”。  第三种类型是mapping(映射),
  • const对象什么可以头文件中定义

    千次阅读 2014-06-15 10:13:53
    首先明确两点: const对象默认为文件的局部变量。《C++ Primer 4》p86头文件用于声明而不是用于定义。《C++ Primer 4》p100,
  • 深入理解Java类型信息(Class对象)与反射机制

    万次阅读 多人点赞 2017-05-01 23:19:19
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ...深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解
  • 第六部分:SQL 和对象关系映射 原文:Part VI: SQL and Object Relational Mapping 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 本书的这一部分中,我们将介绍一些内容,它们与本书其余部分的...
  • 深入理解Python中的面向对象

    万次阅读 多人点赞 2017-06-12 06:42:41
    1、面向过程与面向对象的对比面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。 优点是:极大的降低了程序的
  • ES嵌套对象和父子文档

    千次阅读 2019-09-19 10:23:37
    嵌套对象 由于 Elasticsearch 中单个文档的增删改都是原子性操作,那么将相关实体数据都存储同一文档中也就...又比如,我们可以将一篇博客文章的评论以一个comments数组的形式博客文章放在一起: PUT /...
  • java对象在内存中的分配

    万次阅读 多人点赞 2017-02-10 22:45:33
    堆内存中存放的是new出的对象,new出的对象只包含成员变量。  栈内存中:存放的是局部成员变量。对于基本的数据类型存放的是基本变量的值,而对于对象变量,存放的是堆内存的地址。  静态、常量区:存放的是静态...
  • 1、JAVA 对象布局1.1对象头(Header):1.2实例数据(Instance Data)1.3对齐填充(Padding)2、Java数据类型有哪些2.1基础数据类型内存占用如下2.2引用类型 内存占用如下:2.3字段重排序3、验证3.1有一个Fruit类...
  • C++面向对象基础

    万次阅读 多人点赞 2018-05-20 12:40:59
    面向对象基础面向对象三大特性封装性:数据代码捆绑在一起,避免外界干扰不确定性访问。封装可以使得代码模块化。优点:确保用户代码不会无意间破坏封装对象的状态被封装的类的具体实现细节可以随时改变,而无须...
  • js:面向对象编程,带你认识封装、继承多态

    万次阅读 多人点赞 2017-06-05 22:59:25
    先上一张图,可以对面向对象有一个大致的了解,然而什么是面向对象呢,用java中的一句经典语句来就是:万事万物皆对象。 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完
  • 本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用。
  • 面向对象的入门课程C++(或者JAVA)中,封装数据方法好像是面向对象最重要的一个特点,当然还有基于继承实现的多态重载。其实每一种OOP语言,由于彼此功能上的差异性,这些特点只能适用于某一种特定的...
  • 如何理解模块、组件和对象

    千次阅读 2018-01-15 15:42:15
    **模块化开发的最大价值是分治、分治、分治! 模块化能分离职责,从而达到分治!...模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码数据结构的集合体),或指大型软件系统的
  • 多态,大概每个人都知道。但是,又有几个人真的理解什么是多态、多态有哪些细节呢?如果你看到这篇文章的名字,脑海中对多态没有一个清晰的概念,不妨点进来看看,也许会有收获。
  • 本文将对Java程序设计的对象和类进行深入详细介绍,主要涉及以下内容: - 面向对象程序设计 - 如何创建标准Java类库中的类对象 - 如何编写自己的类
  • 设计模式之面向对象七大基本原则

    万次阅读 2015-04-27 16:25:48
    概述运用面向对象的思想进行软件设计时,需要遵循的原则一共有7个,他们是:1. 单一职责原则(Single Responsibility Principle)每一个类应该专注于做一件事情。2. 里氏替换原则(Liskov Substitution Principle...
  • Java类和对象 详解(一)(转载)

    千次阅读 2018-07-04 10:44:26
    一、面向对象简述面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计...
  • 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物整个解决问题的步骤中的行为。  ----------------------------------------------- 面向过程的思维方式...
  • [摘要]本文介绍了Excel对象模型及相关知识的基本概念,告诉您如何VBA代码中使用与处理对象和集合。主要包括Excel对象模型的介绍,对象(集合)的概念如何引用,设置对象变量,对象的方法属性,对象的事件,用...
  • 认识JVM--第二篇-java对象内存模型

    千次阅读 2011-07-03 23:57:14
    前一段写了一篇《认识JVM》,不过一些方面可以继续阐述的,这里继续探讨一下,本文重点在于heap区域内部对象之间的组织关系,以及各种粒度之间的关系,以及JVM常见优化方法,文章目录如下所示: 1、回顾--...
  • 2.类和对象 3. 成员变量局部变量区别 4. 数据类型 5.构造函数 6.函数 7. 成员变量的初始化 8. this关键字 9. static关键字 9.1 静态成员变量、静态成员变量初始化 9.2静态成员方法 10. JVM对象内存结构 ...
  • 游戏中的对象资源信息管理

    千次阅读 2010-07-12 11:49:00
    游戏中的对象资源信息管理 首先要说明的是资源...一个很好的例子就是模型信息文件,一个模型信息文件中,存放了整个物体的所有部分,每一个部分又是单独的信息。这样做的目的在于游戏中的换装。...
  • 基于Java基础-面向对象实现植物大战僵尸简易版

    万次阅读 多人点赞 2019-03-31 15:15:44
    从零开始学习的Java的第一个月,面向对象(OOP)的学习中,跟着讲师完成了飞机大战的游戏编码。第二个月开始接触API,心血来潮便结合API中的集合、多线程开始植物大战僵尸的编码,从游戏模式的设计到游戏内容的实现...
  • Java面向对象的四个特征

    千次阅读 2018-07-01 08:56:29
    Java面向对象的几个特征分别为...把对象的全部属性全部服务结合在一起,形成不可分割的独立的单位(对象);信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者形成一道屏障〕,只保留有限的对外接...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 233,663
精华内容 93,465
关键字:

和对象单独在一起说什么