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

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

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

基本类型

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

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

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

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

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

引用类型

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

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

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

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

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

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

结构类型

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

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 灵活的地方,我们可以使用自定义的类型做很多事情,比如添加方法,比如可以更明确的表示业务的含义等等。

时间: 2024-12-17 10:59:09

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

自定义类型的学习总结

在C语言中,自定义类型是一种应用非常广泛的,典型的以结构体为例,比如你要描述一个学生,这个学生具有:姓名+年龄+性别+学号 这么几项特征,而通常我们有可能要把学生来包装成一个类型,这样就可以重复性的用这个类型来定义出不同的每个学生.而结构体的出现就可以使得C语言具有了这样能描述复杂类型的能力. 一般的自定义类型我们主要学习这么几种:结构体,枚举,联合体 知识点: >结构体类型创建 >结构体初始化 >结构体内存对齐 >位段,位段计算机大小. >枚举+联合.  首先是看看结构体,

struts2自定义类型转换器

首先,何为struts2的类型转换器? 类型转换器的作用是将请求中的字符串或字符串数组参数与action中的对象进行相互转换. 一.大部分时候,使用struts2提供的类型转换器以及OGNL类型转换机制即可满足大部分类型转换需求.如: 类User.java package models; public class User { private String username; private String password; public String getUsername() { retur

map以自定义类型当Key

关于map的定义: template < class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key,T> > > class map; 第一个template参数被当做元素的key,第二个template参数被当作元素的value.Map的元素型别Key和T,必须满足以下两个条件:1.key/value必须具备assignable(可赋值

苹果新的编程语言 Swift 语言进阶(四)--字符串和收集类型

一.字符串( String  )和字符类型(Character) 字符串是一种字符的带次序的收集类型(相当于数组),字符是字符串中的元素. 在Swift 语言中,字符串是编码独立的Unicode字符的组合,并提供相应方法来获取以各种Unicode呈现方式包含的字符. 1. 字符串定义和初始化 Swift 语言使用var或let关键字来定义一个常量字符串(不可修改)或变量字符串(可以修改).  而不是像Object C语言一样定义两种不同的类型. Swift 语言允许使用一个双引号的字符串来初始化

结构是值类型,类是引用类型的理解

首先咱放个代码,根据代码来理解 using System; namespace CSharp { public class MyClass { public int val; } struct Struct { public int val; } class Program { public static void Main(string[] args) { MyClass MyClass1=new MyClass(); MyClass MyClass2=MyClass1; MyClass1.v

网易云课堂_C语言程序设计进阶_第一周:数据类型:整数类型、浮点类型、枚举类型

C语言程序设计进阶_第一周:数据类型:整数类型.浮点类型.枚举类型 1.0数据类型 1.1整数类型 1.2浮点类型 1.3逻辑类型 1.4类型转换和条件运算 1.0数据类型 1 整数 char(%c), short, int(%d), long(%ld), long long(C99) 2 浮点数 float(%f), double(%lf), long double(C99) 3 逻辑 bool(C99) 4 指针 5 自定义类型 所表达的数的范围:char<short<int<flo

引用类型和基本包装类型的区别

引用类型和基本包装类型的区别就在于一个是即时销毁的,另一个是一直保存的. <HTML> <HEAD> <TITLE>mytext</TITLE> </HEAD> <BODY> <script type="text/javascript"> var s1= new Object("some text"); s1.color = "red"; alert(s1.col

【转】C#语言之“string格式的日期时间字符串转为DateTime类型”的方法

方法一:Convert.ToDateTime(string) string格式有要求,必须是yyyy-MM-dd hh:mm:ss ================================================ 方法二:Convert.ToDateTime(string, IFormatProvider) DateTime dt; DateTimeFormatInfo dtFormat = new System.GlobalizationDateTimeFormatInfo()

C#值类型赋值与引用类型的赋值

在C#中,数据类型大致可以分为两类,一类是值类型,一类是引用类型.初学者往往会被类型之间的相互赋值搞的很迷惑,尤其是引用类型变量的相互赋值. 现在举2个例子,来一一说明. 值类型变量的赋值: 值类型变量中保存的是实际数据,在赋值的时候只是把数据复制一份,然后赋给另一个变量. 例子1: int var1=2; int var2=var1;  //编译器会先复制var1的值,然后把它赋给var2.很明显var2的值也为2 引用类型变量的赋值:  引用类型变量中保存的是“指向实际数据的引用指针”.在进