前面我们或多或少的都使用了结构体这种数据结构,本身结构体也有很多特性,我们一一来看。

结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在Java中class的作用。在结构体重也可以嵌套结构体。结构体还可以有自己的方法。

1.定义结构体

我们先定义一个结构体:

结构体定义如下:

type 标识符 struct {
field1 type
field2 type
}

例子:

type Staff struct {
UserId int16
UserName string
Sex byte
Age int8
}

2. 使用结构体

有三种方式可以使用结构体:

var staff Staff
staff1 := new(Staff)
staff2 := &Staff{}

上面2和3的效果是一样的,返回的都是指向结构体的指针。

Go中的结构体不像class一样有构造函数,一般会使用上述第三种方式来构造出一个结构体对象,简称Go中的工厂模式:

package main

import "fmt"

type Staff struct {
UserId int16
UserName string
Sex byte
Age int8
} func NewStaff(userId int16, userName string, sex byte, age int8) *Staff {
return &Staff{
UserId:userId,
UserName:userName,
Sex:sex,
Age:age,
}
} func main() {
staff := NewStaff(123,"xiaoming",byte(1),13)
fmt.Println(staff)
}

3. 带标签的结构体

结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。 标签的内容不可以在一般的编程中使用,只有通过反射机制才能能获取它

我们现在有这样一段程序:从json文件中读取json字符串,然后转为json对象:

json文件内容:

{
"port": "7788",
"address": "47.95.34.2"
}

代码如下:

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
) type MainConfig struct {
port string
address string } func LoadConfig(path string) *MainConfig {
buf, err := ioutil.ReadFile(path)
if err != nil {
log.Panicln("load config conf failed: ", err)
}
mainConfig := &MainConfig{}
err = json.Unmarshal(buf, mainConfig)
if err != nil {
log.Panicln("decode config file failed:", string(buf), err)
}
fmt.Println(mainConfig.address,mainConfig.port) return mainConfig
} func main() {
LoadConfig("c:/test.json") }

执行以上程序可以发现打印出来的结果是空的,原因是:Go开发规范认为:只有开头是大写字母的对象,方法才被认为是公开的,可以在包外访问,否则就是私有的,外部对象无法访问。

那我们定义结构体大写之后,但是想让结构体中的字段json格式化为小写应该怎么做呢?这时候就可以通过tag来指定:

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
) type MainConfig struct {
Port string `json:"port"`
Address string `json:"address"`
} //反射获取字段中的tag
func reflectTag(mg MainConfig) {
mgType := reflect.TypeOf(mg)
tag0 := mgType.Field(0).Tag
tag1 := mgType.Field(0).Tag
fmt.Println(tag0,tag1)
} func LoadConfig(path string) *MainConfig {
buf, err := ioutil.ReadFile(path)
if err != nil {
log.Panicln("load config conf failed: ", err)
}
mainConfig := &MainConfig{}
err = json.Unmarshal(buf, mainConfig)
if err != nil {
log.Panicln("decode config file failed:", string(buf), err)
}
bytes, err := json.Marshal(mainConfig)
fmt.Println(string(bytes))
return mainConfig
} func main() {
LoadConfig("c:/test.json")
config := MainConfig{"1234", "123.221.134"}
reflectTag(config) }
打印结果:
{"port":"7788","address":"47.95.34.2"}
json:"port" json:"port"

上面的代码是一段从json文件中读取配置文件的例子,也使用了反射机制来获取tag标签,使用 json.Unmarshal来反序列化,使用json.Marshal将对象序列化为json字符串。

4.方法

其实Go中任何自定义的类型都可以有方法,不仅仅是struct才有。除了指针和interface。

看一个自定义类型带方法的例子:

package main

import "fmt"

type MainConfig1 struct {
Port string `json:"port"`
Address string `json:"address"`
} type Str string func (s Str) Compact(str string,str1 string) string {
return str + str1
} func (s *Str) Compact1(str string,str1 string) Str {
return Str(str + str1)
} //mf相当于其他语言中的this,self表示当前对象本身
func (mf MainConfig1) Compact2() Str {
return Str(mf.Port + "|" + mf.Address)
} func main() {
var s Str
compact1 := s.Compact("a", "b")
str := s.Compact1("c", "d")
fmt.Println(compact1,str) var mf MainConfig1
mf.Port = "2"
mf.Address = "3333"
mf.Compact2() }

声明 Str类型的s,通过s可以调用这两个方法。另外还记得Go的访问控制规范吧,首字母大写表示公共方法,小写表示私有,自定义类型的方法同样遵循这个原则,试着把 Compact1方法的首字母改为小写,你会发现通过 s.Compact1("c", "d")找不到方法。

再看Compact2()方法的使用,使用当前接收者本身来获取属性,跟Java中的this关键字相似。

上面还有一个疑问点:

使用 Str 和使用 *Str作为接受者有什么不同呢?

区别就在于:在接收者是指针时,方法可以改变接收者的值(或状态)

我们来改造一下Compact2()方法:

func (mf MainConfig1) Compact2() Str {
mf.Address = "rrrr"
return Str(mf.Port + "|" + mf.Address)
} var mf MainConfig1
mf.Port = "2"
mf.Address = "3333"
compact2 := mf.Compact2()
fmt.Println(compact2)
bytes, _ := json.Marshal(mf)
fmt.Println(string(bytes))

输出的结果是:

2|rrrr
{"port":"2","address":"3333"}

函数作用域内的Address值被改变了,但是实际上mf对象的属性值并没有被改变。

那么我们将receiver 改为指针试一下:

func (mf *MainConfig1) Compact2() Str {
mf.Address = "rrrr"
return Str(mf.Port + "|" + mf.Address)
} var mf MainConfig1
mf.Port = "2"
mf.Address = "3333"
compact2 := mf.Compact2()
fmt.Println(compact2)
bytes, _ := json.Marshal(mf)
fmt.Println(string(bytes))

输出的结果是:

2|rrrr
{"port":"2","address":"rrrr"}

会发现这次mf的属性值也会改变。因为本身我们使用的this对象就是 *MainConfig1自然可以改变对象内部的值。

5. 函数和方法的区别

函数将变量作为参数:Function1(recv)

方法在变量上被调用:recv.Method1()

在接收者是指针时,方法可以改变接收者的值(或状态)

receiver_type 叫做 (接收者)基本类型,这个类型必须在和方法同样的包中被声明。

在 Go 中,(接收者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接收者来建立。

Go中的结构体的更多相关文章

  1. C++中的结构体

    http://zhidao.baidu.com/link?url=8OYQSKV9mvSBc6Hkf9NsLQmipSge9VCZDJQGAZZs5PCBQ54UTmK98VRmAklEEAFYu7d ...

  2. C/C++中的结构体

    结构体定义 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构.   结构体作用 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成 ...

  3. C语言中的结构体,结构体数组

    C语言中的结构体是一个小难点,下面我们详细来讲一下:至于什么是结构体,结构体为什么会产生,我就不说了,原因很简单,但是要注意到是结构体也是连续存储的,但要注意的是结构体里面类型各异,所以必然会产生内存 ...

  4. 再识C中的结构体

    在前面认识C中的结构体中我介绍了结构体的基础知识,下面通过这段代码来回顾一下: #include<stdio.h> #define LEN 20 struct Student{ //定义结 ...

  5. C语言中的结构体和C++中的结构体以及C++中类的区别

    c++中结构体可以定义一个函数 C中的结构体和C++中结构体的不同之处:在C中的结构体只能自定义数据类型,结构体中不允许有函数,而C++中的结构体可以加入成员函数. C++中的结构体和类的异同: 一. ...

  6. 关于C语言中结构体中的结构体成员导致的字节对齐问题

    关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...

  7. C语言中处理结构体的原理

    汇编中有几种寻址方式,分别是直接寻址:(ds:[idata]).寄存器间接寻址(ds:[bx]).寄存器相对寻址(ds:[bx + idata].ds:[bx + si])基址变址寻址(ds:[bx ...

  8. 浅析C#中的结构体和类

    类和结构是 .NET Framework 中的常规类型系统的两种基本构造. 两者在本质上都属于数据结构.封装着一组总体作为一个逻辑单位的数据和行为. 数据和行为是该类或结构的"成员" ...

  9. C++中的结构体的认识

    C++中的结构体的认识 1. typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间. 实例像:typedef ...

  10. x264中重要结构体参数解释,参数设置,函数说明 <转>

    x264中重要结构体参数解释http://www.usr.cc/thread-51995-1-3.htmlx264参数设置http://www.usr.cc/thread-51996-1-3.html ...

随机推荐

  1. HDU 4055:Number String(DP计数)

    http://acm.hdu.edu.cn/showproblem.php?pid=4055 题意:给一个仅包含‘I','D','?'的字符串,’I'表示前面的数字比后面的数字要小(Increase升 ...

  2. windows7(win7)64/32位激活工具

    win7激活工具中文绿色免费版是改自binbin的作品,我们修改的windows7激活工具grldr模拟激活是别人的东西,能激活win7旗舰.原作者是binbin,其他的激活工具都是基于grldr模拟 ...

  3. Java web导出word,文件名为中文,%E6%9D%90%E8%B4%A8%E5%8D%95乱码

    因为文件名为中文,导出时会有乱码问题 原代码: String fileName = "文件名.doc"; response.setHeader("Content-Disp ...

  4. 控制反转&依赖注入

    IoC(Inversion of Control,控制反转).这是spring的核心,贯穿始终.所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系.这是什么 ...

  5. C语言指针专题——为何要学习指针

    欢迎转发本文! 之前的文章与各位谈论了指针是什么,以及指针为何这那么难学.不少知友留言说看了我的文章对指针了解了不少,这给我继续创作提供了莫大的动力啊.指针其实就是一个纸老虎,你看着可怕,等你了解其本 ...

  6. Dom4J的基本使用

    初始化数据 <?xml version="1.0" encoding="UTF-8"?> <RESULT> <VALUE> ...

  7. mysql重启遇到的问题

    不知道是不是每次更新 MySQL 软件之后都需要执行数据库升级指令?在我进行过的几次软件升级之后,总会在 MySQL 的日志中见到 “[ERROR] Missing system table mysq ...

  8. Java编程思想:File类list()方法

    import java.util.regex.Pattern; import java.io.File; import java.io.FilenameFilter; public class Tes ...

  9. 在 .h 和 cpp 中查找 :grep consume ~/test/2016/AMQP-CPP/**/*.cpp ~/test/2016/AMQP-CPP/**/*.h -r

    :grep consume ~/test/2016/AMQP-CPP/**/*.cpp ~/test/2016/AMQP-CPP/**/*.h -r -w "whole" 匹配整个 ...

  10. [leetcode] 559. Maximum Depth of N-ary Tree (easy)

    原题链接 思路: 简单bfs class Solution { public: int maxDepth(Node *root) { int depth = 0; if (root == NULL) ...