精华内容
下载资源
问答
  • kotlin 定义对象数组_Kotlin基本数组和对象数组
    千次阅读
    2020-04-26 20:45:52

    kotlin 定义对象数组

    我最初打算写这篇文章是因为我在玩一些反射代码,并认为自己发现了一些有趣的东西。 las,绝对不是这样。 相反,它只是Kotlin的基本功能,我不需要使用或关注。 尽管这篇文章并没有表明我希望的样子,但我仍然认为这是一篇很好的小文章,可以使这个主题更加清晰。

    在Java中,存在基本类型及其包装版本的概念。 由于自动装箱和拆箱,类型可以在其原始版本和包装版本之间互换。 换句话说,在大多数情况下,您可以使用long代替Long或使用Long代替long 。 如果您没有注意到最后一句中的大写字母在哪里,那么我想它可能看起来很混乱。 该句中的措辞也很关键。 更具体地说,声明“在大多数情况下”。

    尝试互换原始数组和包装的( Object )数组时,自动装箱和拆箱不起作用。 例如,

     public class PrimitiveArrays { 
       public static void main(String args[]) { 
         Long[] longArray = new Long[] {1L, 2L, 3L}; 
         takesInPrimitiveLongArray(longArray); 
       } 
       static void takesInPrimitiveLongArray( long [] array) {}  } 

    这不起作用,尝试对其进行编译会产生以下错误:

     error: incompatible types: Long[] cannot be converted to long [] 
         takesInPrimitiveLongArray(longArray); 

    出于相同的原因,将方法切换为采用Long[]并传入long[]也会导致编译失败。 这并不是大多数Java开发人员都会感兴趣的东西,但有助于为本文的实际内容奠定基础。

    Kotlin需要为您提供Java原始数组的等效功能。 但是,Kotlin不允许您使用与Java相同的语法来定义数组。 在Kotlin中,初始化数组如下所示:

     val array = Array<Long>( 3 )  // or  val array: Array<Long> = arrayOf( 1 , 2 , 3 ) 

    您可以看到Array使用泛型这一事实应突出表明它不是原始数组。 在Java和Kotlin中,这都是事实,即泛型类型不能是基元。 否则,可以将其切换为Array<long> ,我们都会很高兴。 上面的代码向下编译为Long[]的对象数组,而不是原始的long[]

    这种情况在阵列上是很独特的。 单独使用的Kotlin Long可以编译为JVM字节码中的Longlong 。 编译的类型取决于字段的可为空性。 数组更明确,因此编译时它们的类型不会改变。

    为了避免这种情况,Kotlin提供了一些选择,这些类在向下编译为JVM字节码时变为原始数组。

    这些类包括:

    Kotlin Java
    字节数组 字节[]
    字符数组 字符[]
    短数组 短[]
    整数数组 int []
    长数组 长[]
    双数组 双[]
    FloatArray 浮动[]
    布尔数组 布尔值[]

    对于无符号类型的数组,还有其他类。

    这些类也可以在Kotlin和Java之间互换,而无需任何额外的努力。

    作为最后的证据向您展示Kotlin中原始数组与包装式/对象数组之间的区别,我想向您展示一些Kotlin代码,这些代码已转换为Java对应的代码:

    字节数组

    使用Intellij的Kotlin字节码反编译器,该代码段反编译为:

    字节数组

    首先,请注意,Kotlin为您的阵列提供了有用的初始化功能。 对于原始数组和对象数组。 其次,它们是如何编译的。 例如, LongArray变为long[]Array<Long>变为Long[]

    现在,您可以看到这些数组之间的差异。 但是,我没有提到您应该利用哪个。 您应该像Java一样使用基本类型。 这是由于自动装箱和拆箱会对应用程序产生性能影响。

    对于较小的工作负载,结果可能微不足道。 另一方面,对于性能至关重要的应用中的较大阵列,此可能很小的更改可能会产生明显的影响。 有关此主题的更多信息,请参见此处

    如果需要在数组中存储空值,则仍然需要引用回包装/对象数组。 在大多数情况下,我认为您应该能够使用原始数组,但是总会有很多时候无法使用原始数组。 话虽这么说,大多数时候我们都只使用List ,所以这些都不重要。

    现在,您应该对LongArray类的原始数组与Array<Long>类的对象数组之间的区别有了更好的了解。 如果没有,那么我让你失败了,对此我表示歉意。

    翻译自: https://www.javacodegeeks.com/2019/07/kotlin-primitive-object-arrays.html

    kotlin 定义对象数组

    更多相关内容
  • 深入剖析kubernetes的API对象类型定义

    万次阅读 2019-10-14 22:46:07
    其实kubernetes绝大部分API对象的类型都是这个结构,他们都继承metav1.TypeMeta和metav1.ObjectMeta,前者用于定义类型的属性,后者用于定义对象的公共属性;Spec用于定义API对象类型的私有属性,也是API对象之间的...

    目录

     

    1.背景

    2.分析

    2.1api

    2.2metav1

    2.2.1MetaType

    2.2.3ListMeta

    2.3runtime

    2.3.1schema

    2.3.2Object

    3.总结


    1.背景

    在初学kuberentes的时候,笔者作为码农就非常好奇它的代码实现。于是开始git clone代码,首先就被kuberentes的代码仓库搞蒙了,kuberentes项目有好多仓库,包括kubernetes、client-go、api、apimachinery...,我该从哪儿看起?索性就从kubernetes的API对象的定义开始看吧,跟api有关的仓库有两个api、apimachinery,同时在apimachinery仓库中还有api、apis两个包,貌似他们都有types.go、meta.go、interface.go。此时笔者的心情真是难以言表,但是好奇心又怂恿自己弄明白原理,于是也就有了这篇类似总结的文章。希望本文能够帮助读者初步了解kubernetes代码,成为一篇比较基础的入门文章。

    2.分析

    以前,在笔者印象中API都是各种接口,比较典型的就是操作系统的API。一提到API默认就是函数定义,直到REST API流行起来,API的虽然形式上不再是函数,但是原理上是一样的,只是接口的服务端从本地变成了远程。其实笔者一直都忽略了API的另一个重点,那就是接口类型,也就是接口函数的参数类型,它也是API的一部分。笔者认为:操作系统的API强调方法,为方法设计参数类型居多,而Rest API的强调资源,而方法就那么几个。在了解一个系统的Rest API时首先就要看有哪些资源,这些资源如何定义,支持哪些操作等等。

    kubernetes对外提供的就是Rest API,只是这部分被client-go这个项目封装成了类似SDK的形态。而apisever提供的就是基于API对象的各种操作,本文目标是探究一下API对象在kubernetes内部是如何定义以及实现的。

    2.1api

    在kubernetes里提供了非常多的API对象,它们被定义在k8s.io/api这个仓库中,这也是本章节命名为api的原因。Pod应该是最为基础的对象之一,在初学kubernetes时我相信大部分同学都写过类似下面的代码:

    apiVersion: v1
    kind: Pod
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

    通过命令kubectl create -f xxx.yaml在kubernetes中创建了一个名字为myapp-pod的Pod对象(此处忽略namespace)。

    用编程的角度分析上面的流程:在kubernetes中需要有一个Pod的类型,每次执行kubectl create -f xxx.yaml创建Pod对象的时候需要实例化Pod,并把xxx.yaml中的参数赋值到Pod对象中。

    现在就来看看kubernetes中Pod类型是如何定义的:

    // 代码源自k8s.io/api/core/v1/types.go
    // kubernetes的API对象是单独的git仓库(https://github.com/kubernetes/api.git),可见API对象
    // 在kubernetes项目中的重要程度。
    type Pod struct {
        // metav1是"k8s.io/apimachinery/pkg/apis/meta/v1"的重命名。额...,apimachinery又是
        // 什么鬼?apis包又是干啥的?起初笔者被这些名字搞得云里雾里,但所有的这些迷雾都会在本文揭开,此处
        // 读者只需要知道这个是类型的meta。那什么又是类型的meta呢?以普通类型int32为例,类型名称
        // “int32”、类型占用内存空间4字节就是类型的meta,简单的说就是类型属性的描述。而kubernetes的
        // API对象的类型meta是如何定义的,后面会有单独章节详细说明。
        metav1.TypeMeta `json:",inline"`
        // 同样是metav1包,这回是对象的meta。同样以int32为例,对象的地址就属于对象meta。在kubernetes
        // 中,API对象都有自己的属性,并且他们都有相同的属性,例如名字、命名空间、标签等等。kuberentes把
        // 这些公共的属性提取出来就是metav1.ObjectMeta,成为了API对象类型的父类。
        metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
        // 从名字上看还是比较好理解的,就是Pod规格,最为代表性的就是CPU、内存的资源使用。它和xxx.yaml中
        // spec是关联的。PodSpec不作为本文的说明重点,所以不再详细解释。
        Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
        // Pod的状态,比如是运行还是挂起、Pod的IP、启动时间等等。
        Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
    }

    从Pod的定义来看,它继承了metav1.TypeMeta和metav1.ObjectMeta两个类型,同时还定义了Spec和Status两个成员变量。其实kubernetes绝大部分API对象的类型都是这个结构,他们都继承metav1.TypeMeta和metav1.ObjectMeta,前者用于定义类型的属性,后者用于定义对象的公共属性;Spec用于定义API对象类型的私有属性,也是API对象之间的区别所在(例如Pod和Deployment虽然都继承了两个父类,但是他们二者的区别就是通过Spec实现的。就像是同一对父母的两个孩子,有像的地方,更多的还是不像的地方让他们成为了两个独立的个体,这就是继承的魅力所在);Status则是用于描述每个对象的状态的,这和每个对象的类型紧密相关的。细心的读者不难发现,metav1.TypeMeta和metav1.ObjectMeta对应的是xxx.yaml中的kind、apiVersion和metadata字段,Spec对应xxx.yaml中spec字段。这一点在代码注释`json:...`可以证实,这里也可以得出另一个结论,那就是xxx.yaml就是Pod类型yaml的序列化。所以,kubectl create -f xxx.yaml就等同于new(Pod)。

    此处要对metav1.TypeMeta和metav1.ObjectMeta多说两句,可以把他们两个看做是kubernetes全部API对象的基类,类似java中的Object类。语言因为有编译器的存在,类似metav1.TypeMeta的东西被编译屏蔽了,所以开发者看到的所有的类继承于Object。但在kubernetes中,每个API对象都需要metav1.TypeMeta字段用于描述自己是什么类型,这样才能构造相应类型的对象,所以相同类型的所有对象的metav1.TypeMeta字段都是相同的。但是metav1.ObjectMeta则不同,它是定义对象的公共属性,即所有对象都应该具备的属性。这部分就是和对象本身相关,和类型无关,所以相同类型的所有对象的metav1.ObjectMeta可能是不同的。

    在kubernetes的API对象中除了单体对象外,还有对象列表类型,用于描述一组对象,等同于golang中的slice。对象列表的典型应用场景就是列举,对象列表就可以表达一组对象。可能有些读者会问为什么不用对象的slice,例如[]Pod,伴随着笔者对对象列表的解释读者就会理解,此处以PodList为例进行分析:

    // 代码源自k8s.io/api/core/v1/types.go
    type PodList struct {
        // PodList同样需要继承metav1.TypeMeta,毕竟对象列表也好、单体对象也好都需要类型属性。
        // PodList比[]Pod类型在yaml或者json表达上多了类型描述,当需要根据yaml构建对象列表的时候,
        // 就可以根据类型描述反序列成为PodList。而[]Pod则不可以,他必须确保yaml就是[]Pod序列化的
        // 结果,否则就会报错。这就无法实现一个通用的对象序列化/反序列化。
        metav1.TypeMeta `json:",inline"`
        // 与Pod不同,PodList继承了metav1.ListMeta,metav1.ListMeta是所有对象类表类型的父类,
        // 他定义了所有列表类型公共属性。
        metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
        // Items就是PodList定义的本质了,其实就是Pod的slice。说白了PodList就是[]Pod基础上加了一些
        // 跟类型和类表相关的信息,这些信息的作用会在后面的章节做详细解释。
        Items []Pod `json:"items" protobuf:"bytes,2,rep,name=items"`
    }

    前面已经解释了Pod的定义,PodList就不多解释了。此处做一个小结:

    1. metav1.TypeMeta和metav1.ObjectMeta是所有API单体对象的父类;
    2. metav1.TypeMeta和metav1.ListMeta是所有API列表对象的父类;
    3. metav1.TypeMeta才是所有API对象的父类,这也很好理解,毕竟所有的对象都要说明自己是什么类型;

    2.2metav1

    metav1是k8s.io/apimachinery/pkg/apis/meta/v1的缩写,后文会简称为metav1。

    2.2.1MetaType

    作为所有API对象的父类,是时候揭开它的真面目了:

    // 代码源自:k8s.io/apimachinery/pkg/apis/meta/v1/types.go
    // 同样来自apimachinery仓库,ObjectMeta是xxx.yaml中metadata字段,平时我们填写的metadata
    // 一般只有name、label,其实ObjectMeta字段还是有很多内容了,让笔者逐一介绍一下。
    type ObjectMeta struct {
        // 对象的名字应该不用介绍了。
        Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
        // 如果Name为空,系统这为该对象生成一个唯一的名字。
        GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
        // 命名空间,在平时学习、调试的时候很少用,但是在发布的时候会经常用。
        Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
        // 对象的URL,由系统生成。
        SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`
        // 对象的唯一ID,由系统生成。
        UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
        // 资源版本,这是一个非常有意思且变量,版本可以理解为对象在时间轴上的一个时间节点,代表着对象最后
        // 一次更新的时刻。如果说Name是在Namespace空间下唯一,那么ResourceVersion则是同名、同类型
        // 对象时间下唯一。因为同名对象在不同时间可能会更新、删除再添加,在比较两个对象谁比较新的情况
        // 非常有用,比如Watch。
        ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`
        // 笔者很少关注这个值,所以也不太了解是干什么的,读者感兴趣可以自己了解一下。
        Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`
        // 对象创建时间,由系统生成。
        CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`
        // 对象删除时间,指针类型说明是可选的,当指针不为空的时候说明对象被删除了,也是由系统生成
        DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`
        // 对象被删除前允许优雅结束的时间,单位为秒。
        DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`
        // 对象标签,这个是我们经常用的,不用多解释了
        Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
        // 批注,这个和标签很像,但是用法不同,比如可以用来做配置。
        Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
        // 该对象依赖的对象类表,如果这些依赖对象全部被删除了,那么该对象也会被回收。如果该对象对象被
        // 某一controller管理,那么类表中有一条就是指向这个controller的。例如Deployment对象的
        // OwnerReferences有一条就是指向DeploymentController的。
        OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`
        // 下面这几个变量笔者没有了解过,等笔者知道了再来更新文章吧。
        Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
        ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
        ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
    }
    
    // 代码源自k8s.io/apimachinery/pkg/apis/meta/v1/meta.go,GetObjectMeta是MetaAccessor
    // 的接口函数,这个函数说明了ObjectMeta实现了MetaAccessor。
    func (obj *ObjectMeta) GetObjectMeta() Object { return obj }
    
    // 下面所有的函数是接口Object的ObjectMeta实现,可以看出来基本就是setter/getter方法。纳尼?
    // 又一个Object(这个Object是metav1.Object,本章节简写为Object)?Object是API对象公共属性
    // (meta信息)的抽象,下面的函数是Object所有函数的实现,因为功能比较简单,笔者就不一一注释了。
    func (meta *ObjectMeta) GetNamespace() string                { return meta.Namespace }
    func (meta *ObjectMeta) SetNamespace(namespace string)       { meta.Namespace = namespace }
    func (meta *ObjectMeta) GetName() string                     { return meta.Name }
    func (meta *ObjectMeta) SetName(name string)                 { meta.Name = name }
    func (meta *ObjectMeta) GetGenerateName() string             { return meta.GenerateName }
    func (meta *ObjectMeta) SetGenerateName(generateName string) { meta.GenerateName = generateName }
    func (meta *ObjectMeta) GetUID() types.UID                   { return meta.UID }
    func (meta *ObjectMeta) SetUID(uid types.UID)                { meta.UID = uid }
    func (meta *ObjectMeta) GetResourceVersion() string          { return meta.ResourceVersion }
    func (meta *ObjectMeta) SetResourceVersion(version string)   { meta.ResourceVersion = version }
    func (meta *ObjectMeta) GetGeneration() int64                { return meta.Generation }
    func (meta *ObjectMeta) SetGeneration(generation int64)      { meta.Generation = generation }
    func (meta *ObjectMeta) GetSelfLink() string                 { return meta.SelfLink }
    func (meta *ObjectMeta) SetSelfLink(selfLink string)         { meta.SelfLink = selfLink }
    func (meta *ObjectMeta) GetCreationTimestamp() Time          { return meta.CreationTimestamp }
    func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp Time) {
        meta.CreationTimestamp = creationTimestamp
    }
    func (meta *ObjectMeta) GetDeletionTimestamp() *Time { return meta.DeletionTimestamp }
    func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *Time) {
        meta.DeletionTimestamp = deletionTimestamp
    }
    func (meta *ObjectMeta) GetDeletionGracePeriodSeconds() *int64 { return meta.DeletionGracePeriodSeconds }
    func (meta *ObjectMeta) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
        meta.DeletionGracePeriodSeconds = deletionGracePeriodSeconds
    }
    func (meta *ObjectMeta) GetLabels() map[string]string                 { return meta.Labels }
    func (meta *ObjectMeta) SetLabels(labels map[string]string)           { meta.Labels = labels }
    func (meta *ObjectMeta) GetAnnotations() map[string]string            { return meta.Annotations }
    func (meta *ObjectMeta) SetAnnotations(annotations map[string]string) { meta.Annotations = annotations }
    func (meta *ObjectMeta) GetFinalizers() []string                      { return meta.Finalizers }
    func (meta *ObjectMeta) SetFinalizers(finalizers []string)            { meta.Finalizers = finalizers }
    func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference         { return meta.OwnerReferences }
    func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
        meta.OwnerReferences = references
    }
    func (meta *ObjectMeta) GetClusterName() string                 { return meta.ClusterName }
    func (meta *ObjectMeta) SetClusterName(clusterName string)      { meta.ClusterName = clusterName }
    func (meta *ObjectMeta) GetManagedFields() []ManagedFieldsEntry { return meta.ManagedFields }
    func (meta *ObjectMeta) SetManagedFields(managedFields []ManagedFieldsEntry) {
        meta.ManagedFields = managedFields
    }

    ObjectMeta每个成员变量是干什么用的对于理解本文并没有什么帮助,对于读者来说只需要知道这些属性是所有API对象都有的公共属性。所以笔者对于每个成员变量的注释也仅仅停留在表面定义,并没有做深入的解释。

    此时,可以得出一个结论:ObjectMeta实现了Object和MetaAccessor两个interface,而kubernetes所有单体对象都继承了ObjectMeta,那么所有的API对象就都实现了Object和MetaAccessor。kubernetes中有很多地方访问API对象的这些meta信息并且不区分对象类型,Object是一个不错的选择。

    2.2.3ListMeta

    和ObjectMeta功能类似,ListMeta定义了所有列表对象的公共属性:

    // 代码源自:k8s.io/apimachinery/pkg/apis/meta/v1/types.go
    type ListMeta struct {
        // 下面这两个变量在ObjectMeta相同,不多解释
        SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,1,opt,name=selfLink"`
        ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
        // 在列举对象的时候可能会有非常多的对象,kubernetes支持分页获取,类似于SQL的limit,当对象总量
        // 多于单页的总量的时候,这个变量就会被设置。它用来告知用户需要继续获取,并且它包含了下次获取的
        // 起始位置。
        Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"`
        // 从字面意思也能理解,就是还剩多少个对象,这个和Continue是配合使用的,当Continue被设置了
        // 这个变量就不会为空,用来告诉用户还有多少对象没有获取。
        RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
    }
    
    // 代码源自k8s.io/apimachinery/pkg/apis/meta/v1/meta.go
    // 下面所有的函数是接口ListInterface的ListMeta实现,ListInterface是API对象列表公共属性(meta)
    // 的抽象。
    func (meta *ListMeta) GetResourceVersion() string        { return meta.ResourceVersion }
    func (meta *ListMeta) SetResourceVersion(version string) { meta.ResourceVersion = version }
    func (meta *ListMeta) GetSelfLink() string               { return meta.SelfLink }
    func (meta *ListMeta) SetSelfLink(selfLink string)       { meta.SelfLink = selfLink }
    func (meta *ListMeta) GetContinue() string               { return meta.Continue }
    func (meta *ListMeta) SetContinue(c string)              { meta.Continue = c }
    func (meta *ListMeta) GetRemainingItemCount() *int64     { return meta.RemainingItemCount }
    func (meta *ListMeta) SetRemainingItemCount(c *int64)    { meta.RemainingItemCount = c }

    此处做一个小结,在metav1包中,为API单体对象和对象列表的公共属性(meta)做了抽象,分别为metav1.Object和 metav1.ListInterface。同时,metav1包为这两个抽象做了实现,他们分别为metav1.ObjectMeta和metav.listMeta,API对象类型可以通过继承这些类实现抽象,是不是有一点apimachinery所定义的那样“类型”、“基础设施”的意思了?

    2.3runtime

    2.3.1schema

    前文提到了metav1.TypeMeta实现了schema.ObjectKind(本节简称ObjectKind),并且metav1.TypeMeta已经非常直观的和xxx.yaml的相应字段对应上了,那ObjectKind又是干什么的呢?

    // 代码源自k8s.io/apimachinery/pkg/runtime/schema/interfaces.go
    // ObjectKind是接口,两个接口函数是GroupVersionKind类型的setter和getter
    type ObjectKind interface {
        SetGroupVersionKind(kind GroupVersionKind)
        GroupVersionKind() GroupVersionKind
    }
    // GroupVersionKind才是kubernetes的API对象类型真身,他包括Kind、Version和Group。其中Kind和
    // Version还比较好理解,Group又是什么?其实Group/Version才是xxx.yaml的apiVersion字段。
    // 在kuberentes中API对象是分组的,像Pod、Service、ConfigMap都属于core分组,而core分组的对象
    // 无需在apiVersion字段明文写出来,系统会默认将这类的对象归为core分组,正如文章开始那个Pod的例子。
    // 详情可以看下面的代码实现。
    type GroupVersionKind struct {
        Group   string
        Version string
        Kind    string
    }
    // 这个函数在metav1.TypeMeta实现GroupVersionKind()接口的时候调用了,该函数调用了ParseGroupVersion
    // 实现从apiVersion解析Group和Version。
    func FromAPIVersionAndKind(apiVersion, kind string) GroupVersionKind {
        if gv, err := ParseGroupVersion(apiVersion); err == nil {
            return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
        }
        return GroupVersionKind{Kind: kind}
    }
    // 从apiVersion解析Group和Version。
    func ParseGroupVersion(gv string) (GroupVersion, error) {
        // 这种不报错是什么道理?什么情况下会有对象没有Group和Version?
        if (len(gv) == 0) || (gv == "/") {
            return GroupVersion{}, nil
        }
        // 数apiVersion中有几个‘/’
        switch strings.Count(gv, "/") {
        // 没有'/',就像文章开始的Pod的例子,那么Group就是空字符串,系统默认会把空字符串归为core
        case 0:
            return GroupVersion{"", gv}, nil
        // 有一个'/',那么就以'/'分割apiVersion,左边为Group,右边为Version。
        case 1:
            i := strings.Index(gv, "/")
            return GroupVersion{gv[:i], gv[i+1:]}, nil
        // 其他则为格式错误。
        default:
            return GroupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv)
        }
    }

    这就为什么Deployment的apiVersion是apps/v1或者extension/v1beta1了,至此可以总结如下:

    1. schema.ObjecKind是所有API对象类型meta的抽象;
    2. metav1.TypeMeta是schema.ObjecKind的一个实现,API对象类型通过继承metav1.TypeMeta实现schema.ObjecKind;

    2.3.2Object

    如果说schema.ObjecKind是所有API对象类型的抽象,配合metav1.Object作为所有API单体对象公共属性的抽象,似乎已经找到了所有API对象的根。但是有没有感觉怪怪的,如果想通过一个基类的指针指向任意API单体对象,schema.ObjecKind和metav1.Object感觉都不合适,因为他们所能访问的域是有限。如果有一个函数需要访问任何API对象的类型和公共属性,那么就要传入同一个对象的两个指针(schema.ObjecKind和metav1.Object),这就太让人难以接受了。有没有一个类型作为API单体对象的统一的基类呢?这就是本节要讨论的:runtime.Object(本章节简称 Object)。

    // 代码源自k8s.io/apimachinery/pkg/runtime/interfaces.go
    type Object interface {
        // 有了这个函数,就可以访问对象的类型域
        GetObjectKind() schema.ObjectKind
        // deepcopy是golang深度复制对象的方法,至于什么是深度复制本文就不解释了。这是个不错的函数,
        // 可以通过这个接口复制任何API对象而无需类型依赖。
        DeepCopyObject() Object
        // 就这么两个函数了么?那如果需要访问对象的公共属性域怎么办?不应该有一个类似GetObjectMeta()
        // 的接口么?这一点,kubernetes是通过另一个方式实现的,见下面的代码。
    }
    
    // 代码源自k8s.io/apimachinery/pkg/api/meta/meta.go,注意是api包,不是apis
    // Accessor()函数可以把obj安全的转换为metav1.Object,这样也就避免了每个API对象类型都需要实现
    // 类似GetObjectMeta()的接口了。有的读者肯定会问:所有的API对象都继承了metav1.ObjectMeta,
    // 这个类型不是实现了GetObjectMeta()么?笔者就要在这里做出说明:笔者提到是类似GetObjectMeta(),
    // 如果接口名字是ObjectMeta(),那岂不是继承metav1.ObjectMeta就没用了?一个顶层的类型抽象定义不
    // 应该依赖于相对底层类型的实现。
    func Accessor(obj interface{}) (metav1.Object, error) {
        // 使用了golang的switch type语法
        switch t := obj.(type) {
        // 因为API对象类型都继承了metav1.ObjectMeta,也就自然实现了metav1.Object。
        case metav1.Object:
            return t, nil
        // 在ObjectMeta章节笔者提到了,metav1.ObjectMeta实现了metav1.ObjectMetaAccessor,
        // 所以API对象也自然实现了metav1.ObjectMetaAccessor。但是API对象会在上一个case就返回
        // 了,这个case是给谁用的呢?笔者也比较疑惑,笔者感觉是那些没有直接继承metav1.ObjectMeta
        // 却实现了metav1.ObjectMetaAccessor的类型,笔者暂时还没找到相关类型定义。
        case metav1.ObjectMetaAccessor:
            if m := t.GetObjectMeta(); m != nil {
                return m, nil
            }
            return nil, errNotObject
        default:
            return nil, errNotObject
        }
    }

    等下,为什么没有看到API对象实现runtime.Object.DeepCopyObject()?那是因为deep copy是具体API对象类型需要实现的,存在类型依赖,作为API对象类型的父类不能实现。此处还是以Pod为例,看看Pod是如何实现DeepCopyObject()的。

    // +genclient
    // +genclient:method=GetEphemeralContainers,verb=get,subresource=ephemeralcontainers,result=EphemeralContainers
    // +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers,input=EphemeralContainers,result=EphemeralContainers
    // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    // 上面+k8s:deepcopy-gen:....就是告诉代码生成工具为下面的类型生成runtime.Object接口的
    // DeepCopyObject()函数实现。因为所有的API对象类型都要实现DeepCopyObject()函数,这是一个相当
    // 大量的重复工作,所以kubernetes用代码生成工具来实现。至于如何实现的不作为本文讨论重点,只要读者
    // 知道deep copy的目的就可以了。
    type Pod struct {
        ......
    }

    3.总结

    至此,前面各章节的总结都可以忘掉了,因为那些总结都是基于当时的知识背景做的总结,可能缺乏全局性的考虑做出错误的结论,所以在此做出通盘的总结,如下图所示:

     

    1. runtime.Object是所有API单体对象的根类(interface);
    2. schema.ObjectKind是对API对象类型的抽象(interface);
    3. metav1.Object是对API对象公共属性的抽象(interface);
    4. metav1.ListInterface是对API对象列表公共属性的抽象(interface);
    5. metav1.TypeMeta是schema.ObjectKind的一个实现,API对象类型继承之;
    6. metav1.ObjectMeta是metav1.Object的一个实现,API对象类型继承之;
    7. metav1.ListMeta是metav1.ListInterface的一个实现,API对象列表继承之;

     

    展开全文
  • 兴趣对象是COSMIC方法中一个... 在COSMIC中对兴趣对象有明确的定义,识别兴趣对象的规则也有定义。结合度量手册与其他指南,结合我的实践经验,总结了如下的实用性规则,供大家参考:1兴趣对象一定是来自于功能需求

    兴趣对象是COSMIC方法中一个关键的概念。它影响了我们识别的功能点的多少。兴趣对象是描述中在功能需求中的、功能用户感兴趣的事物,它可以是一个具体的东西,也可以是一个抽象的概念,系统一定要对它执行输入、输出、读或写的动作。

     在COSMIC中对兴趣对象有明确的定义,识别兴趣对象的规则也有定义。结合度量手册与其他指南,结合我的实践经验,总结了如下的实用性规则,供大家参考:

    1兴趣对象一定是来自于功能需求的,是需求中提到的,而不是实现时开发人员基于技术的需求而临时存储或处理的数据。比如临时表、临时变量、系统界面中软件开发商的信息,这些都不是兴趣对象,也就是对这些信息的处理都不计算功能点。

    2兴趣对象未必一定要存储到硬盘上或其他介质中,即兴趣对象未必一定要在系统中读或写,它也可以只有输入或只有输出,比如在查询功能中的查询条件,就是一个兴趣对象,他可能只有输入的数据移动。

    3不同的兴趣对象代表了不同的事物或概念。比如人和车就是不同的事物。

    4在一个系统中,具有不同属性的事物是不同的兴趣对象。

    5在一个系统中,具有不同发生频率的事物是不同的兴趣对象。

    6在一个系统中,具有不同的关键字的事物是不同的兴趣对象。


    我们可以通过如下的例子,加深对上面的规则的理解:

    例1:录入人员的如下信息:人名,身份证号,性别,车牌号。

    此时是识别为几个兴趣对象呢?应该只有一个兴趣对象:人员,它的属性包括人名,身份证号,性别,车牌号。车牌号不应该被识别为一个单独的兴趣对象,它只是人员的一个属性!

    所以此需求的功能点为:




    例2:录入如下的信息:人名,身份证号,性别,车牌号,车的发动机号,车的品牌。一个人最多拥有一部车。
    此时是识别为几个兴趣对象呢?应该是两个兴趣对象:

    人员(人名,身份证号,性别,车牌号)

    车(车牌号,车的发动机号,车的品牌)

    根据前面的规则3与规则4,此时人和车是两个不同的兴趣对象,他们具有不同的属性,有不同的关键字,虽然他们在本需求中是一对一的关系。不能认为车的发动机号,车的品牌是人的属性。

    所以此需求的功能点为:



    例3:
    录入如下的信息:人名,性别,车牌号,发动机号,品牌。但是一个人可以拥有多部车。 

    此时是识别为几个兴趣对象呢?还应该是两个兴趣对象:

    人员(人名,身份证号,性别)

    车(车牌号,车的发动机号,车的品牌,车主的身份证号)

    根据前面的规则5,一个人拥有多部车,人与车是单独的兴趣对象。

    所以此需求的功能点为:


    例4:录入如下的信息:人名,性别,车牌号,车的发动机号,车的品牌。但是一个人可以拥有多部车,一部车可以属于多个人拥有,比如一家人,都可以认为是车的主人,一家人可以有多部车。 

    此时是识别为几个兴趣对象呢?应该是三个兴趣对象:

    人员(人名,身份证号,性别)

    车(车牌号,车的发动机号,车的品牌)

    人与车的对应关系(车牌号,身份证号)

     根据前面的规则5,一个人拥有多部车,人与车之间对应关系是单独的兴趣对象,而人与车的关系,有自己独立的关键字,所以应该识别为单独的一个兴趣对象,并且,人、车,人与车的对应关系,他们三者发生的频率也是不同的。

    所以此需求的功能点为:


     上述的这个例子仅仅是一个示例,现实中的需求可能不是这样的,因为维护上述的关系很可能是不同的功能处理。

    例5: 查询在人员数据库中所有年龄超过x的员工

    在此需求中,根据规则2,查询条件是一个单独的兴趣对象,它只有会被输入,不会被存储,而人员信息是另外一个兴趣对象。此需求的功能点为:


    在商业应用软件中,每个兴趣对象通常有多个属性,而在嵌入式实时系统中,很可能一个兴趣对象只有一个属性: 

    例6: 人可以通过遥控器可以控制空调的温度大小。

    此时,目标温度是一个兴趣对象,其属性只有一个,即温度值。

    此时在区分目标温度是否是是一个单独的兴趣对象时,要根据规则3来判定。

    兴趣对象并不意味着要求要采用面向对象的方法,这里的兴趣对象与面向对象方法中的对象不能画等号。采用结构化开发方法或其他任何开发方法都可以使用COSMIC方法计算功能点。

     



    展开全文
  • OpenCV 对象跟踪

    千次阅读 2022-02-07 22:23:09
    OpenCV 对象跟踪 这篇文章使用 OpenCV 中内置的八种不同的对象跟踪算法,实现对物体的跟踪。 首先,介绍一下8种跟踪算法。 然后,演示如何使用OpenCV实现这些...仅出于遗留原因和比较其他算法而兴趣。 (最低 OpenC

    OpenCV 对象跟踪

    这篇文章使用 OpenCV 中内置的八种不同的对象跟踪算法,实现对物体的跟踪。

    首先,介绍一下8种跟踪算法。

    然后,演示如何使用OpenCV实现这些跟踪算法。

    最后,对本文做总结。

    OpenCV 对象跟踪器

    OpenCV 八种对象跟踪器:

    • BOOSTING Tracker:基于用于驱动 Haar 级联 (AdaBoost) 背后的机器学习的相同算法,但与 Haar 级联一样,已有十多年的历史。这个跟踪器很慢,而且效果不太好。仅出于遗留原因和比较其他算法而感兴趣。 (最低 OpenCV 3.0.0)
    • MIL Tracker:比 BOOSTING 跟踪器更准确,但在报告失败方面做得很差。 (最低 OpenCV 3.0.0)
    • KCF 跟踪器:内核化相关过滤器。比 BOOSTING 和 MIL 更快。与 MIL 和 KCF 类似,不能很好地处理完全遮挡。 (最低 OpenCV 3.1.0)
    • CSRT Tracker:判别相关滤波器(具有通道和空间可靠性)。往往比 KCF 更准确,但速度稍慢。 (最低 OpenCV 3.4.2)
    • MedianFlow Tracker:很好地报告失败;但是,如果运动中的跳跃太大,例如快速移动的物体,或者外观快速变化的物体,模型就会失败。 (最低 OpenCV 3.0.0)
    • TLD 跟踪器:我不确定 TLD 跟踪器的 OpenCV 实现或实际算法本身是否存在问题,但 TLD 跟踪器极易出现误报。我不推荐使用这个 OpenCV 对象跟踪器。 (最低 OpenCV 3.0.0)
    • MOSSE Tracker:非常非常快。不如 CSRT 或 KCF 准确,但如果您需要纯粹的速度,这是一个不错的选择。 (最低 OpenCV 3.4.1)
    • GOTURN Tracker:OpenCV 中唯一基于深度学习的目标检测器。它需要额外的模型文件才能运行(本文不会涉及)。我最初的实验表明,尽管据说它可以很好地处理查看变化,但使用起来还是有点痛苦(尽管我最初的实验并没有证实这一点)。我将尝试在以后的帖子中介绍它,但与此同时,请看一下 Satya 的文章。 (最低 OpenCV 3.2.0)

    个人建议:

    • 当需要更高的对象跟踪精度并且可以容忍较慢的 FPS 吞吐量时,请使用 CSRT
    • 当需要更快的 FPS 吞吐量但可以处理稍低的对象跟踪精度时使用 KCF
    • 当需要纯粹的速度时使用 MOSSE

    物体跟踪

    在开始算法之前,先写辅助方法和类。

    fps类:

    import datetime
    
    class FPS:
    	def __init__(self):
    		# 定义开始时间、结束时间和总帧数
    		self._start = None
    		self._end = None
    		self._numFrames = 0
    
    	def start(self):
    		# 开始计时
    		self._start = datetime.datetime.now()
    		return self
    
    	def stop(self):
    		# 停止计时
    		self._end = datetime.datetime.now()
    
    	def update(self):
    		# 增加在开始和结束间隔期间检查的总帧数
    		self._numFrames += 1
    
    	def elapsed(self):
    		# 返回开始和结束间隔之间的总秒数
    		return (self._end - self._start).total_seconds()
    
    	def fps(self):
    		# 计算每秒帧数
    		return self._numFrames / self.elapsed()
    

    请打开一个新文件,将其命名为 object_tracker.py ,定义resize方法,等比例缩放图片。

    import cv2
    from fps import FPS
    def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
        # 初始化要调整大小的图像的尺寸并抓取图像大小
        dim = None
        (h, w) = image.shape[:2]
        # 如果宽高都为None,则返回原图
        if width is None and height is None:
            return image
        # 检查宽度是否为None
        if width is None:
            # 计算高度的比例并构造尺寸
            r = height / float(h)
            dim = (int(w * r), height)
        # 否则,高度为 None
        else:
    		# 计算宽度的比例并构造尺寸
            r = width / float(w)
            dim = (width, int(h * r))
        resized = cv2.resize(image, dim, interpolation=inter)
        return resized
    

    定义全局变量:

    videos = 0
    tracker_type = 'kcf'
    

    我们的命令行参数包括:

    videos:输入视频文件或者摄像头的ID。

    tracker_type:跟踪器的类型,接下来的代码定义了跟踪器列表。

    接下来定义不同类型的跟踪器:

    # 提取 OpenCV 版本信息
    (major, minor) = cv2.__version__.split(".")[:2]
    # 如果我们使用 OpenCV 3.2 或之前版本,我们可以使用特殊的工厂函数来创建我们的对象跟踪器
    if int(major) == 3 and int(minor) < 3:
        tracker = cv2.Tracker_create(tracker_type)
    # 否则,对于 OpenCV 3.3 或更新版本,我们需要显式调用对应的对象跟踪器构造函数:
    else:
        # 初始化一个字典,将字符串映射到其对应的 OpenCV 对象跟踪器实现
        OPENCV_OBJECT_TRACKERS = {
            "csrt": cv2.TrackerCSRT_create,
            "kcf": cv2.TrackerKCF_create,
            "boosting": cv2.legacy.TrackerBoosting_create,
            "mil": cv2.TrackerMIL_create,
            "tld": cv2.legacy.TrackerTLD_create,
            "medianflow": cv2.legacy.TrackerMedianFlow_create,
            "mosse": cv2.legacy.TrackerMOSSE_create
        }
        # 使用我们的 OpenCV 对象跟踪器对象字典获取适当的对象跟踪器
        tracker = OPENCV_OBJECT_TRACKERS[tracker_type]()
    

    在OpenCV 3.3之前,必须使用cv2.Tracker_create创建跟踪器对象,并传递跟踪器名称的大写字符串。

    对于OpenCV 3.3+,可以使用各自的函数调用创建每个跟踪器,例如cv2.TrackerCSRT_create。字典OPENCV_OBJECT_TRACKERS包含8个内置OpenCV对象跟踪器中的七个。它将对象跟踪器命令行参数字符串(键)与实际的OpenCV对象跟踪器函数(值)进行映射。

    # 初始化我们要追踪的物体的边界框坐标
    initBB = None
    vs = cv2.VideoCapture(videos)
    fps = None
    

    initBB初始化为None,此变量将保存我们使用鼠标选择的对象的边界框坐标。

    接下来,初始化VideoCapture对象和FPS计数器。

    让我们开始循环来自视频流的帧:

    # 循环播放视频流中的帧
    while True:
        # 抓取当前帧。
        (grabbed, frame) = vs.read()
        if not grabbed:
            break
        # 调整框架大小并获取框架尺寸。
        frame = resize(frame, width=500)
        (H, W) = frame.shape[:2]
    
        # 检查是否正在跟踪一个对象
        if initBB is not None:
            # 抓取物体的新边界框坐标
            (success, box) = tracker.update(frame)
            # 检查跟踪是否成功
            if success:
                (x, y, w, h) = [int(v) for v in box]
                cv2.rectangle(frame, (x, y), (x + w, y + h),
                              (0, 255, 0), 2)
            # 更新 FPS 计数器
            fps.update()
            fps.stop()
            # 初始化在框架上显示的信息集
            info = [
                ("Tracker", tracker_type),
                ("Success", "Yes" if success else "No"),
                ("FPS", "{:.2f}".format(fps.fps())),
            ]
            # 遍历信息元组并将它们绘制在框架上
            for (i, (k, v)) in enumerate(info):
                text = "{}: {}".format(k, v)
                cv2.putText(frame, text, (10, H - ((i * 20) + 20)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
    
        # 显示输出帧
        cv2.imshow("Frame", frame)
        key = cv2.waitKey(1) & 0xFF
    

    抓住一个帧,如果获取不到帧,则退出。

    为了使对象跟踪算法能够更快地处理帧,我们将输入帧的大小调整为500像素。

    然后输出框架的高和宽。

    如果已选择对象,则需要更新对象的位置。 update方法将定位对象的新位置并返回成功布尔值和对象的边界框。

    如果成功,我们在框架上绘制新的,更新的边界框位置。

    更新FPS。

    初始化显示的文本信息列表。随后,绘制到frame上。

    显示输出帧。

       # 使用's'键选择一个边界框来跟踪
        if key == ord("s"):
            # 选择跟踪的对象的边界框(选择 ROI 后按 ENTER 或 SPACE)
            initBB = cv2.selectROI("Frame", frame, fromCenter=False,
                                   showCrosshair=True)
            # 使用提供的边界框坐标启动 OpenCV 对象跟踪器,然后也启动 FPS 吞吐量估计器
            tracker.init(frame, initBB)
            fps = FPS().start()
        # 如果 `q` 键被按下,则退出循环
        elif key == ord("q"):
            break
    vs.release()
    cv2.destroyAllWindows()
    
    

    按下“s”键时,使用cv2.selectROI“选择”对象ROI。此时,视频帧冻结,用鼠标绘制跟踪对象的边界框。

    绘制完边界框,然后按“ENTER”或“SPACE”确认选择。如果需要重新选择区域,只需按“ESCAPE”即可。

    然后,启动OpenCV 对象跟踪器,再启动 FPS 吞吐量估计器。

    最后一个段代码只是处理我们已经脱离循环的情况。释放所有指针并关闭窗口。

    总结

    在今天的博客文章中,您学习了如何利用OpenCV进行对象跟踪。具体来说,我们回顾了OpenCV库中包含的8个对象跟踪算法(从OpenCV 3.4开始):

    CSRT、KCF、Boosting、MIL、TLD、MedianFlow、MOSSE、GOTURN。

    建议对大多数对象跟踪应用程序使用CSRT,KCF或MOSSE:

    当需要更高的对象跟踪精度并且可以容忍更慢的FPS吞吐量时,请使用CSRT

    当需要更快的FPS吞吐量时使用KCF,但可以处理稍低的对象跟踪精度

    当需要纯粹的速度时使用MOSSE

    完整代码:

    https://download.csdn.net/download/hhhhhhhhhhwwwwwwwwww/79602278

    展开全文
  • DICOM标准之三_信息对象定义1

    千次阅读 2012-07-17 14:52:38
    第3部分 信息对象定义(1) 1.应用范围和层次 Dicom标准的本部分明确了一套信息对象定义(IOD),提供了可应用于数字医疗信息的真实世界对象的抽象定义。 对于每个IOD,本部分明确了: 1)IOD语义描述的任何所需信息; 2...
  • 机器学习(概述一)——定义

    万次阅读 2019-05-28 16:18:17
    基本定义 基本概念 机器学习能用来干吗 机器学习的常见应用框架 机器学习的分类 基于学习形式分类 基于目的分类 机器学习中的十大经典算法 补充 术语 关系 总结 机器学习(Machine Learning, ML)是一门多领域交叉...
  • Vue3.0 的 reactive API 定义和源码实现

    千次阅读 2020-03-23 11:24:51
    一、定义及优点 1.1 定义 reactive API的定义为传入一个对象并返回一个基于原对象的响应式代理,即返回一个 Proxy,相当于 Vue2x版本中的 Vue.observer。 首先,我们需要知道在 Vue3.0中彻底废掉了原先的 Options ...
  • python可迭代对象概念讲解

    千次阅读 2020-12-05 14:14:15
    python可迭代对象怎么理解什么是可迭代对象?简单的理解是可以用for循环的对象,就是可迭代对象。比如:list,字符串,dict,元组,生成器等。具有可迭代的特征。(推荐学习:Python视频教程)自定义可迭代对象(本质)...
  • 今天闲来无事,有空闲的时间,所以想坐下来聊一聊Java的GC以及Java对象在内存中的分配。 和标题一样,本篇绝对是用最直接最通俗易懂的大白话来聊 文章中基本不会有听起来很高大上专业术语,也不会有太多概念性的...
  • js返回对象的类型方法封装

    千次阅读 2020-05-11 10:37:09
    type方法封装,返回对象类型问题引出 问题引出 我们有时会遇到下面的问题: 那么我们怎样准确判断一个对象的准确类型呢,特别是遇到类数组时,这里我们介绍三种常见方法: 1.第一种: [ ].constructor / { }....
  • ES6对象操作实例详解

    千次阅读 2021-01-13 18:57:49
    本文实例讲述了ES6对象操作。分享给大家供大家参考,具体如下:1.对象赋值es5中的对象赋值方式如下:let name="小明";let skill= 'es6开发';var obj= {name:name,skill:skill};console.log(obj);结果为:ES6允许把...
  • C++类和对象详解

    千次阅读 多人点赞 2020-05-08 00:43:54
    两种创建对象的方式:一种是在栈上创建,形式和定义普通变量类似;另外一种是在堆上使用 new 关键字创建,必须要用一个指针指向它,读者要记得 delete 掉不再使用的对象。 通过对象名字访问成员使用点号.,通过对象...
  • 你好呀,我是沉默王二,是《Web 全栈开发进阶之路》的作者,...本篇来谈一谈“面向对象编程”中的所有概念。 因为是第一次做付费专栏,所以有一定的压力,虽然只需要 9.9 元,但我对自己的要求是至少要达到 199 元...
  • 对象和接口的理解

    千次阅读 2019-05-19 17:56:03
    对象 接口之间的关系及理解 1.类和对象的关系 首先,类是对象的类型。 然后,类是具有相同属性和方法的一组对象集合(一个类可对应多个对象)。 那怎么理解理解类和对象之间的关系呢。 我们举个简单的例子,...
  •  在ES5及早期版本中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码,如下: let options = { repeat:true, save:false }; //从对象中取数据 let repeat = options....
  • C# 匿名对象

    千次阅读 2017-09-07 13:16:26
    匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。 可通过使用 new 运算符和对象...
  • Mybatis通过一条SQL查出关联的对象

    万次阅读 2017-04-24 17:47:08
    Mybatis通过一条SQL查出关联的对象 以往在做对象的查询时如果需要把关联的对象一起查出来是通过resultMap的子查询来进行的。通过子查询来进行的关联对象的查询时,Mybatis会重新发起一次数据库请求,这在有的时候...
  • 目标检测(Object Detection)旨在寻找出图像中所有兴趣的目标物体或对象,包含物体定位和物体分类两个子任务,同时确定它们的类别和位置。如下图所示,通过卷积神经网络有效定位猫的轮廓及类别。 目标检测是...
  • 二哥,我就是上次说你《教妹学Spring》看不懂的那个小白,没想到你还特意写了一篇入门级的 Java 基础知识,这次真的看懂了,感觉好棒。请原谅我上次的唐突,二哥能够照顾我们这些小白的学习进度,真的...类和对象是...
  • 文章目录C++类的对象和类的指针的区别指向地址的指针指针本身的大小指向数组的指针指针数组指向指针数组的指针多维指针数组函数参数中使用指针数组指针传址实现数组求和函数指针模仿C++ 类别函数指针数组对象指针...
  • 在我们的日常工作与学习中,java的使用是十分重要的,几乎涉及到了工作的方方面面。...一、首先说一下,在定义对象时,直接添加属性和方法,代码展示如下:functionPerson(name,age){this.name=name;this.age...
  • 基于Java基础-面向对象实现植物大战僵尸简易版

    万次阅读 多人点赞 2019-03-31 15:15:44
    基于Java基础-面向对象实现植物大战僵尸简易版前言游戏设计游戏对象游戏内容游戏优化放置植物的优化移除植物的优化游戏可玩性的优化添加游戏背景音乐后续优化源码分享 前言 从零开始学习的Java的第一个月,在面向...
  • 本文实例讲述了python对象转字典的两种实现方式。分享给大家供大家参考,具体如下:一. 方便但不完美的__dict__对象转字典用到的方法为__dict__. 比如对象对象a的属性a.name='wk', a.age=18, 那么如果直接将使用a.__...
  • 这是一本游戏设计方面的好书 ... 感谢天之虹的无私奉献 ...玩家和游戏设计师之间都有着一些初始的共同语言,但我们从来没有为了讨论特定游戏而一起去定义游戏。我们会用“飘”、“响应灵敏”或者“操作很差”来描述一个
  • 重学设计模式——你真的面向对象了吗?

    千次阅读 多人点赞 2022-01-04 20:03:02
    前言 在最初学习Java的时候,我们都听到过一句话,Java是面向对象语言。每当提到面向对象的时候,许多开发者也嗤之以鼻:都什么年代了,谁还不知道面向对象。 重学设计模式后,请回答,你真的面向对象了吗? ...
  • 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 具体如下图 java 普通对象结构 java 数组对象结构 对象结构组成 对象头 ...
  • 逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。 例如: public   static  StringBuffer ...
  • 面向对象之设计模式

    千次阅读 2016-11-22 23:15:32
    面向对象之设计模式前言 要想推开架构师的那扇大门,就离不开设计模式这把钥匙。对设计模式的理解与精通,是通往架构师之路的第一步 在任何面向对象语言的开发过程以及个人职业技能成长的道路中,新手与新手或者新手...
  • 十一、JAVA接口的定义和使用

    万次阅读 多人点赞 2018-04-26 20:44:39
    接口的基本概念: 我们知道知道java中只支持单继承,但如果我们想定义一些功能,想让一个子类都继承实现,显然没办法做到,所有Java提供了接口这个概念,这样我们就可以用一个子类去实现多个接口。我们可以理解为...
  • Java中的对象是什么?

    千次阅读 2020-07-14 12:45:19
    Java是一种面向对象的编程语言,它将世界视为具有属性和行为的对象的集合。 Java的面向对象版本非常简单,它是该语言几乎所有内容的基础。 因为它对Java非常重要,所以我将对幕后内容进行一些解释,以帮助任何不熟悉...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 203,909
精华内容 81,563
关键字:

对象感的定义