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

结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在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. 阿里云服务器纯净版centos7.4 LNMP安装

    Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable ...

  2. Codeforces Gym101246H:``North-East''(LIS+思维)

    http://codeforces.com/gym/101246/problem/H 题意:在二维平面上有n个点,从最左下角的点出发,每次走只能走在当前的点的右上角的点(xj > xi, yj ...

  3. 02(d)多元无约束优化问题-拟牛顿法

    此部分内容接<02(a)多元无约束优化问题-牛顿法>!!! 第三类:拟牛顿法(Quasi-Newton methods) 拟牛顿法的下降方向写为: ${{\mathbf{d}}_{k}}= ...

  4. c++2的幂次方

    c++2的幂次方 题目描述 任何一个正整数都可以用2的幂次方表示. 同时约定用括号来表示方次,即a的b次,可以表示为a(b). 由此可知,137可以表示为: 2(7)+2(3)+2(0) 进一步: ...

  5. Altium Designer设计PCB--如何设置铺铜与导线或过孔的间距

    笑话: 到银行汇款,车临时停路边上. 为了怕交警罚就把朋友留下看车,跟他说有查车的过来了告诉我一声. 进去几分钟果然有交警来了. 那个朋友风风火火地闯进银行大声吼道:“大哥,警察来了,快走啊!” 偌大 ...

  6. 4.秋招复习简单整理之java支持多继承吗?

    java仅支持单继承,但支持接口多实现.

  7. 【学习笔记】动态规划—斜率优化DP(超详细)

    [学习笔记]动态规划-斜率优化DP(超详细) [前言] 第一次写这么长的文章. 写完后感觉对斜优的理解又加深了一些. 斜优通常与决策单调性同时出现.可以说决策单调性是斜率优化的前提. 斜率优化 \(D ...

  8. 【题解】搬书-C++

    搬书 Description 陈老师桌上的书有三堆,每一堆都有厚厚的一叠,你想逗一下陈老师,于是你设计一个最累的方式给他,让他把书 拿下来给同学们.若告诉你这三堆分别有i,j,k本书,以及每堆从下到上 ...

  9. 和朱晔一起复习Java并发(五):并发容器和同步器

    本节我们先会来复习一下java.util.concurrent下面的一些并发容器,然后再会来简单看一下各种同步器. ConcurrentHashMap和ConcurrentSkipListMap的性能 ...

  10. WMI_COM_API

    Win32_Processor // CPU 处理器 Win32_PhysicalMemory // 物理内存 Win32_Keyboard // 键盘 Win32_PointingDevice // ...