《Go语言实战》Go 类型:基本类型、引用类型、结构类型、自定义类型

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

提早知道值的类型的好处有不少,好比编译器能够合理的使用这些值,能够进一步优化代码,提升执行的效率,减小 bug 等等。数组

基本类型

基本类型是 Go 语言自带的类型,好比 数值浮点字符串布尔数组错误 类型,他们本质上是原始类型,也就是不可改变的,因此对他们进行操做,通常都会返回一个新建立的值,因此把这些值传递给函数时,其实传递的是一个值的副本。安全

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

funcmodify(sstring)string{
	s = s + s
	return s
}
张三张三
张三

以上是一个操做字符串的例子,经过打印的结果,能够看到,原本 name 的值并无被改变,也就是说,咱们传递的时一个副本,而且返回一个新建立的字符串。数据结构

基本类型由于是拷贝的值,而且在对他进行操做的时候,生成的也是新建立的值,因此这些类型在多线程里是安全的,咱们不用担忧一个线程的修改影响了另一个线程的数据。多线程

引用类型

引用类型和原始的基本类型偏偏相反,它的修改能够影响到任何引用到它的变量。在 Go 语言中,引用类型有 切片(slice)字典(map)接口(interface)函数(func) 以及 通道(chan) 。编程语言

引用类型之因此能够引用,是由于咱们建立引用类型的变量,实际上是一个标头值,标头值里包含一个指针,指向底层的数据结构,当咱们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并无被复制传递,这也是引用类型传递高效的缘由。函数

本质上,咱们能够理解函数的传递都是值传递,只不过引用类型传递的是一个指向底层数据的指针,因此咱们在操做的时候,能够修改共享的底层数据的值,进而影响到全部引用到这个共享底层数据的变量。优化

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

funcmodify(mmap[string]int) {
	m["张三"] = 10
}

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

结构类型

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

type person struct {
	age int
	name string
}

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

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

var p person

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

若是咱们须要指定非零值,就可使用咱们字面量方式了。

jim := person{10, "Jim"}

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

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

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

除了基本的原始类型外,结构体内的值也能够是引用类型,或者本身定义的其余类型。具体选择类型,要根据实际状况,好比是否容许修改值自己,若是容许的话,能够选择引用类型,若是不容许的话,则须要使用基本类型。

函数传参是值传递,因此对于结构体来讲也不例外,结构体传递的是其自己以及里面的值的拷贝。

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

funcmodify(p person) {
	p.age = p.age + 10
}

type person struct {
	age int
	name string
}

以上示例的输出是同样的,因此咱们能够验证传递的是值的副本。若是上面的例子咱们要修改 age 的值能够经过传递结构体的指针,咱们稍微改动下例子

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

funcmodify(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 灵活的地方,咱们可使用自定义的类型作不少事情,好比添加方法,好比能够更明确的表示业务的含义等等。

相关文章
相关标签/搜索