Go语言结构
Go通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型。一个带属性的结构体试图表示一个现实世界中的实体。结构体是复合类型(composite types),当需要定义一个类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。然后可以访问这些数据,就好像它是一个独立实体的一部分。结构体也是值类型,因此可以通过new函数来创建。
组成结构体类型的那些数据称为字段(fields)。每个字段都有一个类型和一个名字;在一个结构体中,字段名字必须是唯一的。
我们为什么需要结构体?
编程语言,终究是为了解决我们现实生活中的问题,生活中的事物(如人)都有属性(嘴、胳膊)和方法(行走、跳跃)。使用之前的普通类型和数组来表示这些是不方便的,结构体就像其它编程语言中的类(class),包含了一系列的属性和方法,结构体能够更好的描述事物,更好的解决问题。
tips:属性即字段
结构体定义
结构体定义的一般方式如下:
type identifier struct {
field1 type1
field2 type2
...
}
示例:
type Student struct {
Name string
Age int
Score float64
}
type T struct{a,b int}
也是合法的语法,它更适用于简单的结构体。
结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口。可以声明结构体类型的一个变量,然后像下面这样给它的字段赋值:
var s T
s.a = 6
s.b = 8
数组可以看作是一种结构体类型,不过它使用下标而不是具名的字段。
创建结构体实例
创建结构体实例有两种方式,一种是普通方式(var t T
)创建,另一种是使用new()方法来创建对应结构体的实例。
普通方式创建结构体实例
声明var t T
会给t分配内存,并零值化内存,这个时候t的类型是T。语法如下
var t T
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p Person
p = Person{Name:"黄忠", age:35}
fmt.Println(p)
}
new()创建结构体实例
使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针: var t *T = new(T)
,如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配却没有必要在开始就做)。
var t *T = new(T)
写这条语句的惯用方法是:t := new(T)
,变量 t 是一个指向 T 的指针(t的类型是*T),此时结构体字段的值是它们所属类型的零值。
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p *Person = new(Person)
(*p).Name = "黄忠" //也可以简写成 p.Name = "黄忠",编译器在编译时会优化自动帮我们加上
(*p).age = 34 //也可以简写成 p.age = 34
fmt.Println(*p)
}
在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值)。在首次使用结构体里的引用字段的时候一定要先make()或者使用字面量的方式初始化才能再使用。
例如:
package main
import "fmt"
type Person struct {
Name string
Age int
Scores [5]float64
ptr *int //指针
slice []int //切片
mp map[string]string //map
}
func main() {
var p Person
p.Name = "黄忠"
p.Age = 34
p.Scores[0] = 12
//使用引用类型字段,要先初始化内存,才能使用,或者使用字面量方式初始化
p.ptr = new(int)
*p.ptr = 4
p.slice = []int{1, 2, 3, 4}
p.mp = make(map[string]string)
p.mp["test"] = "hello"
fmt.Println(p)
}
结构体实例初始化
初始化实例的常规方式如下:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p3 Person
p3.Name = "狄仁杰"
p3.age = 34
fmt.Println(p3)
}
上面的代码中使用了(.语法
)来给属性/字段赋值,在Go语言中这叫选择器(selector),无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的 选择器符(selector-notation) 来引用结构体的字段/属性。(注:字段即属性)
还有更简单的方式就是使用混合字面量语法(composite literal syntax)
时间间隔(开始和结束时间以秒为单位)是使用结构体的一个典型例子:
type Interval struct {
start int
end int
}
intr := Interval{0, 3} //(A)
intr := Interval{end:5, start:1} //(B)
intr := Interval{end:5} //(C)
在(A)中,值必须以字段在结构体定义时的顺序给出,& 不是必须的。(B)显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像(C)中那样。
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
//方法一:混合字面量方法,
var p Person
p = Person{Name:"黄忠", age:35}
//方法2:混合字面量方法(和上面的不同点是没有指定属性名,这里值的顺序必须按照属性顺序来写)
var p2 Person
p2 = Person{"小乔", 16}
fmt.Println(p)
fmt.Println(p2)
}
聊聊给结构体字段赋值
来看看下面的代码:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var p *Person = new(Person)
//对指针有了解的人都知道这里本来应该写成(*p).Name = "黄忠" (*p).Age = 34
//但是为什么写成p.Name和p.Age也可以呢?
//这是因为编译器在内部帮我们做了优化,在编译时会自动帮我们加上,让我们在书写代码的时候更加的方便快捷
//但是我们应当知道,这里本应该写成(*p).Name = "黄忠" (*p).Age = 34
//因为.运算符比*运算符优先级高,所以把*p用括号括起来
p.Name = "黄忠"
p.Age = 34
fmt.Println(*p)
}
结构体类型实例和指向它的指针内存布局
说明了结构体类型实例和一个指向它的指针的内存布局:
type Point struct {
x int
y int
}
使用new初始化:
作为结构体字面量初始化:
Go 语言中,结构体和它所包含的数据在内存中是以连续块的形式存在的,即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。不像 Java 中的引用类型,一个对象和它里面包含的对象可能会在不同的内存空间中,这点和 Go 语言中的指针很像。下面的例子清晰地说明了这些情况:
type Rect1 struct{Min, Max Point}
type Rect2 struct{Min, Max *Point}
结构体的方法
方法与函数及其类似,本质上是一样的,只不过方法比函数多了接收者。也就是下图中的(1)
示例:
定义了一个Person结构体,并且Person结构体定义了一个OutputName的方法,该方法属于Person结构体,该方法只能通过Person结构体的实例来调用,
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) OutputName() {
fmt.Println(p.Name)
}
func main() {
var test Person
test.Name = "黄忠"
test.OutputName()
}
面向对象
组合(继承)
Go语言实现继承的方式和其它大多数编程语言不太一样,Go原因是通过组合来实现的。比如一个人具有姓名、年龄等属性,而学生不仅具有该人类的各项属性,而且还有分数,年级等额外属性,此时就可以在学生结构体中组合人结构体来简化代码,代码如下:
package main
import "fmt"
type Person struct {
Name string //姓名
Age int //年龄
}
type Student struct {
Person
score float64 //分数
grade int //年级
}
func main() {
var stu Student
stu.Name = "黄忠" //stu.Person.Name = "黄忠"
stu.Age = 34 //stu.Person.Age = 34
stu.score = 99
stu.grade = 11
fmt.Println(stu) //输出: {{黄忠 34} 99 11}
}
对上面代码的小结:
(1) 当我们直接通过stu访问字段或方式时,执行流程如下,比如stu.Name
(2) 编译器会先看stu对应的类型有没有Name,如果有,则直接调用Student类型的Name字段
(3) 如果没有就去Student中嵌入的匿名结构体Person中有没有声明Name字段,如果有就调用,没有没有就继续查找…如果找不到就报错
(4) 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则,如果希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分,如stu.Person.Name
(5) 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。
(6) 如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字
import "fmt"
type Person struct {
Name string //姓名
Age int //年龄
}
type Student struct {
person Person //有名结构体
score float64 //分数
grade int //年级
}
func main() {
var stu Student
stu.person.Name = "黄忠"
stu.person.Age = 34
stu.score = 99
stu.grade = 11
fmt.Println(stu) //输出: {{黄忠 34} 99 11}
}
继承带来的便利
代码的复用性提高了
代码的扩展性和维护性提高了
结构体使用注意事项
结构体是用户单独定义的类型,和其它类型进行转换时,需要有完全相同的字段(名字、个数和类型),示例如下:
package main import "fmt" type A struct {
Num int
} type B struct {
Num int
} func main() { var a A
var b B b.Num = 3
a = A(b) fmt.Println(a, b)
}
结构体是值类型,在方法调用中遵守值类型的传递机制,是值拷贝传递方式
如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
package main import "fmt" type Person struct {
Name string
Age int
} func (p *Person) modifyName() {
p.Name = "小乔"
}
func main() { var test Person
test.Name = "黄忠"
test.modifyName() fmt.Println(test.Name) //输出小乔
}
Go中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型都可以有方法, 而不仅仅是struct,比如int, float64等都可以有方法
package main import "fmt" type Integer int func (i Integer) print() {
fmt.Println("i = ", i)
} func main() { var i Integer = 4
i.print()
}
方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
package main import "fmt" type Student struct {
Name string
Age int
}
//给*Student实现方法String()
func (stu *Student) String() string {
str := fmt.Sprintf("Name = [%v], Age = [%v]", stu.Name, stu.Age)
return str
} func main() { stu := Student{
Name: "Jane",
Age : 30,
} //如果实现了*Student类型的String方法,就会自动调用
fmt.Println(&stu)
}
Go语言结构的更多相关文章
- 漫谈C语言结构体struct、公用体union空间占用
先用代码说话: #include<stdio.h> union union_data0{ int a ;//本身占用4个字节 char b ;//本身占用1个字节 int c ; }; u ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
- 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符
[源码下载] 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 结构体 ...
- (转)PHP的语言结构和函数的区别
相信大家经常看到对比一些PHP应用中,说用isset() 替换 strlen(),isset比strlen执行速度快等. 例子: if ( isset($user) ) { //do some thi ...
- php入门 数据类型 运算符 语言结构语句 函数 类与面向对象
php PHP-enabled web pages are treated just like regular HTML pages and you can create and edit them ...
- Go语言结构体(struct)
Go 语言结构体 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型. 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合. 结构体表示一项记录,比如保存图 ...
- C语言结构体定义的几种方法
什么是结构体? 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类.结构体可以被声明为变量.指针或数组等,用以实现较复杂的数据 ...
- 对嵌入式开发C语言结构体的一点总结
今天冬至居然不上班,公司的良心啊!这回有心情写博客和日志了,好了,废话不多说.直接看下文: 鉴于嵌入式开发过程中,C语言结构体的使用当然是必不可少.话说,基础什么的比你会更牛逼的算法更重要,基础不牢, ...
- C语言结构体变量私有化
操作系统 : CentOS7.3.1611_x64 gcc版本 :4.8.5 问题描述 C语言结构体定义中的变量默认是公有(Public)属性,如果实现成员变量的私有(Private)化? 解决方案 ...
- 在C语言结构体中添加成员函数
我们在使用C语言的结构体时,经常都是只定义几个成员变量,而学过面向对象的人应该知道,我们定义类时,不只是定义了成员变量,还定义了成员方法,而类的结构和结构体非常的相似,所以,为什么不想想如何在C语言结 ...
随机推荐
- 阿里云SLB负载均衡与使用SSL域名证书
阿里云SLB负载均衡与使用SSL证书 1.购买两台ECS服务器,这就是后台服务器,在这两个服务器上面部署你的网站,注意网站的端口要一样:比如都是 88. 2.在阿里云控制台的菜单里找到 负载均衡,创建 ...
- go语言学习逻辑运算符if判断,iota的理解
第一天学习go语言,首先吐槽一下,配置go语言浪费了我两个小时的时间 不是在百度,就是在百度的路上,这里介绍一下我的go语言的版本和开发平台 go语言1.12版本,之前没有用过在早的版本了首先记录一下 ...
- Node.js前端程序通过Nginx部署后刷新出现404问题的解决办法
方案一 (这种方式容易被第三方劫持) location / { root /data/nginx/html; index index.html index.htm; error_page 404 /i ...
- java redispool测试类保存
这两天睡眠不足,今晚吃完饭,肚子烦躁的很,迟迟进入不了代码的状态,强打精神,又赶上处理了几个编辑器的报错,等到真正面对问题的时候又是晚上十一点, 晚上十一点是幸运点,到这个点无关问题都能解决完毕进入调 ...
- .net webapi 接收 xml 格式数据的三种情况
webapi 接收 xml 的三种方法 前段时间接到一个任务写一个小接口,要接收java端返回过来的短信xml数据. 刚拿到项目,我的第一想法是对方会以什么形式发送xml格式的数据给我呢,设想三种情况 ...
- Java第4次实训作业
编写"电费管理类"及其测试类. 第一步 编写"电费管理"类 私有属性:上月电表读数.本月电表读数 构造方法:无参.2个参数 成员方法:getXXX()方法.se ...
- select标签操作
//遍历select标签 WebElement select = driver.findElement(By.tagName("select")); List<WebElem ...
- Maven 的这 7 个问题你思考过没有?
在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见.Maven的仓库管理.依赖管理.继承和聚合等特性为项目的构建提供了一整套完善的解决方案,可以说如果你搞不懂Maven,那么一个 ...
- d3.js画折线图
下载d3.zip,并解压到网页文件所在的文件夹 windows下,在命令行进入网页文件夹,输入 python -m http.server 在浏览器中输入127.0.0.1:8000/xxx.html ...
- mysql 主从库同步
#主库修改my.ini [mysqld] server log-bin=mysql-bin binlog-do-db=demo #从库修改my.ini [mysqld] server replicat ...