Golang结构体

面向对象编程

结构体

Golang语言面向对象编程说明

  1. Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
  2. Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Golang是基于struct来实现OOP特性的。
  3. Golang面向对象编程非常简洁,去掉了传统OOP语言的继承、方法重载、构造函数和析构函数、隐藏的this指针等等
  4. Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承: Golang没有extends 关键字,继承是通过匿名字段来实现。
  5. Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang中面向接口编程是非常重要的特性
package main

import "fmt"

type Cat struct {
   Name  string
   Age   int
   Color string
}

func main() {
   var cat1 Cat
   cat1.Name = "小白"
   cat1.Age = 1
   cat1.Color = "白色"
   fmt.Println(cat1)
   fmt.Println(cat1.Name)
   fmt.Println(cat1.Age)
   fmt.Println(cat1.Color)
}

结构体和结构体变量(实例)的区别和联系

  1. 结构体是自定义的数据类型,代表一类事物.
  2. 结构体变量(实例)是具体的,实际的,代表一个具体变量
Golang结构体_第1张图片

字段/属性

  • 字段声明语法同变量,示例:字段名字段类型

  • 字段的类型可以为:基本类型、数组或引用类型

  • 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样:

    ​ 布尔类型是false ,数值是0,字符串是""。

    ​ 数组类型的默认值和它的元素类型相关,比如score [3]int则为[0,0,0] 指针,slice,和map的零值都是nil,即还没有分配空间。

  • 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个。

package main

import "fmt"

//如果结构体的字段类型是:指针,slice,和map的零值都是 nil ,即还没有分配空间
//如果需要使用这样的字段,需要先make,才能使用.

type Person struct {
   Name   string
   Age    int
   Scores [5]float64
   ptr    *int
   slice  []int
   map1   map[string]string
}

func main() {
   //定义结构体
   var p1 Person
   fmt.Println(p1)

   p1.slice = make([]int, 10)
   p1.slice[0] = 100
   p1.map1 = make(map[string]string)
   p1.map1["key"] = "tom"
   fmt.Println(p1)
}

结构体是值类型

Golang结构体_第2张图片

创建结构体变量和访问结构体字段

方式1-直接声明案例演示: var person Person

方式2-{} 案例演示: var person Person = Person{}

方式3-* 案例: var person *Person= new (Person)

方式4-&案例: var person *Person = &Person

package main

import "fmt"

type Person struct {
   Name string
   Age  int
}

func main() {
   //方式一:
   var p1 Person
   fmt.Println(p1)

   //方式二:
   var p2 Person
   p2.Name = "tom"
   p2.Age = 12
   fmt.Println(p2)

   //方式三:
   var p3 *Person = new(Person)
   //因为p3是一个指针,因此标准的给字段赋值方式
   //(*p3).Name = "tom" 也可以这样写 p3.Name = "tom"
   //原因:go的设计者 为了程序员使用方便, 底层会对 p3.Name = "tom" 进行处理
   //会给p3加上 取值运算 (*p3).Name = "tom"
   (*p3).Name = "tom"
   p3.Name = "john"
   (*p3).Age = 30
   fmt.Println(*p3)

   //方式四:
   var p4 *Person = &Person{"marry", 1}
   //因为p4是指针,因此标准的访问字段的方法
   //(*p4).Name = "tom"
   //go的设计者为了程序员使用方便,也可以 p4.Name = "tom"
   //原因和上面一样,底层会对 p4.Name = "tom" 进行处理, 会加上(*p4)
   fmt.Println(*p4)
   (*p4).Name = "tom"
   p4.Name = "tom2"
   (*p4).Age = 48
   fmt.Println(*p4)
}
  • 第3种和第4种方式返回的是结构体指针。
  • 结构体指针访问字段的标准方式应该是:(结构体指针).字段名,比如(*person).Name = “tom”
  • 但go做了一个简化,也支持结构体指针.字段名,比如 person.Name = “tom”。更力符合程序员使用的习惯,go编译器底层对person.Name做了转化(*person).Name

结构体细节

细节一

结构体的所有字段在内存中是连续的

Golang结构体_第3张图片
package main

import "fmt"

type Point struct {
   x int
   y int
}

type Rect struct {
   leftUp, rightDown Point
}

type Rect2 struct {
   leftUp, rightDown *Point
}

func main() {
   r1 := Rect{Point{1, 2}, Point{3, 4}}

   //r1有4个int ,在内存中是连续分布的
   fmt.Printf("r1.leftUp.x 地址 = %p r1.leftUp.y 地址= %p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p\n",
      &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)

   r2 := Rect2{&Point{10, 20}, &Point{30, 40}}

   //r2有两个 *point类型, 这两个*Point类型的本身地址也是连续的,
   //但是他们指向的地址不一定是连续的
   fmt.Printf("r2.leftUp 本身的地址 = %p  r2.rightDown 本身的地址= %p\n",
      &r2.leftUp, &r1.rightDown)
}

细节二

结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)

package main

import "fmt"

type A struct {
   Num int
}

type B struct {
   Num int
}

func main() {
   var a A
   var b B
   a = A(b) // 可以转换 , 但是有要求,就是结构体的字段要完全一样(包括:名字,个数和类型)
   fmt.Println(a, b)
}

细节三

结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转

Golang结构体_第4张图片

细节四

struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化。

package main

import (
   "encoding/json"
   "fmt"
)

type Monster struct {
   Name  string `json:"name"` // 就是 struct tag
   Age   int    `json:"age"`
   Skill string `json:"skill"`
}

func main() {
   //1.创建一Monster变量
   monster := Monster{"牛魔王", 500, "芭蕉扇"}

   //2.将monster变量序列化为JSON格式字符串
   //json.Marshal 使用到反射
   jsonStr, err := json.Marshal(monster)
   if err != nil {
      fmt.Println("错误")
   }
   fmt.Println("jsonStr = ", string(jsonStr))
}

你可能感兴趣的:(Golang,golang,开发语言,后端)