第一章 Go语言简介
- 协程是go最显著的特性
- 最好读一读go语言源码 ,项目链接:https://github.com/golang/go
- go语言只支持i++,别的写法会报错
- GOPATH 是一个路径,用来存放开发中需要用到的代码包。
- linux中需要设置 GOROOT(安装目录) 和 PATH,把安装目录追加到PATH中,export PATH=$ PATH:$ GOROOT/bin:$GOBIN
- 如果想要构建一个项目,就需要将这个项目的目录添加到 GOPATH 中,多个项目之间可以使用;分隔
- go语言项目的目录一般包含三个子目录 : src, pkg, bin, 包名与目录名应该一一对应
- Go语言会把通过go get 命令获取到的库源文件下载到 src 目录下对应的文件夹当中
- godep是一个go语言官方通过vender模式来管理第三方依赖的工具
- Go语言以“包”作为管理单位,每个 Go 源文件必须先声明它所属的包
- import 语句,用于导入程序中所依赖的包,导入的包名使用双引号""包围
- go build命令可以将Go语言程序代码编译成二进制的可执行文件
- go run命令 会在编译后直接运行Go语言程序,编译过程中会产生一个临时文件,但不会生成可执行文件,很适合用来调试程序。
第二章 GO语言基本语法
- 声明变量的一般形式是使用 var 关键字:
var a, b *int
- 当一个变量被声明之后,系统自动赋予它该类型的零值,所有的内存在 Go 中都是经过初始化的。
- 变量的命名规则遵循骆驼命名法,即首个单词小写
- 批量申明变量
var (
a int
b string
c []float32
d func() bool
e struct {
x int
}
)
- 可使用更加简短的变量定义和初始化语法
i, j := 0, 1
6.变量初始化的标准格式
//var 变量名 类型 = 表达式
var hp int = 100
- 在标准格式的基础上,将 int 省略后,编译器会尝试根据等号右边的表达式推导 hp 变量的类型。
var hp = 100
- var 的变量声明还有一种更为精简的写法
//短变量声明并初始化
//如果 hp 已经被声明过,但依然使用:=时编译器会报错
hp := 100
- 交换变量
var a int = 100
var b int = 200
b, a = a, b
10.匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
a, _ := 1, 2
_, b := 3, 4
11.函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。
12. 在函数体外声明的变量称之为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,当然,不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量。
13. 全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。
14. 一个 float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度,通常应该优先使用 float64 类型
15. 一个布尔类型的值只有两种:true 或 false
16. 可以使用双引号""来定义字符串,字符串中可以使用转义字符来实现换行、缩进等效果
var str = "GO语言从入门到放弃\nGO语言从入门到放弃"
//两个字符串 s1 和 s2 可以通过 s := s1 + s2 拼接在一起
str += "\n"
//嵌入一个多行字符串时,就必须使用`反引号
const s = `
第一行
第二行
第三行
`
- 在 ASCII 码表中,A 的值是 65,使用 16 进制表示则为 41,所以下面的写法是等效的
//(\x 总是紧跟着长度为 2 的 16 进制数)
var ch byte = 65 或 var ch byte = '\x41'
18.一个类型的值可以被转换成另一种类型的值
//类型 B 的值 = 类型 B(类型 A 的值)
//不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)
a := 5.0
b := int(a)
19.当一个指针被定义后没有分配到任何变量时,它的默认值为 nil。指针变量通常缩写为 ptr
//Go语言中使用在变量名前面添加&操作符(前缀)来获取变量的内存地址
ptr := &v // v 的类型为 T
var cat int = 1
var str string = "banana"
fmt.Printf("%p %p", &cat, &str)
20.Go语言还提供了另外一种方法来创建指针变量,格式如下:
str := new(string)
*str = "Go语言教程"
fmt.Println(*str)
21.变量逃逸分析: 通过编译器分析代码的特征和代码的生命周期,决定应该使用堆还是栈来进行内存分配。
22. 变量的生命周期与变量的作用域有着不可分割的联系
23. Go语言中的常量使用关键字 const 定义,并且只能是布尔型、数字型(整数型、浮点型和复数)和字符串型
//常量的值必须是能够在编译时就能够确定的
const pi = 3.14159 // 相当于 math.Pi 的近似值
24.iota 常量生成器: 在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。
type Weekday int
//周日将对应 0,周一为 1,以此类推。
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
//String() 方法的 ChipType 在使用上和普通的常量没有区别。
//当这个类型需要显示为字符串时,
//Go语言会自动寻找 String() 方法并进行调用。
func (c WeekDay) String() string{
switch c {
case Sunday:
return "Sunday"
}
}
- 定义类型别名的写法为
//type TypeAlias = Type
// 将NewInt定义为int类型
type NewInt int
// 将int取一个别名叫IntAlias
type IntAlias = int
- godoc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档
第三章 go语言容器
1.Go语言数组
var a [3]int // 定义三个整数的数组
fmt.Println(a[0]) // 打印第一个元素
fmt.Println(a[len(a)-1]) // 打印最后一个元素
// 打印索引和元素
for i, v := range a {
fmt.Printf("%d %d\n", i, v)
}
// 仅打印元素
for _, v := range a {
fmt.Printf("%d\n", v)
}
2.用一组值来初始化数组
var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
//在数组的定义中,如果在数组长度的位置出现“...”省略号,
//则表示数组的长度是根据初始化值的个数来计算
q := [...]int{1, 2, 3}
//数组的长度是数组类型的一个组成部分,
//因此 [3]int 和 [4]int 是两种不同的数组类型,
//数组的长度必须是常量表达式,
//因为数组的长度需要在编译阶段确定。
q := [3]int{1, 2, 3}
q = [4]int{1, 2, 3, 4} // 编译错误:无法将 [4]int 赋给 [3]int
//如果两个数组类型相同(包括数组的长度,数组中元素的类型)的情况下,
//我们可以直接通过较运算符(== 和!=)来判断两个数组是否相等
- Go语言多维数组
// 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化数组中指定的元素
array = [4][2]int{1: {0: 20}, 3: {1: 41}}
- 切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型
var a = [3]int{1, 2, 3}
fmt.Println(a, a[1:2])
// 声明字符串切片
var strList []string
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{}
//如果需要动态地创建一个切片,可以使用 make() 内建函数,
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), len(b))
//用append为切片添加元素
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
a = append([]int{0}, a...) // 在开头添加1个元素
// 在第i个位置插入x
a = append(a[:i], append([]int{x}, a[i:]...)...)
a = a[1:] // 删除开头1个元素
a = a[N:] // 删除开头N个元素
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
a = []int{1, 2, 3}
a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素
//声明一个二维切片
var slice [][]int
//为二维切片赋值
slice = [][]int{{10}, {100, 200}}
- 在使用 append() 函数为切片动态添加元素时,如果空间不足以容纳足够多的元素,切片就会进行“扩容”
- map
var mapname map[keytype]valuetype
noteFrequency := map[string]float32 {
"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
"G0": 24.50, "A0": 27.50, "B0": 30.87, "A4": 440}
//使用 delete() 函数从 map 中删除键值对
delete(map, 键)
- 当发生并发时, map会报错, sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。
- 初始化列表: 变量名 := list.New()
- 通过 var 关键字声明初始化 list : var 变量名 list.List
- 在列表中插入元素
l := list.New()
l.PushBack("fist")
l.PushFront(67)
// 尾部添加后保存元素句柄
element := l.PushBack("fist")
// 在fist之后添加high
l.InsertAfter("high", element)
// 在fist之前添加noon
l.InsertBefore("noon", element)
// 使用
l.Remove(element)
//遍历
for i := l.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
11.指针、切片、映射、通道、函数和接口的零值则是 nil。Go语言中的 nil 和其他语言中的 null 有很多不同点
//nil 标识符是不能比较的
nil == nil //报错
//nil 不是关键字或保留字
var nil = errors.New("my god")
//不同类型的 nil 值占用的内存大小可能是不一样的
var p *struct{}
fmt.Println( unsafe.Sizeof( p ) ) // 8
12.new 函数不仅仅能够为系统默认的数据类型,分配空间,自定义类型也可以使用 new 函数来分配空间
type Student struct {
name string
age int
}
var s *Student
//如果我们不使用 new 函数为自定义类型分配空间就会报错
s = new(Student) //分配空间
s.name ="dequan"
fmt.Println(s)
- make 也是用于内存分配的,但是和 new 不同,它只用于 chan、map 以及 slice 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
第四章 流程控制
- Go语言中的循环语句只支持 for 关键字,而不支持 while 和 do-while 结构
sum := 0
for i := 0; i < 10; i++ {//左花括号{必须与 for 处于同一行。
sum += i
}
//写法2
for {
sum++
if sum > 100 {
break
}
}
//初始语句可以被忽略,但是初始语句之后的分号必须要写
step := 2
for ; step > 0; step-- {
fmt.Println(step)
}
//只有一个循环条件的循环
for i <= 10 {
i++
}
- Go语言改进了 switch 的语法设计,case 与 case 之间是独立的代码块,不需要通过 break 语句跳出当前 case 代码块以避免执行到下一行
第五章 函数
- 普通函数声明(定义)
func 函数名(形式参数列表)(返回值列表){
函数体
}
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
- Go语言经常使用多返回值中的最后一个返回参数返回函数执行中可能发生的错误
//connectToNetwork 返回两个参数,conn 表示连接对象,err 返回错误信息。
conn, err := connectToNetwork()
- 函数也是一种类型,可以和其他类型一样保存在变量中
func fire() {
fmt.Println("fire")
}
func main() {
var f func()
f = fire
f()
}
- 定义一个匿名函数
func(参数列表)(返回参数列表){
函数体
}
- go语言支持闭包
- 可变参数是指函数传入的参数个数是可变的,为了做到这点,首先需要将函数定义为可以接受可变参数的类型
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
}
//任意类型的可变参数
func Printf(format string, args ...interface{}) {
// ...
}
- 当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出)
// 将defer放入延迟调用栈
defer fmt.Println(1)
defer fmt.Println(2)
// 最后一个放入, 位于栈顶, 最先调用
defer fmt.Println(3)
//输出结果是3 2 1
- 一般使用延迟执行语句在函数退出时释放资源,释放文件句柄
- 自定义一个错误
var err = errors.New("this is an error")
- 手动触发宕机, 在宕机时会触发延迟执行语句
package main
func main() {
panic("crash")
}
- 单元(功能)测试: 在同一文件夹下创建两个Go语言文件,分别命名为 demo.go 和 demt_test.go, 执行测试命令: go test -v
- 性能(压力)测试 : go test -bench="."
第六章 结构体
- 结构体的定义格式如下
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
//基本实例化格式如下
var ins T
type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
//创建指针类型的结构体
ins := new(Point)
//对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作
ins := &Point{}
- 初始化结构体的成员变量
type People struct {
name string
child *People
}
relation := &People{
name: "爷爷",
child: &People{
name: "爸爸",
child: &People{
name: "我",
},
},
}
- 使用多个值的列表初始化结构体
type Address struct {
Province string
City string
ZipCode int
PhoneNumber string
}
addr := Address{
"四川",
"成都",
610000,
"0",
}
- 初始化匿名结构体
msg := &struct { // 定义部分
id int
data string
}{ // 值初始化部分
1024,
"hello",
}
- 为结构体添加方法
type Bag struct {
items []int
}
//(b*Bag) 表示接收器,即 Insert 作用的对象实例
func (b *Bag) Insert(itemid int) {
b.items = append(b.items, itemid)
}
- 在一个结构体中对于每一种数据类型只能有一个匿名字段
type innerS struct {
in1 int
in2 int
}
type outerS struct {
b int
c float32
int // anonymous field
innerS //anonymous field
}
- 内嵌结构体的字段名是它的类型名
- 内嵌结构体实现对象特性,可以自由地在对象中增、删、改各种特性
- GC 是自动进行的,如果要手动进行 GC,可以使用 runtime.GC() 函数
来源:CSDN
作者:命中无时必强求
链接:https://blog.csdn.net/manwea/article/details/103470248