Go语言的整个设计哲学就是:将简单、实用体现的淋漓尽致
基础语法学习 HelloWorld!
package mainimport "fmt" func main () { fmt.Println("hello world 胥天昊" ) }
注释 注释跟Java的一样
变量 package mainimport "fmt" func main () { var name string = "胥天昊" var age int = 18 var ( sex string = "男" addr string = "河南" ) var a, b, c int = 1 , 2 , 3 n1 := 10 n2 := "变量" println ("hello " + name) println (age) println (sex, addr) println (a, b, c) fmt.Printf("%T,%T" , n1, n2) }
内存地址 package mainimport "fmt" func main () { var num int = 100 fmt.Printf("%d" , num) println () fmt.Printf("%p" , &num) }
变量交换 package mainfunc main () { var a int = 100 var b int = 200 b, a = a, b println (a) println (b) }
匿名变量 package mainfunc test () (int , int ) { return 100 , 200 } func main () { a, _ := test() println (a) }
变量的作用域 —个变量(常量、类型或函数)在程序中都有一定的作用范围。称之为作用域。
了解变量的作用域对我们学习Go语言来说是比较重要的,因为Go语言会在编译时检查每个变量是否使用过 ,一旦出现未使用的变量,就会报编译错误。如果不能理解变量的作用域,就有可能会带来一些不明所以的编译错误。
package mainvar name string = "xth" func main () { var name string = "胥天昊" println (name) }
常量 不可以改变的数据
package mainfunc main () { const URL string = "www.xiaobaicai.com" const URL2 = "www.xiaobaicai.com" URL="www" }
iota iota。特殊常量 。可以认为是一个可以被编译器修改的常量。iota是go语言的常量计数器
iota在const关键字出现时将被重置为0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)
package mainfunc main () { const ( a = iota b c d = "haha" e f = 100 g h = iota i ) const ( j = iota k ) }
基本数据类型
输入 package mainimport "fmt" func main () { var x int var y float32 fmt.Scanln(&x, &y) println (x) println (y) }
容器 数组 数组 是一个由 固定长度 的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在 Go 语言中很少直接使用数组 。和数组对应的类型是 slice(切片) ,它是可以动态的增长和收缩的序列, slice 功能也更灵活,下面我们再讨论 slice 。
数组声明 可以使用 [n]Type 来声明一个数组。其中 n 表示数组中元素的数量, Type 表示每个元素的类型。
package mainimport "fmt" func test01 () { var arr [5 ]int fmt.Println(arr) arr[0 ] = 1 arr[1 ] = 2 arr[2 ] = 3 fmt.Println(arr) } func test02 () { var arr1 = [5 ]int {15 , 20 , 25 , 30 , 35 } fmt.Println(arr1) arr2 := [5 ]int {15 , 20 , 25 , 30 , 35 } fmt.Println(arr2) arr3 := [5 ]int {15 , 20 } fmt.Println(arr3) arr4 := [5 ]int {1 : 100 , 4 : 200 } fmt.Println(arr4) arr5 := [...]int {15 , 20 , 25 , 30 , 35 , 40 } fmt.Println(arr5) } func test03 () { arr1 := [3 ]int {15 , 20 , 25 } arr2 := [5 ]int {15 , 20 , 25 , 30 , 35 } fmt.Printf("type of arr1 is %T\n" , arr1) fmt.Printf("type of arr2 is %T\n" , arr2) } func test04 () { arr := [3 ][2 ]string { {"1" , "Go语言极简一本通" }, {"2" , "Go语言微服务架构核心22讲" }, {"3" , "从0到Go语言微服务架构师" }} fmt.Println(arr) } func main () { test01() test02() test03() test04() }
数组长度 使用内置的 len 函数将返回数组中元素的个数,即数组的长度。
func arrLength () { arr := [...]string {"Go语言极简一本通" , "Go语言微服务架构核心22讲" , "从0到Go语言微服务架构师" } fmt.Println("数组的长度是:" , len (arr)) }
数组遍历 使用 for range 循环可以获取数组每个索引以及索引上对应的元素。
func showArr () { arr := [...]string {"Go语言极简一本通" , "Go语言微服务架构核心22讲" , "从0到Go语言微服务架构师" } for index, value := range arr { fmt.Printf("arr[%d]=%s\n" , index, value) } for _, value := range arr { fmt.Printf("value=%s\n" , value) } }
数组是值类型 Go 中的数组是值类型而不是引用类型。当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,不会影响原始数组。
func arrByValue () { arr := [...]string {"Go语言极简一本通" , "Go语言微服务架构核心22讲" , "从0到Go语言微服务架构师" } copy := arr copy [0 ] = "Golang" fmt.Println(arr) fmt.Println(copy ) }
切片(Slice) 切片是对数组的一个连续片段的引用,所以切片是一个引用类型 。切片本身不拥有任何数据,它们只是对现有数组的引用 ,每个切片值都会将数组作为其底层的数据结构。slice 的语法和数组很像,只是没有固定长度而已。
创建切片 使用 []Type 可以创建一个带有 Type 类型元素的切片。
var arr [5 ]int var numList []int var numListEmpty = []int {}
你也可以使用 make 函数构造一个切片,格式为 make([]Type, size, cap) 。
numList := make ([]int , 3 , 5 )
当然,我们可以通过对数组进行片段截取创建一个切片。
arr := [5 ]string {"Go语言极简一本通" , "Go语言微服务架构核心22讲" , "从0到Go语言微服务架构师" , "微服务" , "分布式" } var s1 = arr[1 :4 ]fmt.Println(arr) fmt.Println(s1)
切片的长度和容量 一个 slice 由三个部分构成:指针 、 长度 和 容量 。
指针 指向第一个 slice 元素对应的底层数组元素的地址,要注意的是 slice 的第一个元素并不一定就是数组的第一个元素。
长度 对应 slice 中元素的数目;长度不能超过容量,容量一般是从 slice 的开始位置到底层数据的结尾位置。
容量 就是从创建切片索引开始的底层数组中的元素个数,而长度是切片中的元素个数。
内置的 len 和 cap 函数分别返回 slice 的长度和容量。
s := make ([]string , 3 , 5 ) fmt.Println(len (s)) fmt.Println(cap (s))
如果切片操作超出上限将导致一个 panic 异常。
s := make ([]int , 3 , 5 ) fmt.Println(s[10 ])
Tips:
切片元素的修改 切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。
func modifySlice () { var arr = [...]string {"Go语言极简一本通" , "Go语言微服务架构核心22讲" , "从0到Go语言微服务架构师" } s := arr[:] fmt.Println(arr) fmt.Println(s) s[0 ] = "Go语言" fmt.Println(arr) fmt.Println(s) }
这里的 arr[:] 没有填入起始值和结束值,默认就是 0 和 len(arr) 。
追加切片元素 使用 append 可以将新元素追加到切片上。append 函数的定义是 func append(slice []Type, elems ...Type) []Type 。其中 elems ...Type 在函数定义中表示该函数接受参数 elems 的个数是可变的。这些类型的函数被称为可变函数。
func appendSliceData () { s := []string {"Go语言极简一本通" } fmt.Println(s) fmt.Println(cap (s)) s = append (s, "Go语言微服务架构核心22讲" ) fmt.Println(s) fmt.Println(cap (s)) s = append (s, "从0到Go语言微服务架构师" , "分布式" ) fmt.Println(s) fmt.Println(cap (s)) s = append (s, []string {"微服务" , "分布式锁" }...) fmt.Println(s) fmt.Println(cap (s)) }
当新的元素被添加到切片时,如果容量不足,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回新的引用。现在新切片的容量是旧切片的两倍。
多维切片 类似于数组,切片也可以有多个维度。
func mSlice () { numList := [][]string { {"1" , "Go语言极简一本通" }, {"2" , "Go语言微服务架构核心22讲" }, {"3" , "从0到Go语言微服务架构师" }, } fmt.Println(numList) }
Map 在 Go 语言中,map 是散列表(哈希表)的引用。它是一个拥有键值对元素的无序集合 ,在这个集合中,键是唯一的,可以通过键来获取、更新或移除操作。无论这个散列表有多大,这些操作基本上是通过常量时间完成的。所有可比较的类型,如 整型 ,字符串 等,都可以作为 key 。
创建 Map 使用 make 函数传入键和值的类型,可以创建 map 。具体语法为 make(map[KeyType]ValueType) 。
scores := make (map [string ]int ) steps := make (map [string ]string )
我们也可以用 map 字面值的语法创建 map ,同时还可以指定一些最初的 key/value :
var steps2 map [string ]string = map [string ]string { "第一步" : "Go语言极简一本通" , "第二步" : "Go语言微服务架构师核心22讲" , "第三步" : "从0到Go语言微服务架构师" , } fmt.Println(steps2)
或者
steps3 := map [string ]string { "第一步" : "Go语言极简一本通" , "第二步" : "Go语言微服务架构师核心22讲" , "第三步" : "从0到Go语言微服务架构师" , } fmt.Println(steps3)
Map 操作
map 是引用类型 当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构。因此,改变其中一个变量,就会影响到另一变量。
func mapByReference () { steps4 := map [string ]string { "第一步" : "Go语言极简一本通" , "第二步" : "Go语言微服务架构师核心22讲" , "第三步" : "从0到Go语言微服务架构师" , } fmt.Println("steps4: " , steps4) newSteps4 := steps4 newSteps4["第一步" ] = "Go语言极简一本通-222" newSteps4["第二步" ] = "Go语言微服务架构师核心22讲-222" newSteps4["第三步" ] = "从0到Go语言微服务架构师-222" fmt.Println("steps4: " , steps4) fmt.Println("newSteps4: " , newSteps4) }
指针 指针也是一种类型,也可以创建变量,称之为指针变量。指针变量的类型为 *Type,该指针指向一个 Type 类型的变量。指针变量最大的特点就是存储的某个实际变量的内存地址,通过记录某个变量的地址,从而间接的操作该变量。
创建指针 创建指针有三种方法。
首先定义普通变量,再通过获取该普通变量的地址创建指针:
GO // 定义普通变量 x x := "面向加薪学习" // 取普通变量 x 的地址创建指针 p ptr := &x
先创建指针并分配好内存,再给指针指向的内存地址写入对应的值:
GO // 创建指针 ptr2 := new(string) // 给指针指向的内存地址写入对应的值 *ptr2 = "从0到Go语言微服务架构师" fmt.Println(ptr2) fmt.Println(*ptr2)
首先声明一个指针变量,再从其他变量获取内存地址给指针变量:
GO // 定义变量 x2 x2 := "Go语言微服务架构师核心22讲" // 声明指针变量 var p *string // 指针初始化 p = &x2 fmt.Println(p)
Tip:
上面举的创建指针的三种方法对学过 C 语言的人来说可能很简单,但没学过指针相关知识的人可能不太明白,特别是上面代码中出现的指针操作符 & 和 * 。
& 操作符可以从一个变量中取到其内存地址。
* 操作符如果在赋值操作值的左边,指该指针指向的变量;* 操作符如果在赋值操作符的右边,指从一个指针变量中取得变量值,又称指针的解引用。
通过下面的例子,你应该就会比较清楚的理解上面两个指针操作符了。
GO package main import "fmt" func main() { x := "面向加薪学习" ptr := &x fmt.Println("x = ", x) // x = 面向加薪学习 fmt.Println("*ptr = ", *ptr) // *p = 面向加薪学习 fmt.Println("&x = ", &x) // &x = 0x14000010290 fmt.Println("ptr = ", ptr) // p = 0x14000010290 }
指针的类型 *(指向变量值的数据类型) 就是对应的指针类型。
GO func pointerType() { mystr := "Go语言极简一本通" myint := 1 mybool := false myfloat := 3.2 fmt.Printf("type of &mystr is :%T\n", &mystr) fmt.Printf("type of &myint is :%T\n", &myint) fmt.Printf("type of &mybool is :%T\n", &mybool) fmt.Printf("type of &myfloat is :%T\n", &myfloat) } func main() { //... pointerType() }
指针的零值 如果指针声明后没有进行初始化,其默认零值是 nil
GO func zeroPointer() { x := "从0到Go语言微服务架构师" var ptr *string fmt.Println("ptr is ", ptr) ptr = &x fmt.Println("ptr is ", ptr) } func main() { //... zeroPointer() }
函数传递指针参数 在函数中对指针参数所做的修改,在函数返回后会保存相应的修改。
GO package main import ( "fmt" ) func changeByPointer(value *int) { *value = 200 } func main() { x3 := 99 p3 := &x3 fmt.Println("执行changeByPointer函数之前p3是", *p3) changeByPointer(p3) fmt.Println("执行changeByPointer函数之后p3是", *p3) }
运行程序输出如下,函数传入的是指针参数,即内存地址,所以在函数内的修改是在内存地址上的修改,在函数执行后还会保留结果。
指针与切片 切片与指针一样是引用类型,如果我们想通过一个函数改变一个数组的值,可以将该数组的切片当作参数传给函数,也可以将这个数组的指针当作参数传给函数。但 Go 中建议使用第一种方法,即将该数组的切片当作参数传给函数,因为这么写更加简洁易读。
GO package main import "fmt" // 使用切片 func changeSlice(value []int) { value[0] = 200 } // 使用数组指针 func changeArray(value *[3]int) { (*value)[0] = 200 } func main() { x := [3]int{10, 20, 30} changeSlice(x[:]) fmt.Println(x) // [200 20 30] y := [3]int{100, 200, 300} changeArray(&y) fmt.Println(y) // [200 200 300] }
Go 中不支持指针运算 学过 C 语言的人肯定知道在 C 中支持指针的运算,例如:p++ ,但这在 Go 中是不支持的。
GO package main func main() { x := [...]int{20, 30, 40} p := &x p++ // error }
流程控制 if package mainfunc main () { var num int = 85 if num >= 90 { println ("A" ) } else if num < 90 && num >= 80 { println ("B" ) } else { println ("C" ) } }
switch package mainfunc main () { var num int = 80 switch num { case 90 : println ("A" ) case 80 : println ("B" ) fallthrough case 50 , 60 , 70 : println ("C1" ) if num == 80 { break } println ("C2" ) } switch { case false : println ("false" ) case true : println ("true" ) } }
输出结果
for func main () { for i := 0 ; i < 10 ; i++ { println (i) } }
字符串 package mainfunc main () { str := "hello" println (len (str)) println (str[0 ]) println (str[1 ]) for i, v := range str { println (i, v) } }
输出结果
5 104 101 0 104 1 101 2 108 3 108 4 111
函数 func func_name(...param)(...retrun_types){ }
package mainfunc add (a, b int ) int { return a + b } func main () { println (add(1 , 2 )) }
详细说明 func f1 () { println ("1" ) } func f2 (a int ) { println (a) } func f3 (a, b int ) { println (a, b) } func f4 () int { return 1 } func f5 () (int ,int ) { return 1 , 2 }
函数注释 格式:// 函数名 注释内容
可变参数 package mainfunc main () { fn(1 , 2 , 3 , 4 ) } func fn (nums ...int ) { i := len (nums) println (i) }
参数传递 值传递 package mainimport "fmt" func main () { arr := [4 ]int {1 , 2 , 3 , 4 } fmt.Println(arr) update(arr) fmt.Println(arr) } func update (arr [4]int ) { arr[0 ] = 100 }
输出结果
引用传递 package mainimport "fmt" func main () { s := []int {1 , 2 , 3 , 4 } fmt.Println(s) sUpdate(s) fmt.Println(s) } func sUpdate (s []int ) { s[0 ] = 100 }
输出结果
总结
值类型的数据:操作的是数据本身、int. string. bool. float64. array
引用类型的数据:操作的是数据的地址slice. map. chan
延迟 defer的基本使用 package mainfunc main () { println (1 ) defer println (2 ) println (3 ) defer println (4 ) println (5 ) defer println (6 ) }
输出结果
defer中传参 package mainfunc main () { var num int = 10 defer f(num) num++ println (num) } func f (num int ) { println (num) }
输出结果:
函数的本质 package mainimport "fmt" func main () { fmt.Printf("%T\n" , func1) var func2 func (int , int ) func2 = func1 func2(1 , 2 ) } func func1 (a, b int ) { fmt.Println(a, b) }
输出结果:
匿名函数 package mainfunc main () { func () { println ("我是匿名函数" ) }() funcDemo := func () { println ("我也是匿名函数" ) } funcDemo() func (a, b int ) { println ("我是带参数的匿名函数" ) println (a, b) }(1 , 2 ) sum := func (a, b int ) int { return a + b }(1 , 2 ) println (sum) }
高阶函数 package mainfunc main () { a := operate(1 , 2 , sum) println (a) i := operate(8 , 4 , func (a, b int ) int { return a / b }) println (i) } func operate (a, b int , fun func (a, b int ) int ) int { return fun(a, b) } func sum (a, b int ) int { return a + b }
闭包结构 package mainfunc main () { method1 := increment() println (method1()) println (method1()) method2 := method1 println (method2()) println (method2()) method3 := increment() println (method3()) println (method3()) } func increment () func () int { i := 0 fun := func () int { i++ return i } return fun }