Go中的结构体
前面我们或多或少的都使用了结构体这种数据结构,本身结构体也有很多特性,我们一一来看。
结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在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中的结构体的更多相关文章
- C++中的结构体
http://zhidao.baidu.com/link?url=8OYQSKV9mvSBc6Hkf9NsLQmipSge9VCZDJQGAZZs5PCBQ54UTmK98VRmAklEEAFYu7d ...
- C/C++中的结构体
结构体定义 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构. 结构体作用 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成 ...
- C语言中的结构体,结构体数组
C语言中的结构体是一个小难点,下面我们详细来讲一下:至于什么是结构体,结构体为什么会产生,我就不说了,原因很简单,但是要注意到是结构体也是连续存储的,但要注意的是结构体里面类型各异,所以必然会产生内存 ...
- 再识C中的结构体
在前面认识C中的结构体中我介绍了结构体的基础知识,下面通过这段代码来回顾一下: #include<stdio.h> #define LEN 20 struct Student{ //定义结 ...
- C语言中的结构体和C++中的结构体以及C++中类的区别
c++中结构体可以定义一个函数 C中的结构体和C++中结构体的不同之处:在C中的结构体只能自定义数据类型,结构体中不允许有函数,而C++中的结构体可以加入成员函数. C++中的结构体和类的异同: 一. ...
- 关于C语言中结构体中的结构体成员导致的字节对齐问题
关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...
- C语言中处理结构体的原理
汇编中有几种寻址方式,分别是直接寻址:(ds:[idata]).寄存器间接寻址(ds:[bx]).寄存器相对寻址(ds:[bx + idata].ds:[bx + si])基址变址寻址(ds:[bx ...
- 浅析C#中的结构体和类
类和结构是 .NET Framework 中的常规类型系统的两种基本构造. 两者在本质上都属于数据结构.封装着一组总体作为一个逻辑单位的数据和行为. 数据和行为是该类或结构的"成员" ...
- C++中的结构体的认识
C++中的结构体的认识 1. typedef的用法 在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间. 实例像:typedef ...
- x264中重要结构体参数解释,参数设置,函数说明 <转>
x264中重要结构体参数解释http://www.usr.cc/thread-51995-1-3.htmlx264参数设置http://www.usr.cc/thread-51996-1-3.html ...
随机推荐
- 在django中用MySQL为数据库 新建一个项目的流程
创建mysql数据库 在cmd输入 mysql -uroot -p 启动mysql客户端并连接服务器 创建一个db_orm的数据库 mysql>CREATE DATABASE db_orm ch ...
- scrapy基础知识之 CrawlSpiders:
通过下面的命令可以快速创建 CrawlSpider模板 的代码: scrapy genspider -t crawl spidername xx.com LinkExtractors class sc ...
- Modbus 指令
本节内容: 一.S7-1200 作为Modbus RTU 主站 二.S7-1200 作为Modbus RTU 从站 三.S7-1200 作为Modbus RTU 主站 S7-1200 作为Modbus ...
- JAVA超级简单的爬虫例子(1)
爬取整个页面的数据,并进行有效的提取信息,注释都有就不废话了: public class Reptile { public static void main(String[] args) { Stri ...
- Spring boot ,dubbo整合异常
Caused by: java.lang.IllegalArgumentException: java.lang.ClassCastException: com.guooo.boot.acc.serv ...
- ASP.NET--Repeater控件分页功能实现
这两天由于‘销售渠道’系统需要实现新功能,开发了三个页面,三个界面功能大致相同. 功能:分页显示特定sql查询结果,点击上一页下一页均可显示.单击某记录可以选定修改某特定字段<DropDownL ...
- 使用flink Table &Sql api来构建批量和流式应用(3)Flink Sql 使用
从flink的官方文档,我们知道flink的编程模型分为四层,sql层是最高层的api,Table api是中间层,DataStream/DataSet Api 是核心,stateful Stream ...
- SpringBoot工程热部署
SpringBoot工程热部署 1.在pom文件中添加热部署依赖 <!-- 热部署配置 --> <dependency> <groupId>org.springfr ...
- AT649 自由研究
这道题有些水... 我们观察到,这是一道彻底离线的题目,连输入也没有,我们可以发现1<=n<=401<=n<=401<=n<=40 于是,我们就可以考虑n=1n=1 ...
- Spark学习之RDD
RDD概述 什么是RDD RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素可并行计算的集合 ...