Go工程师来源:https://class.imooc.com/sale/go --ccmouse老师
做个人学习记录
导学
Go语言基础入门与编程思想
基本语法指针,值类型/引用类型切片与maperror机制panic/recover机制struct及其方法struct的内嵌interface函数式编程goroutinechannel/select并发编程模式context机制超时机制json格式处理
租车项目技术栈(DDD开发)
微服务
grpc领域划分服务领域防入侵数据一致性保证(不使用事务)Docker+k8s部署服务治理 Go主流框架、库
zapgrpc及中间件jwt的验证机制图片上传端到端三方协作 数据库
mongodb索引 *** 作的原子性基于真实mongodb的单元测试框架 中间件
rabbitmqwebsocket(与前端进行通信) 前端
typescript、css(短小篇幅)异步编程小程序开发
领域驱动的全栈开发,从前端出发开始做一个产品的原型,从这个原型开始定义我们的业务模型,才开始做后端
Go语言的安装
官网:https://go.dev/dl/国内下载:https://studygolang.com/dl国内镜像:
https://goproxy.cn/https://proxy.golang.com.cn
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
本课程使用的IDE
Goland/Intellij IDEA+Go插件 (Go语法基础及Bobby老师的电商项目)VSCode(ccmouse 老师的租辆酷车项目)
优点:免费缺点:对Go语言的支持上不如Goland。主要体现在代码重构或者对接口的支持上面
点击转到对应页面进行下载(笔者已经装好了其开发环境,大家可以网上找教程跟着安装即可,这里不做过多赘述,如有下载安装遇到问题欢迎私信我)
Goland的安装
go mod 的配置
初始化项目时选择go module的方式,再次配置了国内的镜像File Watcher 和 goimports的配置
保证我们在保存文件的时候一律去运行goimports运行方式配置
选择按照File去运行Goland的快捷键
删除行前进/后退转到定义
Go语言基本语法
变量定义
使用var关键字
go中变量定义了以后要求有一个合理的初始值,也就是ZeroValue,且定义之后必须要对变量进行使用。
C:变量定义了之后其值是不确定(未决)的
Java:null
var a,b,c bool
var s1,s2 string = "hello","world"
可放在函数内,或直接放在包内
使用var()集中定义变量
让编译器自动决定类型
var a,b,i,s1,s2 = true,false,3,"hello","world"
使用:=定义变量
a,b,i,s1,s2 := true,false,3,"hello","world"
只能在函数内使用
内建变量类型
bool,string(u) (int,int8,int16,int32,int64),uintptrbyte,rune(字符型int32)
Unicode、UTF-8、UTF-16 float32,float64,complex64(float32,float32
),complex128(float64,float64
)
复数回顾
i = − 1 \sqrt{-1} −1
复数:3+4 i
3:实部4:虚部KaTeX parse error: Undefined control sequence: \abs at position 1: \̲a̲b̲s̲{3 + 4i}= 3 2 + 4 2 \sqrt{3^2 + 4^2} 32+42 = 5
i 2 = − 1 , i 3 = − i , i 4 = 1 , . . . i^2 = -1,i^3 = -i,i^4 = 1,... i2=−1,i3=−i,i4=1,...
e i φ = c o s φ + i s i n φ e^{i\varphi}=cos\varphi + isin\varphi eiφ=cosφ+isinφ
KaTeX parse error: Undefined control sequence: \abs at position 1: \̲a̲b̲s̲{e^{i\varphi}}=…
e 0 = 1 , e i π 2 e^0 = 1, e^{i\frac{\pi}{2}} e0=1,ei2π
e i π = − 1 , e i 3 2 π = − i , e i 2 π = 1 e^{i\pi} = -1, e^{i\frac{3}{2}\pi}=-i,e^{i2\pi}=1 eiπ=−1,ei23π=−i,ei2π=1
最美公式--欧拉公式
e
i
π
=
−
1
e^{i\pi} = -1
eiπ=−1
强制类型转换
类型转换是强制的
var a,b int =3,4
var c int = math.Sqrt(a*a+b*b) ❌
var c int = int(math.Sqrt(float64(a*a + b*b))) ❓
float会导致一定的不精确性
常量的定义
const filename = "abc.txt"
const数值可作为各种类型使用
const a,b = 3,4
var c int = int(math.Sqrt(a*a + b*b))
使用常量定义枚举类型
普通枚举类型自增值枚举类型
Iota
func enums() {
const (
//一个自增值的种子
//iota 0-1-2-3...
cpp = iota
_
python
golang
javascript
)
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(cpp, javascript, python, golang)
fmt.Println(b, kb, mb, gb, tb, pb)
}
变量定义要点回顾
变量类型写在变量名之后编译器可推测变量类型没有char,只有rune(int32)
原生支持复数类型
基本控制结构
if
func bounded(v int) int {
if v > 100 {
return 100
} else if v < 0 {
return 0
}else {
return v
}
}
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(string(contents))
} else {
fmt.Println("cannot print file contents:", err)
}
if的条件里可以赋值if的条件里赋值的变量作用域就在这个if语句里
switch
func eval(a, b int, op string) int {
var result int
switch op {
case "+":
result = a + b
case "-":
result = a - b
case "*":
result = a * b
case "/":
result = a / b
default:
//报错,让程序停下来
panic("unsupported operator:" + op)
}
return result
//switch会自动break 除非使用fallthrough
}
switch后可以没有表达式
for
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
for的条件里不需要括号for的条件里可以省略初始条件、结束条件、递增表达式
//将整数转成二进制表达式
func convertToBin(n int) string {
result := ""
for ; n > 0; n /= 2 {
lsb := n % 2
result = strconv.Itoa(lsb) + result
}
return result
}
省略初始条件,相当于while
//while 死循环
func forever(){
for {
fmt.Println("abc")
}
}
//应用:goroutines
无限循环
基本语法要点回顾
for,if后面的条件没有括号if条件里也可定义变量没有whileswitch不需要break,也可以直接switch多个条件
函数
func eval(a,b int, op string) int
函数返回多个值时可以起名字
func div(a,b int)(int,int) {
return a/b,a%b
}
func div(a, b int) (q, r int) {
//建议写法
return a/b,a%b
//另外一种写法,但不建议
/*
q = a / b
r = a % b
return q, r
*/
}
仅用于非常简单的函数
对于调用者而言没有区别
函数作为参数
func apply(op func(int,int) int, a,b int) int {
fmt.Printf("Calling %s with %d, %d\n",
runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(),a,b)
return op(a,b)
}
可变参数列表
func sum(numbers ...int) int {
s := 0
for i := range numbers {
s += numbers[i]
}
return s
}
函数语法要点回顾
返回值类型写在最后面可返回多个值函数作为参数没有默认参数,可选参数
指针
var a int = 2
var pa * int = &a
*pa = 3
fmt.Println(a)
指针不能运算
只能进行指向,而不能像C/C++那样 ++
参数传递
值传递?引用传递?
//lang : C++
void pass_by_val(int a){
a++;
}
void pass_by_ref(int& a){
a++;
}
int main(){
int a = 3;
pass_by_val(a);
printf("After pass_by_val: %d\n",a);
pass_by_ref(a);
printf("After pass_by_ref: %d\n",a);
}
// Output:3 4
Go语言使用值传递?引用传递?
Go语言只有值传递一种方式func swap(a,b * int) {
*b,*a = *a,*b
}
值传递
采用指针进行传递,&a的拷贝了a的地址到
func f()
中,如果在func f()
中修改pa所指向的地址里面的内容*p=xx
,则 a的值被修改为了xx
这是一个object(对象)cache,其类型为
Cache
,将cache传递给func f()
。cache的结构其本身并不带有data,通常只是一个指向data的指针(pData)。当拷贝了一份cache到func f()
中。func f()
中的pData与。这两个pData所指向的是同一个data。拷贝的是指针,但其指向的仍旧是同一份内容。考虑:Go语言中我们自定义的一些类型,它在定义的时候要考虑到我们用它是当作指针来用还是说当作值来用。此处的cache是作为值,其可以被很安全的拷贝到其他的位置进行使用。
不能够应用的场景:除了pData之外还要维护一些状态。比如说这里的data有多个,cache中有多个数据,这种情况就不能用作值传递的一个类型。
数组、切片和容器
//map
package main
func main() {
cache := make(map[string]string)
cache["name"] = "ccmouse"
}
数组
//创建数组arr1(数组内的值为对应类型的零值)
var arr1 [5]int
//创建数组并对其进行赋值 arr2[0] = 1 ...
arr2 := [3]int{1, 2, 3}
//采用 ... 语法糖,接收可变个参数,并对其赋值
arr3 := [...]int{2, 4, 6, 8, 10}
var grid [4][5]bool
数组的遍历
//得到最大的value,和最大value所在的索引位置 i
maxi := -1
maxValue := -1
for i,v := range numbers {
if v > maxValue {
maxi, maxValue = i,v
}
}
//求出值的总和
sum := 0
for _,v := range numbers {
sum += v
}
可通过_
省略变量不仅range,任何地方都可以通过 _
省略变量如果只要i,可写成 for i := range numbers
为什么要用range
意义明确,美观C++:没有类似能力Java/Python:只能for each value,不能同时获取i,v
数组是值类型
[10]int 和[20]int是不同类型
调用func f(arr [10]int)会 拷贝 数组
在go语言中一般不直接使用数组
Slice(切片)
arr := [...]int{0,1,2,3,4,5,6,7}
//左取右不取
s := arr[2:6]
s[0] = 10
Slice本身没有数据,是对底层array的一个view(视图)arr的值变为[0 1 10 3 4 5 6 7]
Reslice
s := arr[2:6]
s = s[:3]
s = s[1:]
s = arr[:]
Slice的扩展
arr := [...]int{0,1,2,3,4,5,6,7}
s1 := arr[2:6]
s2 := s1[3:5]//不会报错,取到s1中对应位置的cap值
s1的值为[2 3 4 5],s2的值为[5 6]slice可以向后扩展,不可以向前扩展s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)Slice的实现
ptr 指向slice开头的元素len 说明slice的长度
当用 []
进行取值的时候,只能取到len里面的值,当下标
≥
\geq
≥ len会报错(下标越界) cap 代表整个array,从ptr开始到结束的整个的一个长度向Slice添加元素
s3,s4,s5的值为?arr的值为?
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
//s1 = 2 3 4 5
//s2 = 5 6
//s3 = 5 6 10
//s4 and s5: 开辟一个新的arr,将旧arr中的值拷贝到新arr中。
//s4 = 5 6 10 11
//s5 = 5 6 10 11 12
//若arr有人用则不GC,没人用就GC
//arr = 0 1 2 3 4 5 6 10
添加元素时如果超越cap,系统会重新分配更大的底层数组由于值传递的关系,必须接收append的返回值s = append(s,val)
//对slice的一些 *** 作
fmt.Println("Deleting elements from slice")
s2 = append(s2[:3], s2[4:]...)
printSlice(s2)
fmt.Println("Popping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println("Popping from back")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front, tail)
printSlice(s2)
Map
m := map[string]string {
"name": "ccmouse",
"course":"golang",
"site":"com",
"quality":"notbad",
}
map[K] V,map[K1]map[K2]V
Map的 *** 作
创建:make(map[string]int)
m := map[string]string{
"name": "ccmouse",
"course": "golang",
"site": "imooc",
"quality": "notbad",
}
m2 := make(map[string]int) // m2 == empty map
var m3 map[string]int //m3 == nil
//nil 和 empty map可以进行混用
fmt.Println(m, m2, m3)
获取元素:m[key]
key不存在时,获得Value类型的初始值(string->空串""
)
用value,ok := m[key]来判断是否存在key
用delete删除一个key
Map的遍历
使用range 遍历key,或者遍历key,value对不保证遍历顺序,如需顺序,需手动对key排序(是一个无序的字典)使用len获得元素个数
map的key
map使用哈希表,必须可以比较相等
除了slice,map,function的内建类型都可以作为key
Struct类型不包含上述字段(slice,map,function),也可作为key
例: 无重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/description/
abcabcbb -> abc
bbbbb ->b
pwwkew ->wke
自行查看leetcode的题解,略过
对于每一个字母x
lastOccurred[x]不存在,或者< start ->无需 *** 作
lastOccured[x] >= start ->更新start
更新lastOccurred[x],更新maxLength
rune相当于go的char
使用range遍历pos,rune对使用utf8.RuneCountInString获得字符数量使用len获得字节长度使用[]byte获得字节
其他字符串 *** 作
strings.xxx
Fields,Split,JoinContains,IndexToLower,ToUpperTrim,TrimRight,TrimLeft
面向对象
go语言仅支持封装,不支持继承和多态go语言没有class,只有struct
结构
结构的定义
type TreeNode struct {
Left,Right *TreeNode
Value int
}
结构的创建
root := TreeNode{value: 3}
root.left = &TreeNode{}
root.right = &TreeNode{5, nil, nil}
root.right.left = new(TreeNode)
不论地址还是结构本身,一律使用 .
来访问成员
//添加工厂函数
func createTreeNode(value int)*TreeNode {
//无&且返回->stack//&且返回 ->heap
return &TreeNode{Value: value}
//堆上分配完了之后参与垃圾回收
}
root.Left.Right = createTreeNode(2)
使用自定义工厂函数注意返回了局部变量的地址!
结构创建在堆上还是栈上?
不需要知道
为结构定义方法
func (node TreeNode) print() {
fmt.Print(node.Value)
}
显示定义和命名方法接收者
接收者
使用指针作为方法接收者
func (node *TreeNode) setValue(value int) {
node.Value = value
}
只有使用指针才可以改变结构内容
nil指针也可以调用方法!
值接收者 vs 指针接收者
要改变内容必须使用指针接收者结构过大也考虑使用指针接收者一致性:如有指针接收者,最好都是指针接受者值接收者
是go语言特有值/指针接受者均可接收值/指针
封装
名字一般使用CamerCase首字母大写:public首字母小写:private
包
每个目录一个包main包包含可执行入口为结构定义的方法必须放在同一个包内可以是不同文件
如何扩充系统类型或者别人的类型
定义别名:最简单使用组合:最常用使用内嵌:需要省下许多代码Go语言的依赖管理
依赖的概念依赖管理的三个阶段GOPATH,GOVENDOR,go mod
GOPATH
默认在~/go(unix,linux),%USERPROFILE%\go(windows)
历史:Google将20亿行代码,9百万个文件放在一个repo里
GOVENDOR
每个项目有自己的vendor目录,存放第三方库大量第三方依赖管理工具:glide,dep,go dep…
go mod
由go命令统一的管理,用户不必关心目录结构初始化:go mod init增加依赖:go get更新依赖:go get [@v…], go mod tidy将旧项目迁移到go mod: go mod init, go build ./…
更新日志
2022-04-28
对于基础知识部分进行二刷,对细节知识点进行了一定的阐释
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)