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 ...
随机推荐
- kafka入门(三)备份
一.相关概念 备份相关的角色 Kafka消息备份分三个角色:分别是Leader副本.Follower副本.ISR集合 Leader副本 负责直接响应client端的读写请求,即和生产者和消费者直接对接 ...
- 我在工作中遇到的redis集群使用
写次随笔,给自己工作学习中记录一个笔记 废话不多说,直接上代码: 功能:将相关信息存到redis中,并设置过期时间,如果redis中有,从redis获取,如果没有,从mysql中获取.redis配置了 ...
- c++ 组合
组合 题目描述 已知一个一维数组a1..n,又已知一整数m. 如能使数组a中任意几个元素之和等于m,则输出YES,反之则为NO. 输入 输入包括两行,第一行包含两个整数n m(1<=n<2 ...
- 对于springboot的几种注入方法的个人看法
最近在知乎上面看到一篇关于程序员面试的问题,面试官问我们一般有几种注入的方法,这几种注入的方法分别在什么时候运用比合理,当时我看到这个时候懵逼了,由于我自己也是刚刚接触springboot不久,所以就 ...
- 详叙BeanWrapper和PropertyDescriptor
每篇一句 千古以来要饭的没有要早饭的,知道为什么吗? 相关阅读 [小家Spring]聊聊Spring中的数据转换:Converter.ConversionService.TypeConverter.P ...
- CF39D Cubical Planet-C++
银河系中没有你找不到的东西!有一颗形状为立方体的的行星正在绕着一颗形状为二十面体的恒星运转.现在我们让这颗行星的两个在同一条体对角线上的顶点置于(0,0,0)和(1,1,1)上.有两只苍蝇住在行星上. ...
- tomcat不需要重启热部署xml文件
项目中,遇到情况,有时候增加struts的配置了,有时候粗心改错了,然后急需要发布线上吧,又不能重启影响其他的,最后发现struts有这个功能呢! 在struts.xml的配置文件中加上一句话就行 & ...
- (读论文)推荐系统之ctr预估-DeepFM模型解析
今天第二篇(最近更新的都是Deep模型,传统的线性模型会后面找个时间更新的哈).本篇介绍华为的DeepFM模型 (2017年),此模型在 Wide&Deep 的基础上进行改进,成功解决了一些问 ...
- dedecms新建内容模型“把数据保存到数据库附加表时出错‘xxx’出错”错误的原因分析和解决方案(转)
把数据保存到数据库附加表 `bc_addonarticle17` 时出错,请把相关信息提交给DedeCms官方.INSERT INTO `bc_addonarticle17`(aid,typeid,r ...
- python取数字、字母
python取数字.字母 有一串字符串“lxa7YzU”,其中有大写字母.小写字母和数字,现编写一脚本使得实现以下功能: 将这串字符串中的数字.大写字母.小写字母分别取出来并进行分类. 脚本如下所示: ...