引用曾经看到的一篇文章里面对 Golang 中结构体的描述,如果说 Golang 的基础类型是原子,那么

结构体就是分子。我们都知道分子是由原子组成的,换言之就是结构体里面可以包含基础类型、切片、

字典、数组以及结构体自身。

结构体类型的定义

结构体定义的一般方式

  1. type identifier struct {
  2. field1 type1
  3. field2 type2
  4. ...
  5. }

结构体里的字段都有名字,而对于字段在代码中从来也不被使用到,那么可以命名它为_。对于相同类型的

字段我们可以使用简写的形式,比如:

  1. type T struct {
  2. a,b int
  3. }

还有一个需要注意的地方是结构体中字段的大小写,首字母大写表示公有变量,首字母小写表示私有变量,

相当于其他语言类成员变量的Public、Private ,但是这个公有变量和私有变量在 Go 中是相对于 Package 来

说的,在同一个 Package 都是公有的。

结构体变量的创建

1 最常见的创建结构体变量形式

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Circle struct {
  6. x int
  7. y int
  8. Radius int
  9. }
  10.  
  11. func main() {
  12. var c Circle = Circle{
  13. x:100,
  14. y:100,
  15. Radius:50,
  16. }
  17.  
  18. fmt.Println(c)
  19. }
  20. --------------------------------
  21. {100 100 50}

通过显示指定结构体内部字段的名称和初始值初始化结构体,可以只指定部分字段的初始值,或一个都不指定,

那些没有被指定初始值的字段会自动初始化为相应类型的零值。

2 结构体变量创建的第二种形式是不显示指定字段而是根据字段顺序初始化,但是这样需要显示的提供所有字段的

初始值

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Circle struct {
  6. x int
  7. y int
  8. Radius int
  9. }
  10.  
  11. func main() {
  12. var c Circle = Circle{100,100,50}
  13.  
  14. fmt.Println(c)
  15. }
  16. -------------------------------
  17. {100 100 50} 

3 使用 new() 函数创建结构体变量

结构体变量和普通变量都有指针形式,使用取地址符就可以得到结构体的指针类型

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Circle struct {
  6. x int
  7. y int
  8. Radius int
  9. }
  10.  
  11. func main() {
  12. var c *Circle = &Circle{100,100,50}
  13. fmt.Printf("%+v\n", c)
  14. }
  15. ------------------------------------------
  16. &{x:100 y:100 Radius:50}

下面看一下使用 new() 创建一个零值结构体,所有的字段都会被初始化成对应类型的零值

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Circle struct {
  6. x int
  7. y int
  8. Radius int
  9. }
  10.  
  11. func main() {
  12. var c *Circle = new(Circle)
  13. fmt.Printf("%+v\n", c)
  14. }
  15. -----------------------------------
  16. &{x:0 y:0 Radius:0}

注意:new 函数返回的是指针类型

结构体的参数传递

看一个实例

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. // 声明一个结构体
  6. type employee struct {
  7. name,address string
  8. height,weight float64
  9. }
  10.  
  11. // 定一个方法,该方法的参数是一个结构体,主要用于修改结构体成员中name的值
  12. func modifyAttribute(emy employee) {
  13. emy.name = "newer"
  14. fmt.Println(emy)
  15. }
  16.  
  17. func main() {
  18. // 初始化结构体
  19. emy := employee{
  20. name:"xiaoming",
  21. address:"beijing",
  22. height:172.0,
  23. weight:75.3,
  24. }
  25.  
  26. // 打印修改前的值
  27. fmt.Println(emy)
  28.  
  29. // 调用modifyAttribute
  30. modifyAttribute(emy)
  31.  
  32. // 打印修改后值
  33. fmt.Println(emy)
  34. }
  35. ----------------------------------
  36. 输出结果
  37. {xiaoming beijing 172 75.3}
  38. {newer beijing 172 75.3}
  39. {xiaoming beijing 172 75.3}

从上面的输出结果上来看,虽然在 modifyAttribute 方法中修改了 name 值,但是在 main 函数中打印 name 的

值并没有变化,说明这是一个值传递

我们把 modifyAttribute 函数的参数变成结构体的指针类型,如下

  1. func modifyAttribute(emy *employee) {
  2. emy.name = "newer"
  3. fmt.Println(emy)
  4. }
  5.  
  6. func main() {
  7. // 初始化结构体
  8. emy := employee{
  9. name:"xiaoming",
  10. address:"beijing",
  11. height:172.0,
  12. weight:75.3,
  13. }
  14.  
  15. // 打印修改前的值
  16. fmt.Println(emy)
  17.  
  18. // 调用modifyAttribute
  19. modifyAttribute(&emy)
  20.  
  21. // 打印修改后值
  22. fmt.Println(emy)
  23. }
  24. ----------------------------
  25. 输出结果
  26. {xiaoming beijing 172 75.3}
  27. &{newer beijing 172 75.3}
  28. {newer beijing 172 75.3}

我们看到在函数  modifyAttribute 中的修改影响到了 main 函数中的 name 值,这里是引用传递

再看一个例子:编写扩大圆半径的函数

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. // 定义一个结构体 Circle
  6. type Circle struct {
  7. x int
  8. y int
  9. Radius int
  10. }
  11.  
  12. // 通过值传递扩大圆半径
  13. func expandByValue(c Circle) {
  14. c.Radius *= 2
  15. }
  16.  
  17. // 通过引用传递扩大圆半径
  18. func expandByPointer(c *Circle) {
  19. c.Radius *= 2
  20. }
  21. func main() {
  22. c := Circle{
  23. Radius:50,
  24. }
  25.  
  26. expandByValue(c)
  27. fmt.Println(c)
  28.  
  29. expandByPointer(&c)
  30. fmt.Println(c)
  31. }
  32. --------------------------------
  33. 输出结果
  34. {0 0 50}
  35. {0 0 100}

 我们可以从上面的输出中再次看到通过值传递,在函数里面修改结构体的状态不会影响原有结构

体的状态,通过值传递就不一样。

结构体方法

    Go 语言不是面向对象的语言,在 Go 语言中没有类的概念,结构体正是类的替代品。类可以附加很多成员方法,

结构体也可以。看一个实例,如何给结构体绑定方法:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "math"
  6. )
  7.  
  8. // 定义一个结构体 Circle
  9. type Circle struct {
  10. x int
  11. y int
  12. Radius int
  13. }
  14.  
  15. // 计算圆的面积 第一个括号里面表示的是方法的接收者 这里方法的接收者是结构体 Circle
  16. // Area() 表示函数名 float64 表示函数的返回值类型
  17. func (c Circle) Area() float64{
  18. return math.Pi * float64(c.Radius) * float64(c.Radius)
  19. }
  20.  
  21. // 计算圆的周长
  22. func (c Circle) Circumference() float64 {
  23. return 2 *math.Pi * float64(c.Radius)
  24. }
  25.  
  26. func main() {
  27. // 初始化结构体
  28. c := Circle{
  29. Radius:50,
  30. }
  31.  
  32. fmt.Println(c.Area(),c.Circumference())
  33. }
  34. -------------------------------------------------
  35. 输出结果
  36. 7853.981633974483 314.1592653589793

结构体的指针方法 

   如果使用结构体方法的形式给 Circle 增加一个扩大圆半径的方法,会发现半径还是扩大不了

  1. func (c Circle) expand() {
  2. c.Radius *= 2
  3. }

 这个方法和前面的 expandByValue 函数是等价的,只是调整了一下第一个参数的位置,在参数传递的时候依然是值传递

,所以,还是无法起到扩大圆半径的作用,这个时候就需要使用结构体的指针方法

  1. func (c *Circle) expand() {
  2. c.Radius *= 2
  3. }

  结构体指针方法和值方法在调用上没有区别,只是一个可以改变结构体内部状态,另一个不会。另外指针方法可以使用结

构体变量调用,值方法也可以使用结构体指针变量使用

结构体变量调用指针方法(比如调用计算圆周长的方法):

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "math"
  6. )
  7.  
  8. // 定义一个结构体 Circle
  9. type Circle struct {
  10. x int
  11. y int
  12. Radius int
  13. }
  14.  
  15. // 计算圆的周长
  16. func (c *Circle) Circumference() float64 {
  17. c.Radius *= 2
  18. return 2 *math.Pi * float64(c.Radius)
  19. }
  20.  
  21. func main() {
  22. // 初始化结构体
  23. c := Circle{
  24. Radius:50,
  25. }
  26. fmt.Println(c.Circumference())
  27. }
  28. -----------------------------------------
  29. 输出结果
  30. 628.3185307179587

使用结构体指针变量调用值方法:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "math"
  6. )
  7.  
  8. // 定义一个结构体 Circle
  9. type Circle struct {
  10. x int
  11. y int
  12. Radius int
  13. }
  14.  
  15. // 计算圆的面积
  16. func (c Circle) Area() float64{
  17. return math.Pi * float64(c.Radius) * float64(c.Radius)
  18. }
  19.  
  20. func main() {
  21. // 初始化结构体
  22. c := &Circle{
  23. Radius:50,
  24. }
  25.  
  26. fmt.Println(c.Area())
  27. }
  28. ---------------------------------
  29. 输出结果
  30. 7853.981633974483

内嵌结构体

   结构体作为一种变量可以嵌套在另一个结构体中作为一个字段使用,这种内嵌结构体在 Go 语言中称之为

组合

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. // 定义一个结构体
  6. type Pointer struct {
  7. x int
  8. y int
  9. }
  10.  
  11. func (p Pointer) show() {
  12. fmt.Println(p.x,p.y)
  13. }
  14.  
  15. // 定义另一个结构体
  16. type Circle struct {
  17. // 将结构体 Pointer 嵌套在 Circle
  18. loc Pointer
  19. Radius int
  20. }
  21.  
  22. func main() {
  23. c := Circle{
  24. loc:Pointer{
  25. x:100,
  26. y:100,
  27. },
  28. Radius:50,
  29. }
  30.  
  31. fmt.Println(c)
  32. fmt.Println(c.loc)
  33. fmt.Println(c.loc.x,c.loc.y)
  34. c.loc.show()
  35. }
  36. -----------------------------------
  37. 输出结果
  38. {{100 100} 50}
  39. {100 100}
  40. 100 100
  41. 100 100

匿名内嵌结构体

还有一种特殊的内嵌结构体形式,内嵌的结构体不提供名称。这时外面的结构体直接继承内嵌结构体的所有

内部字段和方法

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. // 定义一个结构体
  6. type Pointer struct {
  7. x int
  8. y int
  9. }
  10.  
  11. func (p Pointer) show() {
  12. fmt.Println(p.x,p.y)
  13. }
  14.  
  15. // 定义另一个结构体
  16. type Circle struct {
  17. // 匿名内嵌结构体
  18. Pointer
  19. Radius int
  20. }
  21.  
  22. func main() {
  23. c := Circle{
  24. Pointer:Pointer{
  25. x:100,
  26. y:100,
  27. },
  28. Radius:50,
  29. }
  30.  
  31. fmt.Println(c)
  32. fmt.Println(c.Pointer)
  33. fmt.Println(c.x,c.y) //继承字段
  34. fmt.Println(c.Pointer.x,c.Pointer.y)
  35. c.show() // 继承方法
  36. c.Pointer.show()
  37. }
  38. --------------------------------------
  39. 输出结果
  40. {{100 100} 50}
  41. {100 100}
  42. 100 100
  43. 100 100
  44. 100 100
  45. 100 100 

 Go 语言的结构体没有多态性

    我们知道面向对象语言的三大特性,封装、继承、多态。其中多态是指父类定义的方法可以调用子类实现的方法,不同的子类有不同的实现方法,

从而给父类的方法带来了多样的不同行为。而 Go 不是面向对象语言,它的结构体也不支持结构体,如下示例

  1. package main
  2.  
  3. import "fmt"
  4.  
  5. // 定义一个fruit结构体
  6. type Fruit struct {
  7.  
  8. }
  9.  
  10. func (f Fruit) eat() {
  11. fmt.Println("eat Fruit")
  12. }
  13.  
  14. func (f Fruit) enjoy() {
  15. fmt.Println("smell first")
  16. f.eat()
  17. fmt.Println("clean finally")
  18. }
  19.  
  20. // 定一个apple结构体
  21. type Apple struct {
  22. Fruit
  23. }
  24.  
  25. func (a Apple) eat() {
  26. fmt.Println("eat apple")
  27. }
  28.  
  29. // 定义一个banana结构体
  30. type Banana struct {
  31. Fruit
  32. }
  33.  
  34. func (b Banana) eat() {
  35. fmt.Println("eat banana")
  36. }
  37.  
  38. func main() {
  39. apple := Apple{}
  40. banana := Banana{}
  41.  
  42. apple.enjoy()
  43. banana.enjoy()
  44. }
  45. -----------------------------
  46. 输出结果
  47. smell first
  48. eat Fruit
  49. clean finally
  50. smell first
  51. eat Fruit
  52. clean finally

从上面的输出结果中可以看到,虽然外部结构体可以继承内部结构体的方法和字段,但是外部结构体的方法不能覆盖内部结构体的方法,

enjoy 方法调用的 eat 方法还是 Fruit 自己的 eat 方法,它并没有被外面结构体的方法覆盖掉

Go 结构体(Struct)的更多相关文章

  1. C语言结构体-struct

    知识点: 1)结构体的定义. 2)结构体的sizeof. 3)  结构体的指针. 1) 结构体的定义: 在逻辑上有一定关联的多个数据类型做为一整体进行操作的数据结构,它的关键字是struct.下面我将 ...

  2. 内核中用于数据接收的结构体struct msghdr(转)

    内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...

  3. Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别

    Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...

  4. C语言 - 结构体(struct)比特字段(:) 详细解释

    结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...

  5. Go语言学习笔记(四)结构体struct & 接口Interface & 反射

    加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...

  6. 结构体(struct)大小

    结构体(struct)大小 本文参考链接:C语言结构体(struct)常见使用方法,链接中的实例代码经实践有几处不准确,本文在引用时已做更改 注意:在结构体定义时不能申请空间(除非是结构体变量),不可 ...

  7. 结构体struct sockaddr_in, struct sockaddr,struct in_addr

    一.结构体 struct sockaddr_in,  struct sockaddr,  struct in_addr struct sockaddr_in,  struct sockaddr,str ...

  8. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...

  9. 结构体struct、联合体union、枚举类型enum

    1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...

  10. C++/C#:类Class与结构体Struct的区别

    C++中: 默认的访问控制.继承访问权限不同:struct时public的,class时 private的: 其它基本一样. C#中: struct是值类型,class是引用类型的: struct S ...

随机推荐

  1. svn安装使用

    SVN安装使用 获取项目 1.首先新建文件夹.如:测试项目. 2.接着鼠标右键选择:SVN Checkout/SVN 检出 3.在出行的对话框中输入仓库地址.如:svn://198.021.262/2 ...

  2. python字典的排序,按key排序和按value排序---sorted()

    >>> d{'a': 5, 'c': 3, 'b': 4} >>> d.items()[('a', 5), ('c', 3), ('b', 4)] 字典的元素是成键 ...

  3. JDK8 元空间

    1. 运行时常量池和静态变量都存储到了堆中,MetaSpace存储类的元数据,MetaSpace直接申请在本地内存中(Native memory),这样类的元数据分配只受本地内存大小的限制,OOM问题 ...

  4. onclick 常用手册

    1.如何去使用onclick来跳转到我们指定的页面/跳转到指定url ☆如果只是在本页显示的话,可以直接用location, 方法如下: ①onclick="javascript:windo ...

  5. C#——WebApi 接口参数传参详解

    本篇打算通过get.post.put.delete四种请求方式分别谈谈基础类型(包括int/string/datetime等).实体.数组等类型的参数如何传递. 一.get请求 对于取数据,我们使用最 ...

  6. Logstash进程杀不掉

    由于ELK磁盘不足,对磁盘进行扩容,重启服务器,重启之后发现自动起来了一个logstash进程,PID一直变,无法杀掉,极其耗费CPU logstash 是通过rpm方式安装的,版本是6.2.2 解决 ...

  7. ORCAl存储过程

    1.存储过程结构 1.1 第一个存储过程 create or replace procedure proc1( para1 varchar2, para2 out varchar2, para3 in ...

  8. Django框架----外键关联

    app/models.py中: 创建班级表 class classes(models.Model): id = models.AutoField(primary_key=True) name = mo ...

  9. GoldenGate实时投递数据到大数据平台(4)- ElasticSearch 2.x

    ES 2.x ES 2.x安装 下载elasticSearch 2.4.5, https://www.elastic.co/downloads/elasticsearch 解压下载后的压缩包,启动ES ...

  10. Linux centos7下php安装cphalcon扩展的方法

    说明: 操作系统:CentOS7 php安装目录:/usr/local/php php.ini配置文件路径:/usr/local/php/etc/php.ini 运行环境:LNMP ,PHP7 .安装 ...