本笔记主要是本人根据B站视频教程《8小时转职Golang工程师(如果你想低成本学习Go语言)》进行学习总结而来,也对互联网上其他的学习资料有所借鉴,如果有版权相关问题,敬请联系邮箱admin#basecat.cn!(请将#更换为@)
语言介绍
Go语言的优势
- 简单部署(可直接编译成可执行的机器码、运行时不依赖外部库、直接运行即可部署)
- 静态语言,编译时就可以检查出绝大多数隐藏的问题
- 语言层面的并发,充分利用多核
- 强大丰富的标准库、runtime系统调度机制、高效的GC垃圾回收
- 只有25个保留关键字,内嵌C语法支持,面向对象特征,跨平台开发
Go语言的明星作品
- Kubernetes
- docker
Go语言的劣势
- 不权威的包管理,大部分依赖包来自Github
- 没有泛化类型
- 所有Exception都使用Error来处理
- 对C的降级处理不够完美
Go语法
package
- package声明是常见于Java等语言的包概念,主要用于工程管理、划分单元等抽象的用途。
- 每个golang程序都必须有一个package main,并且在这个package中必须包含一个main()函数。
- 在golang工程中,同一个路径下只能存在一个package,一个package可以拆分为多个源文件。
import
- import关键字用于在程序中导入特定的package(标准库包或第三方依赖包等)
- import后面的参数是路径而非包名,只是常见的包基本都是路径名和包名一致
- import导入的package必须需要源码,否则编译器会报错。但编译器实际使用的情况会有所不同:import第三方依赖包时,编译器会主动编译其最新版本的源码并链接其产生的.a文件;而import标准库包时,编译器只会确保源码存在,不会重新编译其最新版本的源码,而是直接链接曾经已经编译好的.a文件。
特殊的import方式
- 标准方式:
import 路径
- 别名方式:
import 别名 路径
(此时别名会在当前程序里作为一个包名存在,用于替代路径下唯一的包名) - 匿名方式:
import _ 路径
(此时当前程序只会执行路径所给包的系列init函数,不能用路径所给包的函数) - 全局方式:
import . 路径
(将路径所给包中的所有函数全部导入到当前所在包中,此时在当前程序中使用路径所给包的函数时不需要使用包名.函数()
,可直接用函数()
) - 在复杂工程中不推荐使用全局方式,不利于工程管理。
- 多个包统一引用的多行写法略
括号
Go语言中的左大括号{
要求和函数名在同一行,否则会编译报错
行末分号
Go语言程序源码中每一行的末尾可以加分号也可以不加,建议不加
变量
声明变量
- 方法一:
var 变量名 变量类型
声明一个变量,数值类型变量默认初值为0(不区分变量所在作用域) - 方法二:
var 变量名 变量类型 = 初值
声明一个变量并为其赋一个初值 - 方法三:
var 变量名 = 初值
声明一个变量并为其赋一个初值,变量类型自动根据初值进行匹配 - 方法四:
变量名 := 初值
最常用的方法,省略var关键词,变量类型自动根据初值进行匹配 - 特别注意:方法四不适用于全局变量的声明
声明多个变量
- 单行写法:
var 变量名1, 变量名2, ... = 初值1, 初值2, ...
位置一一对应,变量类型根据初值自动匹配 - 多行写法略
常量与iota
- 声明常量:
const 变量名 变量类型 = 初值
- iota只用于多行常量声明,常量声明首行的iota值为0,之后每行累加1,空行也会加1
函数
多返回值
func 函数名(参数1 参数1类型, 参数2 参数2类型, ...) (返回值1类型, 返回值2类型, ...) {
...
return 返回值1, 返回值2, ...
}
带形参名称的多返回值
func 函数名(参数1 参数1类型, 参数2 参数2类型, ...) (返回值1 返回值1类型, 返回值2 返回值2类型, ...) {
...
返回值1 = 值1
返回值2 = 值2
...
return
}
init()函数
init()函数是每个package中都会有的隐藏的一个函数,运行在main()函数之前。
golang调包的执行顺序如下图所示
指针
因为是C++转Golang,指针部分略
defer
defer用来设定函数在执行完毕后要执行的操作,函数块内的defer语句序列是用栈来实现的,因此在函数执行完毕后,defer语句是按后进先出的顺序执行。值得一提的是执行顺序:defer所执行的指令会在return后才会执行。
数组与切片slice
数组声明:var myArray [10]int{0, 1, 2}
、myArray2 := [10]int{0, 1, 2}
数组长度函数:len(myArray)
数组遍历:for i := 0; i < len(myArray); i++
、for index, value := range myArray
匿名变量:_
数组传参:func proc(myArray [10]int)
此传参方式会产生数组所有值的拷贝
slice声明:
var myArray []int{0, 1, 2} //声明一个slice并初始化默认值为1、2、3,长度为3
myArray2 := []int //声明一个slice,但不给slice分配空间
myArray2 = make([]int, 3) //为这个slice开辟3个长度的内存空间,初始值均为0
var myArray3 []int = make([]int, 3) // 声明一个slice,分配3个长度的内存空间,初始化值均为0
myArray4 := make([]int, 3) // 声明一个slice,分配3个长度的内存空间,初始化值均为0,最常用
判断slice是否是空切片(长度是否为0):if myArray == nil
slice动态调整容量
var numbers = make([]int, 3, 5)
// len = 3, cap = 5, numbers = [0 0 0]
// len指示slice中的合法元素数量,cap指示最大元素数量上限
numbers = append(numbers, 1) // 向numbers切片追加一个元素1
// len = 4, cap = 5, numbers = [0 0 0 1]
numbers = append(numbers, 2)
// len = 5, cap = 5, numbers = [0 0 0 1 2]
numbers = append(numbers, 3)
// len = 6, cap = 10, numbers = [0 0 0 1 2 3]
// golang的slice动态扩容会直接将cap翻倍
// 弹幕(存疑):动态扩容实际上是新申请一块内存然后拷贝
slice的截取
s := []int{1, 2, 3} // len = 3, cap = 3
s1 := s[0:2] // [lower-bound, upper-bound]
// s1 = [1, 2]
// lower-bound默认值为0,upper-bound默认值为len(s),截取区间左闭右开
// 通过这种方式的截取,s1是s的浅拷贝,和s指向的实际切片是一致的,对一个切片的修改会同步到另一个切片上
s2 := make([]int, 3) // s2 = [0 0 0]
copy(s2, s) // s2 = [1 2 3]
// copy()函数可以实现slice的深拷贝
slice传参:func proc(myArray []int)
此传参方式可以理解为传递的是slice的头指针,类同C++的引用
弹幕(存疑):其实go的所有传参都是值传递,但slice和map数据类型的变量本身是一个指针
map
map声明:
var myMap map[int]string // key为int,value为string
myMap = make(map[int]string, 10) //在使用map之前,必须用make给map分配内存空间
myMap[1] = "java"
myMap[2] = "c++"
// map内部不按key排序,是乱序的
myMap2 := make(map[int]string)
myMap2[1] = "java"
myMap2[2] = "c++"
// 随使用动态扩容
myMap3 := map[int]string {
1 : "java"
2 : "c++"
}
判断是否是空map:if myMap == nil
map使用:
cityMap := make(map[string]string)
// 添加
cityMap["China"] = "Beijing"
cityMap["Japan"] = "Tokyo"
cityMap["USA"] = "NewYork"
// 遍历
for key, value := range cityMap {
fmt.Println("key =", key, " value =", value)
}
// 删除
delete(cityMap, "China")
// 修改
cityMap["USA"] = "Washington D.C."
// (引用)传参
func printMap(cityMap map[string]string) {
...
}
struct
type myint int // 声明一种新的数据类型myint,作为int的别名
type Book struct { // 定义一个结构体
title string
auth string
}
// 新建变量
var book1 Book
book1.title = "Golang"
book1.auth = "zhang3"
// (非引用)传参
func changeBook(book Book) {
...
}
// 引用传参用指针,用指针类型的book参数直接 book.auth = "777"
不再更新
因为我发现我跟着看的教程视频讲的太好了,推荐大家都过去看看吧
您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。