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

结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在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. 关于c++中的Debug以及runtime_error之segment_fault

    差不多每次编一些竞赛类的程序都会至少给我报一次runtime_error(运行时错误).这或许也是广大OIer心中永远的痛.~_~ 本文主要讨论如何对runtime_error以及其中的segment ...

  2. gh-ost

    目录 1.简介 2.为什么不用触发器 ? 3.命名由来 4.亮点 5.使用 6.它是如何工作的? 7.工作模式 7.1.模式1 -- 连上从库,在主库上修改 7.2.模式2 -- 直接在主库上修改 7 ...

  3. Nginx运行报错unknown directive ""

    使用文本编辑器把编码格式修改为UTF-8即可. 推荐文本编辑器:notepad++,自行百度搜索即可下载

  4. 什么是JDK什么是JRE?JDK和JRE的关系

    什么是JDK什么是JRE?JDK和JRE的关系 我们看看来自百度百科的解释: JDK是 Java 语言的软件开发工具包,主要用于移动设备.嵌入式设备上的java应用程序.JDK是整个java开发的核心 ...

  5. Perm排列计数(新博客试水,写的不好,各路大神见谅)

    B. Perm 排列计数 内存限制:512 MiB 时间限制:1000 ms 标准输入输出   题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i&l ...

  6. MyBatis 存储过程

    From<MyBatis从入门到精通> <!-- 6.2 存储过程 6.2.1 第一个存储过程 delimiter ;; create procedure 'select_user_ ...

  7. ZIP:GZIP

    GZIPInputStream: GZIPInputStream(InputStream in) :使用默认缓冲区大小创建新的输入流. GZIPInputStream(InputStream in, ...

  8. 【dfs基础讲解及例题】

    定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种. 接下来因为懒得去找大段大段深奥的材料 所以就是一些个人的理解. 所谓深搜,是相对于广搜(只是第一篇)来说的.深 ...

  9. 学习笔记-jvm运行时数据区

    按照线程私有和共享区域来划分 线程私有 程序计数器 指向当前线程正在执行的字节码行号地址,如果是本地方法,值为undefined 虚拟机中唯一不会oom的区域 为什么会有程序计数器 - java天生多 ...

  10. idea导入ssm javaweb maven项目

    本文笔者辛苦整理, 除了为方便大家贴的maven安装配置和方便的现有项目, 如转载请注明: https://www.cnblogs.com/m-yb/p/11229320.html idea导入ssm ...