Go语言之类型

简介:

Go语言是一种静态类型的编程语言,所以在编译器进行编译的时候,就要知道每个值的类型,这样编译器就知道要为这个值分配多少内存,并且知道这段分配的内存表示什么。


提前知道值的类型的好处有很多,比如编译器可以合理地使用这些值,可以进一步优化代码,提高执行的效率,减少bug等。


基本类型


基本类型是Go语言自带的类型,比如数值类型、浮点类型、字符类型以及布尔类型。它们本质上是原始类型,也就是不可改变的,所以对它们进行操作,一般都会返回一个新创建的值。所以把这些值传递给函数时,其实传递的是一个值的副本。


func main() {
    name:="张三"
    fmt.Println(modify(name))
    fmt.Println(name)
}

func modify(s string) string{
    s=s+s
    return s
}
张三张三
张三


以上是一个操作字符串的例子。通过打印的结果,可以看到,本来name的值并没有被改变,也就是说,我们传递的时一个副本,并且返回一个新创建的字符串。


因为基本类型是拷贝的值,并且在对它进行操作的时候,生成的也是新创建的值,所以这些类型在多线程里是安全的,我们不用担心一个线程的修改影响了另外一个线程的数据。


引用类型


引用类型和原始的基本类型恰恰相反,它的修改可以影响到任何引用到它的变量。在Go语言中,引用类型有切片、map、接口、函数类型以及chan


引用类型之所以可以引用,是因为我们创建的引用类型变量,其实是一个标头值,标头值里包含一个指针,指向底层的数据结构。当我们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并没有被复制传递,这也是引用类型传递高效的原因。


本质上,我们可以理解函数的传递都是值传递,只不过引用类型传递的是一个指向底层数据的指针。所以我们在操作的时候,可以修改共享的底层数据的值,进而影响到所有引用到这个共享底层数据的变量。


func main() {
    ages := map[string]int{"张三": 20}
    fmt.Println(ages)
    modify(ages)
    fmt.Println(ages)
}

func modify(m map[string]int) {
    m["张三"] = 10
}


这是一个很明显的修改引用类型的例子,函数modify的修改,会影响到原来变量ages的值。


结构类型


结构类型是用来描述一组值的,比如一个人有身高、体重、名字和年龄等本质上是一种聚合型的数据类型。


type person struct {
    age int
    name string
}


定义一个结构体的类型,要通过type关键字和类型struct进行声明,以上我们就定义了一个结构体类型person,它有agename这两个字段数据。


结构体类型定义好之后,就可以进行使用了,我们可以用过var关键字声明一个结构体类型的变量。


var p person


这种声明的方式,会对结构体person里的数据类型默认初始化,也就是使用它们类型的零值。如果要创建一个结构体变量并初始化其为零值时,这种var方式最常用。


如果我们需要指定非零值,就可以使用我们字面量方式了。


jim := person{10,"Jim"}


示例中我们就为其指定了值,注意这个值的顺序很重要,必须和结构体里声明字段的顺序一致。当然我们也可以不按顺序,但是这时候我们必须为字段指定值。


jim := person{name:"Jim",age:10}


使用冒号:分开字段名和字段值即可,这样我们就不用严格的按照定义的顺序了。


除了基本的原始类型外,结构体内的值也可以是引用类型,或者自己定义的其他类型。具体选择类型,要根据实际情况,比如是否允许修改值本身,如果允许的话,可以选择引用类型;如果不允许的话,则需要使用基本类型。


函数传参是值传递,所以对于结构体来说也不例外,结构体传递的是其本身以及里面的值的拷贝。


func main() {
    jim := person{10,"Jim"}
    fmt.Println(jim)
    modify(jim)
    fmt.Println(jim)
}

func modify(p person) {
    p.age =p.age+10
}

type person struct {
    age int
    name string
}


以上示例的输出是一样的,所以我们可以验证传递的是值的副本。如果上面的例子我们要修改age的值可以通过传递结构体的指针,我们稍微改动下例子:


func main() {
    jim := person{10,"Jim"}
    fmt.Println(jim)
    modify(&jim)
    fmt.Println(jim)
}

func modify(p *person) {
    p.age =p.age+10
}

type person struct {
    age int
    name string
}


这个例子的输出是:


{10 Jim}
{20 Jim}


非常明显的,age的值已经被改变。如果结构体里有引用类型的值,比如map,那么即使我们传递的是结构体的值副本,如果修改这个map的话,原结构的对应的map值也会被修改。这里不再写例子,大家可以验证下。


自定义类型


Go语言支持我们自定义类型,比如刚刚上面的结构体类型,就是我们自定义的类型,这也是比较常用的自定义类型的方法。


另外一个自定义类型的方法是基于一个已有的类型,就是基于一个现有的类型创造新的类型,这种也是使用type关键字。


type Duration int64


我们在使用time这个包的时候,对于类型time.Duration应该非常熟悉,它其实就是基于int64 这个基本类型创建的新类型来表示时间的间隔。


但是这里我们注意,虽然Duration是基于int64创建的,觉得它们其实一样,比如都可以使用数字赋值。


type Duration int64

var i Duration = 100
var j int64 = 100


但是本质上,它们并不是同一种类型,所以对于Go这种强类型语言,它们是不能相互赋值的。


type Duration int64

var dur Duration
dur=int64(100)
fmt.Println(dur)


上面的例子,在编译的时候,会报类型转换的异常错误。


cannot use int64(100) (type int64) as type Duration in assignment


Go的编译器不会像Java的那样,帮我们做隐式的类型转换。


有时候,大家会迷茫,已经有了int64这些类型了,可以表示,还要基于它们创建新的类型做什么?其实这就是Go灵活的地方,我们可以使用自定义的类型做很多事情,比如添加方法,比如可以更明确的表示业务的含义等



本文转自 baby神 51CTO博客,原文链接:http://blog.51cto.com/babyshen/1916879,如需转载请自行联系原作者

相关文章
|
5天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
21天前
|
Go
go语言中的数据类型
go语言中的数据类型
14 0
|
8天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
2天前
|
存储 Go
用Go语言实现一个单协程消费者模型
用Go语言实现一个单协程消费者模型
13 0
|
2天前
|
编译器 Go C语言
一文速通Go语言面向对象编程
一文速通Go语言面向对象编程
9 0
|
2天前
|
Java 编译器 Go
一文速通go语言类型系统
一文速通go语言类型系统
8 0
|
2天前
|
存储 Java 编译器
一文快速掌握Go语言切片
一文快速掌握Go语言切片
5 0
|
2天前
|
自然语言处理 安全 Java
速通Go语言编译过程
速通Go语言编译过程
15 0
|
4天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
14 1
|
5天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
13 1