视频(P1-P49):【狂神说】Go语言零基础学习视频通俗易懂
1. 安装
去 Go语言中文网下载安装包,一路下一步。然后配置环境变量 GOROOT
、GOPATH
。
GOROOT=D:\env\Go # Go的安装目录
GOPATH=D:\env\GOWorks # Go项目存储的路径
GOPATH
里面需要包含 src
, pkg
, bin
三个目录
2. Hello World
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
-
一个程序只能有一个main包,用
package
关键字 -
需要 导入
fmt
包 -
写一个main函数(主入口)
-
go run hello.go
来运行
如果在GoLand里报错,可以用命令行输入后解决
go env -w GO111MODULE=off
3. 注释
package main
import "fmt"
// 单行注释
/*
多行注释
多行注释
*/
func main() {
fmt.Println("Hello World")
}
4. 变量
变量定义:var name type
,变量会有缺省值
// var 变量名 变量类型 = 值
var name string = "kuangshen"
name = "zhangsan"
多个变量定义:
var (
name string
age int
addr string
)
fmt.Println(name, age, addr)
var a,b,c int
变量应为驼峰命名法
变量赋值
var (
name string
age int
addr string
)
name = "kuangshen"
age = 25
addr = "zhongguo"
fmt.Println(name, age, addr)
var a,b,c int
短变量声明并初始化
- 定义变量的时候必须初始化
name := "kuangshen"
age := 18
fmt.Println(name, age)
打印变量的类型:
name := "kuangshen"
age := 18
fmt.Printf("%T,%T", name, age)
获取变量的地址:
var num int
num = 100
fmt.Printf("num: %d, 内存地址: %p", num, &num)
// num: 100, 内存地址: 0xc00001c088
Go语言变量变换
var a int = 100
var b int = 200
b, a = a, b
// 200 100
匿名变量 _
package main
import "fmt"
func test() (int, int) {
return 100, 200
}
func main() {
a, _ := test() // 用 _接收,方便废弃
fmt.Println(a)
}
变量的作用域:
- 全局变量:放置在函数之外的
- 局部变量:放置在函数之内的
如果全局变量和局部变量同名,在局部变量所在作用域使用时,取到的是局部变量
5. 常量
定义方式:const name [type] = value
类型可以自动推导,无需声明类型
多个常量定义:
const a,b,c = 3.14, "abc", 110
iota,特殊常量,go语言的常量计数器
package main
import "fmt"
func main() {
const (
a = iota
b = iota
c = iota
)
fmt.Println(a, b, c)
}
// 0 1 2
第一个定义 iota,后面不赋值默认为iota,其他值同理
package main
import "fmt"
func main() {
const (
a = iota
b
c
d = "haha"
e
f = 100
g
h = iota
i
)
const (
j = iota
k
)
fmt.Println(a, b, c, d, e, f, g, h, i)
// 0 1 2 haha haha 100 100 7 8
fmt.Println(j, k)
// 0 1
}
6. 数据类型
6.1 布尔型 bool
true
or false
6.2 数字型
6.3 字符型
单引号字符,双引号字符串
7. 数据类型转换
在Go里不存在隐性类型转换,所有的转换必须是显式的
转换后的变量 := 要转换的类型(变量)
package main
import "fmt"
func main() {
a := 3
b := 5.0
fmt.Printf("%T ", a)
fmt.Printf("%T ", b)
// 需求:将int类型的a转换为 float64类型 类型转换
c := float64(a)
d := int(b)
fmt.Printf("%T ", c)
fmt.Printf("%T ", d)
}
类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将int16
转换为int32
)。当从一个取值范围较大的类型转换到取值范围较小的类型时(将int32
转换为int16
或将float32
转换为int
),会发生精度丢失(截断)的情况.
8. 算数运算符
8.1 算数运算符
重点:++、–是语句而不是表达式!
8.2 关系运算符
关系运算符表达式的结果都是bool
8.3 逻辑运算符
逻辑运算符表达式的结果都是bool
8.4 位运算
8.5 赋值运算符
9. 键盘输入输出
fmt.Scanf()
、fmt.Scan()
、fmt.Scanln()
package main
import "fmt"
func main() {
var x int
var y float64
// 定义了两个变量
fmt.Println("请输入两个数1.整数2.浮点数")
fmt.Scanln(&x, &y)
fmt.Println(x, y)
}
/*
请输入两个数1.整数2.浮点数
1 3.14
1 3.14
*/
/*
请输入两个数1.整数2.浮点数
1 3.14
1 3.14
*/
## 10. 流程控制
### 10.1 if
```go
if condition {
} else if condition2 {
} else {
}
10.2 switch
默认switch每个case是不需要break的(不像其他语句)
package main
import "fmt"
func main() {
var score int = 90
switch score {
case 90:
fmt.Println("A")
case 80:
fmt.Println("B")
case 50, 60, 7:
fmt.Println("C")
default:
fmt.Println("D")
}
// 默认为 bool = true
switch {
case false:
fmt.Println("false")
case true:
fmt.Println("true")
default:
fmt.Println("default")
}
}
// A
// true
穿透 fallthrough
package main
import "fmt"
func main() {
a := false
switch a {
case false:
fmt.Println("1. case 条件为 false")
fallthrough
case true:
fmt.Println("2. case 条件为 true")
}
}
// 1. case 条件为 false
// 2. case 条件为 true
10.3 select
10.4 channel
11. for循环
11.1 for 起始;循环条件;控制变量
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
fmt.Print(i, ",")
}
}
// 1,2,3,4,5,6,7,8,9,
11.2 for ;循环条件;
同其他语言的 while
11.3 for {}
同 for true {}
12. string的用法
Go语言中,字符串是字节的切片,是Unicode兼容的,并且UTF8
获取子字符串的长度:len(str)
string
是不能修改的
str := "hello,xuexiangban"
fmt.Println(str)
// 字符串的长度
fmt.Println("字符串的长度为: ", len(str))
// 获取指定的字节
fmt.Printf("%c\n", str[0])
两种方式遍历字符串
// str.for
// for
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i])
}
// for range
for i, v := range str {
fmt.Print(i)
fmt.Printf("%c", v)
}
13. 函数
func 函数名([参数列表]) [返回值列表] {
函数体
}
package main
import "fmt"
func main() {
printInfo()
myPrint("haha")
fmt.Println(add2(1, 2))
x, y := swap("学相伴", "狂神说")
fmt.Println(x, y)
}
// 无参无返回值
func printInfo() {
fmt.Println("printInfo")
}
// 有参的函数
func myPrint(str string) {
fmt.Println(str)
}
// 有一个返回值
func add2(a, b int) int {
return a + b
}
// 有多个返回值的函数
func swap(a, b string) (string, string) {
return b, a
}
可变参数:…
package main
import "fmt"
func main() {
fmt.Println(getSum(1, 2, 3))
}
func getSum(nums ...int) int {
sum := 0
for i := range nums {
sum += i
}
return sum
}
参数传递
- 值类型的数据:操作数据本身,
int
、string
、bool
、array
… - 引用数据类型:操作的是数据的地址:
slice
、map
、chan
值传递:传递的是数据的副本
package main
import "fmt"
func main() {
// 值传递
arr := [4]int{1, 2, 3, 4}
fmt.Println(arr)
update(arr)
fmt.Println("调用后修改的数据", arr)
}
func update(arr2 [4]int) {
fmt.Println("arr2接收时的数据", arr2)
arr2[0] = 100
fmt.Println("arr2接收后的数据", arr2)
}
/*
[1 2 3 4]
arr2接收时的数据 [1 2 3 4]
arr2接收后的数据 [100 2 3 4]
调用后修改的数据 [1 2 3 4]
*/
引用传递
切片,可以扩容的数组
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4}
update2(s1)
fmt.Println("调用后的数据", s1)
}
func update2(s2 []int) {
fmt.Println("传递的数据", s2)
s2[0] = 100
fmt.Println("修改后的数据", s2)
}
/*
传递的数据 [1 2 3 4]
修改后的数据 [100 2 3 4]
调用后的数据 [100 2 3 4]
*/
14. defer
defer语义:推迟、延迟
package main
import "fmt"
func main() {
f(" 1 ")
fmt.Print(" 2 ")
defer f(" 3 ")
fmt.Print(" 4 ")
defer f(" 5 ")
fmt.Print(" 6 ")
defer f(" 7 ")
}
func f(s string) {
fmt.Printf(" %s ", s)
}
/*
1 2 4 6 7 5 3
*/
defer如果传参,虽然延迟执行,但是传参是已经执行的状态
package main
import "fmt"
func main() {
a := 10
fmt.Println("a=", a)
defer f(a) // 此时参数已经传进去了!
a++
fmt.Println("end a=", a)
}
func f(s int) {
fmt.Printf(" %d ", s)
}
/*
a= 10
end a= 11
10
*/
15. 函数的本质
- 函数本身就是一种类型
- 函数的类型是它的函数原型
package main
import "fmt"
func main() {
fmt.Printf("%T", f1)
}
func f1(a, b int) int {
return a + b
}
/*
func(int, int) int
*/
- 函数加上() 为调用,不加() 以变量形式存在
package main
import "fmt"
func main() {
var f5 func(int, int) int
f5 = f1
fmt.Println(f5)
fmt.Println(f1)
f5(1, 2)
}
func f1(a, b int) int {
return a + b
}
/*
0x67e560
0x67e560
*/
函数在Go语言中是复合类型,可以看做是一种特殊的变量。
函数名():调用返回结果
函数名:指向函数体的内存地址,一种特殊类型的指针变量
匿名函数
package main
import "fmt"
func main() {
func(a, b int) {
fmt.Print(a, b)
fmt.Println("我是f3")
}(1, 2)
}
/*
1 2我是f3
*/
回调参数
package main
import "fmt"
func main() {
r1 := add(1, 2)
fmt.Println(r1)
r2 := oper(3, 4, add)
fmt.Println(r2)
r3 := oper(8, 4, sub)
fmt.Println(r3)
r4 := oper(8, 4, func(a int, b int) int {
if b == 0 {
fmt.Println("除数不能为0")
return 0
}
return a / b
})
fmt.Println(r4)
}
// 高阶函数
func oper(a, b int, fun func(int, int) int) int {
return fun(a, b)
}
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
/*
3
7
4
2
*/
闭包
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。这个内层函数和外层函数的局部变量,统称为闭包结构
局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁,但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
package main
import "fmt"
func main() {
r1 := increment()
fmt.Println(r1)
v1 := r1()
fmt.Println(v1)
v2 := r1()
fmt.Println(v2)
}
func increment() func() int {
i := 0
fun := func() int {
i++
return i
}
return fun
}
/*
0x102e5e0
1
2
*/
16. 泛型
16.1. 什么是泛型
package main
import "fmt"
func main() {
strs := []string{"xuexiangban", "kuangshen"}
printArray(strs)
}
// []T 形式类型, 实际类型
/*
内置的泛型类型: any 和 comparable
- any: 表示go里面所有的内置基本类型,等价于 interface{}
- comparable: 表示go里面所有内置的可比较类型:int, uint, float, bool, struct, 指针
*/
func printArray[T string | int](arr []T) {
// 类型断言 x.(T)
for _, a := range arr {
fmt.Print(a)
}
}
泛型减少重复代码并提高类型安全性。
16.2. 泛型类型
type slice[T int|float32|float64] []T
- T就是类型形参,类似占位符
- int|float32|float64 是类型约束,告诉编译器,只能接受int或float32或float64这三种类型的实参
- 中括号里的内容为类型形参列表
package main
import "fmt"
func main() {
type Slice[T int | float64 | float32] []T
var a Slice[int] = []int{1, 2, 3}
var b Slice[float32] = []float32{1, 2, 3}
var c Slice[float64] = []float64{1, 2, 3}
fmt.Print(a)
fmt.Print(b)
fmt.Print(c)
}
这里b可以赋值给a吗?不可以。
下面是map的泛型示例:
type MyMap[KEY int | string, VALUE float64] map[KEY]VALUE
var m1 MyMap[string, float64] = map[string]float64{
"go": 9.9,
"java": 9.0,
}
fmt.Print(m1)
一种特殊泛型:
type Wow[T int|string] int;
var a Wow[int] = 123 // OK
var b Wow[string] = 123 // OK
var c Wow[string] = "123" // ERROR
16.3 泛型receiver
package main
import "fmt"
type MySlice[T int | float64] []T
// 给泛型添加方法
func (s MySlice[T]) Sum() T {
var sum T
for _, v := range s {
sum += v
}
return sum
}
func main() {
var s MySlice[int] = []int{1, 2, 3, 4}
fmt.Print(s.Sum())
var f MySlice[float64] = []float64{1.0, 2.1, 3.3, 4.5}
fmt.Print(f.Sum())
}
16.4 泛型函数
package main
import "fmt"
func Add[T int | float64 | string](a T, b T) T {
return a + b
}
func main() {
res := Add[int](1, 2)
fmt.Print(res)
}
16.5 自定义泛型约束
package main
type MyInt interface {
int | int8 | int16 | int32 | int64
}
func GetMaxNum[T MyInt](a, b T) T {
if a > b {
return a
} else {
return b
}
}
func main() {
var a int = 10
var b int = 20
GetMaxNum(a, b)
}
如果我们创建了一个 int 类型的别名类型 intAAA,这时候如果MyInt里没有intAAA,就会报错。
解决方案1:把intAAA加到MyInt的定义里。
解决方案2:在int的左边加一个~