笔记:
声明变量
新手可看的声明变量方式,可能有不对的地方
var i int // 基本类型 声明不赋值,默认0值(0)
i := 1 // 基本类型 声明并赋值
var i int = 1 // 基本类型 声明并赋值
var i = 1 // 基本类型 声明并赋值
var ANode Node // 结构体 非指针,不赋值(内部的基本类型都为0值,引用类型和指针类型为nil)
var ANode Node = Node{} // 结构体 非指针,赋0值(内部的基本类型都为0值,引用类型和指针类型为nil)
ANode := Node{} // 结构体 非指针,赋0值(内部的基本类型都为0值,引用类型和指针类型为nil)
var ANodePoint = &Node{} // 结构体 指针,分配内存地址,并且该指针指向该地址,其中为0值
var ANodePoint = new(Node) // 结构体 指针,分配指针地址,并且该指针指向该地址,其中为0值
ANodePoint := &Node{} // 结构体 指针,分配指针地址,并且该指针指向该地址,其中为0值
var AMap = make(map[string]int, 5) // map 引用类型 分配内存,并提前分配长度为5的内存空间
AMap := make(map[string]int, 5) // map 引用类型 分配内存,并提前分配长度为5的内存空间
AMap := map[string]int{} // map 引用类型 赋了0值
var AMap = map[string]int{} // map 引用类型 赋了0值
var channel chan int // channel 声明不分配内存
var channel = make(chan int) // channel 分配内存
channel := make(chan int) // channel 分配内存
var slice []string // 切片 声明不赋值,为nil,只能通过append追加,或者从其他切片或数组切出
var slice []string = make([]string, 21) // 切片 声明分配内存
var slice = make([]string, 21) // 切片 声明分配内存
slice := make([]string, 21) // 切片 声明分配内存
slice := []string{"123", "123"} // 切片 声明分配内存
slice := new([]string) // 切片指针 声明分配指针内存,但切片本身为nil
var slice = new([]string) // 切片指针 声明分配指针内存,但切片本身为nil
数据类型
- 有符号整型:int、int8、int16、int32、int64
-
无符号整型:uint、uint8、uint64、uint32、uint64、uintptr 实际开发中由于编译器和计算机硬件的不同,int 和 uint 所能表示的整数大小会在 32bit 或 64bit 之间变化。
-
float32: 范围 约1.4e-45 到 约3.4e38
float64:范围约4.9e-324 到 约1.8e308 通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。
- 布尔型:bool,默认 false
- 字符/字符串:byte,string
用来表示 Unicode 字符的 rune 类型和 int32 类型是等价的,通常用于表示一个 Unicode 码点。这两个名称可以互换使用。同样,byte 和 uint8 也是等价类型,byte 类型一般用于强调数值是一个原始的数据而不是一个小的整数。
切片
切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。
与数组不同的是,无法通过切片类型来确定其值的长度。
每个切片值都会将数组作为其底层数据结构。
我们也把这样的数组称为切片的底层数组。
切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型。
这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(左闭右开的区间)。
Go语言中切片的内部结构包含地址、大小和容量,切片一般用于快速地操作一块数据集合。
从连续内存区域生成切片是常见的操作,格式如下:
slice [开始位置 : 结束位置]
语法说明如下:
- slice:表示目标切片对象;
- 开始位置:对应目标切片对象的索引;
- 结束位置:对应目标切片的结束索引。
从数组生成切片,代码如下:
四种方式
slice1 := []int{1, 2, 3}
var slice2 []int
var slice3 = make([]int, 3)
slice4 := make([]int, 3)
从数组或切片生成新的切片拥有如下特性:
- 取出的元素数量为:结束位置 - 开始位置;
- 取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取;
- 当缺省开始位置时,表示从连续区域开头到结束位置
(a[:2]);
- 当缺省结束位置时,表示从开始位置到整个连续区域末尾
(a[0:]);
- 两者同时缺省时,与切片本身等效
(a[:]);
- 两者同时为 0 时,等效于空切片,一般用于切片复位
(a[0:0])。
函数
Go 语言支持普通函数、匿名函数和闭包,从设计上对函数进行了优化和改进,让函数使用起来更加方便。
Go 语言的函数属于“一等公民”(first-class),也就是说:
- 函数本身可以作为值进行传递。
- 支持匿名函数和闭包(closure)。
- 函数可以满足接口。
函数定义:
func function_name( [parameter list] ) [return_types] {
函数体
}
- func:函数由 func 开始声明
- function_name:函数名称,函数名和参数列表一起构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为
实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:
返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
defer
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理
defer特性:
- 关键字 defer 用于注册延迟调用。
- 可以用来做资源清理。延迟函数执行时间(位置),方法return之后,返回参数到调用方法之前
- 多个defer语句,按先进后出的方式执行。
- defer语句中的变量,在defer声明时就决定了。
- 在方法结束(正常返回,异常结束)都会去调用defer声明的延迟函数,可以有效避免因异常导致的资源无法释放的问题
- defer 与recover配合可以实现异常捕获与处理逻辑
defer的用途:
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
package main
import "fmt"
func main() {
var whatever = [5]int{1,2,3,4,5}
for i := range whatever {
defer fmt.Println(i)
}
}
结构体
Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类型的值聚合成的实体,每个值都可以称为结构体的成员。
结构体成员也可以称为“字段”,这些字段有以下特性:
- 字段拥有自己的类型和值;
- 字段名必须唯一;
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
使用关键字 type 可以将各种基本类型定义为自定义类型,基本类型包括整型、字符串、布尔等。结构体是一种复合的基本类型,通过 type 定义为自定义类型后,使结构体更便于使用。
结构体的定义格式如下:
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- struct{}:表示结构体类型,
type 类型名 struct{}可以理解为将 struct{} 结构体定义为类型名的类型。
- 字段1、字段2……:表示结构体字段名,结构体中的字段名必须唯一。
- 字段1类型、字段2类型……:表示结构体各个字段的类型。
示例:
type Point struct {
X int
Y int
}
接收器
接收器的格式如下:
func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) {
函数体
}
- 接收器变量:接收器中的参数变量名在命名时,官方建议使用接收器类型名的第一个小写字母,而不是 self、this 之类的命名。例如,Socket 类型的接收器变量应该命名为 s,Connector 类型的接收器变量应该命名为 c 等。
- 接收器类型:接收器类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:格式与函数定义一致。
接收器根据接收器的类型可以分为指针接收器、非指针接收器,两种接收器在使用时会产生不同的效果,根据效果的不同,两种接收器会被用于不同性能和功能要求的代码中。
指针类型的接收器:
指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的 this 或者 self。
由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
示例:
使用结构体定义一个属性(Property),为属性添加 SetValue() 方法以封装设置属性的过程,通过属性的 Value() 方法可以重新获得属性的数值,使用属性时,通过 SetValue() 方法的调用,可以达成修改属性值的效果:
package main
import "fmt"
// 定义属性结构
type Property struct {
value int // 属性值
}
// 设置属性值
func (p *Property) SetValue(v int) {
// 修改p的成员变量
p.value = v
}
// 取属性值
func (p *Property) Value() int {
return p.value
}
func main() {
// 实例化属性
p := new(Property)
// 设置值
p.SetValue(100)
// 打印值
fmt.Println(p.Value())
}