一. GO语言编程基础
1. 开发环境搭建
-
首先从网上下载go语言的编译器,我在发布这篇经验的时候go语言编译器的版本已经更新到了1.4版。根据你的系统平台下载相应的版本后,如果是压缩文件,先解压后双击运行,不是压缩文件,直接双击运行就可以了,运行后出现下面的界面,在下面界面上单击“Next”。
2. Go基础知识
//当前程序的包名 package main //导入其他的包 //注意:如果导入包之后, 未调用其中的函数或者类型将会报出编译错误 import "fmt" // 可通过 import std "fmt" 给包设置别名为std //import "os" //import "io" //import "time" //import "strings" //导入多个包的另一种方式 import ( //"fmt" //"os" //"io" //"time" //"strings" ) //常量的定义 const PI = 3.14 //多个常量的定义 /* const ( PI = 3.14 A = "zgf" B = 1 C = 3 ) */ //全局变量的声明与赋值 var name = "raymond" //多个全局变量的声明,在函数体内不可以使用此种方法 /* var ( name1 = "zgf" name2 = 1 name3 = 2 ) */ //一般类型声明 type newType int //多个类型同时声明 /* type ( newType int type1 float32 type2 byte type3 string ) */ //结构的声明 type gopher struct { } //接口的声明 type golang interface { } //由 main函数 作为程序入口点启动 //导入包之后,就可以使用格式<PackageName>.<FuncName>来对包中的函数进行调用,例如下面的fmt.Println func main() { fmt.Println("Hello, World!") fmt.Println("zgf") }
3. 类型与变量
Go基本数据类型
|
类型零值
package main import ( "fmt" "math" ) /* 类型零值: 1.零值并不等于空值,而是当变量被声明为某种类型后的默认值 2.通常情况下值类型的默认值为0,bool为false,string为空字符串 */ func main() { var a int //零值为:0 var b string //零值为:空 var c bool //零值为:false var d []int //在数组中没有指定容量,则为切片 var e [1]int //真正意义上的数组是又一个容量值的,此处数组容量设为[1],数组类型为int型的零值为:[0] var f [1]bool //bool型数组零值:false var g [1]byte //字节型数组零值:0 fmt.Println("a:",a) fmt.Println("b:",b) fmt.Println("c:",c) fmt.Println("d:",d) fmt.Println("e:",e) fmt.Println("f:",f) fmt.Println("g:",g) fmt.Println(math.MinInt8) //math包可以用来检查变量赋值是否有溢出范围 fmt.Println(math.MinInt16) fmt.Println(math.MinInt32) fmt.Println(math.MaxInt32) } //输出结果: /* a: 0 b: c: false d: [] e: [0] f: [false] g: [0] -128 -32768 -2147483648 2147483647 */
单个变量的声明与赋值
//类型别名与变量赋值 package main import "fmt" //单个变量的声明与赋值 /* 1.变量的声明格式:var <变量名称> <变量类型> 2.变量的赋值格式:<变量名称> = <表达式> 3.声明的同时赋值:var <变量名称> [变量类型] = <表达式> */ type ( 文本 string //把文本设置为string的别名 ) func main(){ var s 文本 //使用类型别名 s = "你好" fmt.Println(s) var a int //变量的声明 a = 111 //变量的赋值 var b int = 222 //变量声明的同时赋值 var c = 333 //也可以省略数据类型,由系统推断 d := 444 //变量声明与赋值的最简单写法 fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) }
多个变量的声明与赋值
//多个变量的声明与赋值 /* 1.全局变量的声明可使用 var() 的方式进行简写 2.全局变量的声明不可以省略 var,但可使用并行方式 3.所有变量都可以使用类型推断 4.局部变量不可以使用 var() 的方式简写,只能使用并行方式 */ package main import "fmt" var ( aaa = "hello" sss,bbb = 1,2 //ccc := 3 //不可以在全局变量组内使用 := 来赋值,因为冒号本身就代表var关键字,这样就重复了 ) func main(){ var a,b,c int a,b,c = 1,2,3 d := 4 //只有在方法体内可使用:= 来赋值,: 代表var关键字 var e,f,g,h = 5,6,7,8 var i,j,k,l = 9,10,11,12 i,m,n,o := 13,14,15,16 fmt.Println(aaa) fmt.Println(sss) fmt.Println(bbb) fmt.Println(a,b,c,d,e,f,g,h,i,j,k,l,i,m,n,o) } //输出结果: /* hello 1 2 1 2 3 4 5 6 7 8 13 10 11 12 13 14 15 16 */
变量的类型转换
//变量的类型转换 /* 1.Go中不存在隐式转换,所有类型转换必须显式声明 2.转换只能发生在两种相互兼容的类型之间 3.类型转换的格式:<ValueA> [:]= <TypeOfValueA>(<ValueB>) */ package main import "fmt" func main() { var a float32 = 100.12 fmt.Println(a) b := int(a) //两种类型兼容,可以转换 fmt.Println(b) //c := bool(a) //两种类型不兼容,报错:cannot convert a (type float32) to type bool //d := string(a) //两种类型不兼容,报错:cannot convert a (type float32) to type string } //输出结果: /* 100.12 100 */
package main import ( "fmt" "strconv" ) func main() { var a int = 65 b := string(a) //string()表示将数据转换成文本格式,因为计算机中存储的任何东西;本质上都是数字,因此此函数自然地认为我们需要的是用数字65表示的文本A c := strconv.Itoa(a) //将整型65转换成字符型的65 a, _ = strconv.Atoi(c) //将字符型65转换成整型的65 fmt.Println(b) fmt.Println(c) fmt.Println(a) } //输出结果: /* A 65 65 */
4. 常量与运算符
常量的初始化
//常量的定义 /* 1.常量的值在编译时就已经确定 2.常量的定义格式与变量基本相同 3.等号右侧必须是常量或者常量表达式 4.常量表达式中的函数必须是内置函数 */ package main import "fmt" //定义单个常量 const a int = 12 const b = \'A\' const ( text = "123" length = len(text) num = b * 23 c = d d = a+1 e = a+2 f,g = 3,"4" h,i //h,i套用了上面f,g 的常量表达式格式后,则f,g的常量值分别赋值于h,i ) //同时定义多个常量 const j,k ,l = 1,"2","3" const ( text1 = "456" length1 = len(text) num1 = b * 24 ) func main() { fmt.Println(a,b,c,d,e,f,g,h,i,j,k,l,text,length,num,text1,length1,num1) }
//常量的初始化规则与枚举 package main import "fmt" //常量名一般大写 //在定义常量组时,如果不提供初始值,则表示将使用上行的表达式 const ( a = 1 b c d,e = 3,"4" f,g ) //iota是常量的计数器,从0开始,组中每定义1个常量自动递增1,第一个常量从0计数开始 const ( a1 = "A" b1 c1 = iota //此处iota的值为2,因为已经定义了3个常量(包括本身),所以c1值是2 d1 //d1值是3 ) //通过初始化规则与iota可以达到枚举的效果 const ( Sunday = iota //第一个常量不可省略表达式 Monday Tuesday Wednesday Thursday Friday Saturday ) //每遇到一个const关键字,iota就会重置为0 const ( e1 = iota //定义了一个常量,e1值为0 f1 //所以f1值为1 ) func main() { fmt.Println(a,b,c,d,e,f,g) fmt.Println(a1,b1,c1,d1,e1,f1) fmt.Println(Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday) } //输出结果: /* 1 1 1 3 4 3 4 A A 2 3 0 1 0 1 2 3 4 5 6 */
运算符
//运算符 /* Go中的运算符均是从左至右结合 优先级(从高到低) ^ ; ! (一元运算符) *; /; %; << >>; &; &^ + ; -; |; ^ (二元运算符) ==; != ; < ; <= ; >= ; > <- (专门用于channel) && 如果左边的条件不成立,那么右边的表达式不再执行;GO语言中一般使用&&,而不使用& || */ package main import "fmt" func main() { //一元运算 fmt.Println(^1) fmt.Println(!true) fmt.Println("--------------------") fmt.Println(2*4) fmt.Println(34/2) fmt.Println(7%2) fmt.Println(1<<10) fmt.Println(1>>10) fmt.Println("--------------------") /* 6: 0110 11: 1011 ---------------- //运算后再输出十进制 & 0010 = 2 // 两个值同时为真,则为真 | 1111 = 15 // 两个值有一个为真,则为真 ^ 1101 = 13 // 两个值一个为真,另一个为假;则运算后为真;同时为真或假,均为假 &^ 0100 = 4 // 第二个值为1时,则把第一个值改为0;为0时,则为1 */ fmt.Println(6&11) fmt.Println(6|11) fmt.Println(6^11) fmt.Println(6&^11) fmt.Println("--------------------") //运算符&&的用法 a := 1 if a>0 && (10/a)>2{ fmt.Println("ok") } }
作业:请尝试结合常量iota与 <<运算符实现计算机存储单位的枚举
//请尝试结合常量的iota与<<运算符实现计算机储存单位的枚举 package main import "fmt" const ( _ = iota KB float64 = 1 << (iota * 10) MB GB TB PB EB ZB YB ) func main() { fmt.Println(KB) fmt.Println(MB) fmt.Println(GB) fmt.Println(TB) fmt.Println(PB) fmt.Println(EB) fmt.Println(ZB) fmt.Println(YB) } //输出结果: /* 1024 1.048576e+06 1.073741824e+09 1.099511627776e+12 1.125899906842624e+15 1.152921504606847e+18 1.1805916207174113e+21 1.2089258196146292e+24 */
5. 控制语句
指针
//指针 /* Go虽然保留了指针,但与其它编程语言不同的是,在Go当中不 支持指针运算以及”->”运算符,而直接采用”.”选择符来操作指针 目标对象的成员 1.操作符”&”取变量地址,使用”*”通过指针间接访问目标对象 2.默认值为 nil 而非 NULL */ package main import "fmt" func main() { a := 1 a++ //在Go当中,++ 与 -- 是作为语句,而并不是作为表达式 var P *int = &a //&a 表示变量a的地址 fmt.Println(P) //输出P指针指向a的地址 fmt.Println(*P) //输出P指针指向a的值 a-- fmt.Println(*P) } //输出结果: /* 0xc042050080 2 1 */
判断语句if-else
//判断语句if-else /* 1.条件表达式没有括号 2.支持一个初始化表达式(可以是并行方式) 3.左大括号必须和条件语句或else在同一行,{}也支持单行模式 4.初始化语句中的变量为block级别,同时隐藏外部同名变量 */ package main import "fmt" func main() { a := true // if 变量初始化;条件语句 , 可同时进行;也可以只写条件语句 if a,b,c := 1,2,3;a+b+c > 6{ // 条件表达式没有括号,a,b,c的作用域仅在if-elae语句块中,在该语句块之外无法使用 fmt.Println("大于6") // if-else语句块中的a会覆盖语句块之外的a }else { fmt.Println("小于等于6") fmt.Println(a) // 只输出if-else语句块中的a } fmt.Println(a) // 输出if-else语句块之外的a } //输出结果: /* 小于等于6 1 true */
for循环语句
1.Go只有for一个循环语句关键字,但支持3种形式
2.初始化和步进表达式可以是多个值
3.条件语句每次循环都会被重新检查,因此不建议在条件语句中使用函数,尽量提前计算好条件并以变量或常量代替
4.左大括号必须和条件语句在同一行
for循环语句形式一
// for循环语句形式一 package main import "fmt" func main() { a := 1 for{ // 无条件语句的for循环 a++ if a > 3{ break } fmt.Println(a) } fmt.Println("for over") fmt.Println(a) } //输出结果: /* 2 3 for over 4 */
for循环语句形式二
// for循环语句形式二 package main import "fmt" func main() { a := 1 for a <= 3{ //带有条件语句的for循环 a++ fmt.Println(a) } fmt.Println("for over") fmt.Println(a) } //输出结果: /* 2 3 4 for over 4 */
for循环语句形式三
package main import "fmt" func main() { a := 1 for i := 0; i < 3; i++{ // 经典形式:带有步进表达式的for循环 a++ fmt.Println(a) } fmt.Println("for over") fmt.Println(a) } //输出结果: /* 2 3 4 for over 4 */
for循环条件语句中需要注意的用法
package main import "fmt" func main() { a := "string" l := len(a) // 一般是把函数的值赋予一个变量,然后用于for循环的条件语句中 //for i := 0; i < len(a); i++{ //最好不要在for循环的条件语句中使用函数,函数内部复杂会很耗时 for i := 0; i < l; i++{ fmt.Println(i) } fmt.Println("for over") fmt.Println(a) } //输出结果: /* 0 1 2 3 4 5 for over string */
switch语句
1.可以使用任何类型或表达式作为条件语句
2.不需要写break,一旦条件符合自动终止
3.如希望继续执行下一个case,需使用fallthrough语句
4.支持一个初始化表达式(可以是并行方式),右侧需跟分号
5.左大括号必须和条件语句在同一行
switch语句形式一
//switch语句形式一 package main import "fmt" func main() { a := 1 switch a { case 0: //case语句块为true,则执行该代码块;为false则跳过该代码块 fmt.Println("a=0") case 1: fmt.Println("a=1") default: fmt.Println("None") } fmt.Println(a) } //输出结果 /* a=1 1 */
switch语句形式二
//switch语句形式二 package main import "fmt" func main() { a := 1 switch { case a >= 0: //如果此处条件表达式为true,则执行该代码块后自动跳出switch语句块,不再执行下一个case语句块 fmt.Println("a=0") fallthrough //加上关键字fallthrough后,则第一个case语句执行后,继续检查第二个case条件语句;为true时,继续执行 case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } fmt.Println(a) } //输出结果: /* a=0 a=1 1 */
switch语句形式三
//switch语句形式三 package main import "fmt" func main() { switch a := 1; { case a >= 0: fmt.Println("a=0") fallthrough case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } //fmt.Println(a) 此处报错,switch语句块之外没有定义变量a } //输出结果: /* a=0 a=1 */
跳转语句goto, break, continue
1.三个语法都可以配合标签使用
2.标签名区分大小写,若不使用会造成编译错误
3.Break与continue配合标签可用于多层循环的跳出
4.Goto是调整执行位置,与其它2个语句配合标签的结果并不相同
break和goto的用法
package main import "fmt" func main() { LABEL1: for { LABEL2: for i := 0; i < 10; i++ { if i > 3 { break LABEL2 // 跳出到 LABEL2 ;进入外层的无限循环 } fmt.Println("label2") //没有LABEL1的情况下,此处无限循环输出 break LABEL1 //跳出到LABEL1最外层循环,继续执行下面的代码 //goto LABEL1 重新调整到LABEL1的位置,继续执行下面的for循环语句,goto下面的代码永远不会被执行 } } fmt.Println("label1") } //输出结果: /* label2 label1 */
continue的用法
//continue的用法 package main import "fmt" func main() { LABEL1: for i := 0; i < 3; i++ { fmt.Println("hello") //此处循环输出3次 for { fmt.Println("inside1") //此处循环输出3次 continue LABEL1 //终止当前循环,跳转到LABEL1继续执行下面的循环语句 //goto LABEL1 //运行到此处,立即调整到LABEL1重新执行,无限循环上面的输出语句 fmt.Println("inside2") //此处永远不会被执行 } } fmt.Println("outside") } //输出结果: /* hello inside1 hello inside1 hello inside1 outside */
6. 数组array
数组基础知识
package main import ( "fmt" ) func main() { var a [5]int //定义了一个长度为5的整型数组,默认值为0 a1 := [5]int{1, 2, 3,} //定义数组并给出了数组元素,未给出的元素默认是0值 a2 := [10]int{2: 4, 9: 1} //为数组索引为2,9的元素分别赋值为4,1;其他默认为零值 a3 := [...]int{1, 2, 3, 4, 5} //可用三个点号代表数组长度,进行自动计算出元素值 a4 := [...]int{2: 4, 9: 1} //等价于上面的a2数组,此种写法会自动创建一个:最大索引值+1 长度的数组 fmt.Println(a, a1, a2, a3, a4) var b [2]string //默认值为空 b1 := [3]string{"aaa", "bbb", "ccc"} fmt.Println(b, b1) c := [...]int{9: 1} var p *[10]int = &c //指针p取数组c5的地址 fmt.Println(p) x, y := 1, 2 d := [...]*int{&x, &y} //d是指向int型的指针,此处d保存了变量x,y的地址,并没有保存变量的值 fmt.Println(d) p1 := new([10]int) //可以用new声明指向数组的指针,与上面c数组功能相同 fmt.Println(p1) a5 := [10]int{} a5[1] = 2 //数组对单个元素进行赋值操作 fmt.Println(a5) p2 := new([10]int) p2[1] = 2 //指针数组也可以对单个元素进行赋值操作 fmt.Println(p2) a6 := [2][3]int{ //定义一个多维数组a6,包括2个元素,每个元素的长度为3,也就是里面又有三个元素 {1, 2, 3}, {4, 5, 6}} fmt.Println(a6) a7 := [2][3]int{ //给多维数组赋值,顶级元素[2]可以用[...]表示,但二级元素不可以;可以写为:a7 := [...][3]int {1:2}, {2:3}} fmt.Println(a7) e := [2]int{1, 2} //数组之间可以使用==或!=进行比较,但不可以使用 < 或 > 做比较 f := [2]int{1, 2} fmt.Println(e == f) //输出true e1 := [2]int{1, 2} f1 := [2]int{1, 3} //f2 := [1]int{1} fmt.Println(e1 == f1) //输出false //fmt.Println(e1 == f2) //报错,数组长度也是类型的一部分,所以两个不同类型做比较会报错 } //输出结果: /* [0 0 0 0 0] [1 2 3 0 0] [0 0 4 0 0 0 0 0 0 1] [1 2 3 4 5] [0 0 4 0 0 0 0 0 0 1] [ ] [aaa bbb ccc] &[0 0 0 0 0 0 0 0 0 1] [0xc042008140 0xc042008148] &[0 0 0 0 0 0 0 0 0 0] [0 2 0 0 0 0 0 0 0 0] &[0 2 0 0 0 0 0 0 0 0] [[1 2 3] [4 5 6]] [[0 2 0] [0 0 3]] true false */
go语言版冒泡排序
//go语言版冒泡排序 package main import "fmt" func main() { a := [...]int{5, 2, 6, 3, 9} fmt.Println("排序前:",a) num := len(a) for i := 0; i < num; i++ { for j := i + 1; j < num; j++ { if a[i] < a[j] { temp := a[i] a[i] = a[j] a[j] = temp } } } fmt.Println("排序后:",a) } //输出结果: /* 排序前: [5 2 6 3 9] 排序后: [9 6 5 3 2] */
7. 切片slice
// 切片slice,本质上是一个动态数组,注意两者初始化和函数参数的区别 package main import "fmt" func main() { //初始化:数组需要指定大小,不指定也会根据初始化给出的值自动推算出大小,不可改变 a1 := [...]int{1, 2, 3} a2 := [3]int{4, 5, 6} fmt.Println(a1, a2) fmt.Println("-----------------------------") //切片不需要指定大小 var slice []int //定义一个空的切片,和空的数组写法相同 s1 := []int{1, 2, 3} s2 := make([]int, 10) //切片的标准定义法,使用make() s3 := make([]int, 5, 10) //第一参数表示类型,第二参数表示切片长度,第三参数表示切片最大容量;容量不够时,系统翻倍(10*2)重新分配内存容量 fmt.Println(s3, len(s3), cap(s3), cap(s2)) // 空的切片默认为0值;输出切片的长度和容量,如果不设置容量,则默认为元素的长度;例s2 fmt.Println("-----------------------------") s4 := [10]int{9: 23} //对索引为9的元素赋值为23 s5 := s4[9] //取单个索引的元素 var s6 []int = []int{1, 2, 3, 4, 5} //经典示例:s6切片无需指定大小,直接可与右边切片做比较 s7 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} s8 := s7[5:10] //取索引为5到10的元素;注意:此处包含起始索引,不包含终止索引 s9 := s7[5:] //输出切片中第5到最后一个元素 s10 := s7[:5] //输出切片中第1个至第5个元素 fmt.Println(slice) fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(s4) fmt.Println(s5) fmt.Println(s6) fmt.Println(s7) fmt.Println(s8) fmt.Println(s9) fmt.Println(s10) } //输出结果: /* [1 2 3] [4 5 6] ----------------------------- [0 0 0 0 0] 5 10 10 ----------------------------- [] [1 2 3] [0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0 0 0 0 0 23] 23 [1 2 3 4 5] [1 2 3 4 5 6 7 8 9 10] [6 7 8 9 10] [6 7 8 9 10] [1 2 3 4 5] */
reslice、append和copy
// reslice 对slice某一部分的截取称为reslice /* 1.reslice时索引以被slice的切片为准 2.索引不可以超过被slice的切片的容量cap()值 3.索引越界不会导致底层数组的重新分配而是引发错误 */ // append /* 1.可以在slice尾部追加元素 2.可以将一个slice追加在另一个slice尾部 3.如果最终长度未超过追加到slice的容量则返回原始slice 4.如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据 */ package main import "fmt" func main() { s := make([]int, 3, 6) fmt.Println("定义3个元素的切片s1:", s, len(s), cap(s)) s = append(s, 1, 2, 3) // 向s1追加3个元素 fmt.Println("追加3个元素:", s, len(s), cap(s)) //长度变为6,容量未超出,不需重新分配 s = append(s, 4, 5, 6) //再追加3个元素 fmt.Println("再追加3个元素:", s, len(s), cap(s)) //长度变为9,容量超出原有范围,重新翻倍分配内存容量 a := []int{1, 2, 3, 4, 5} s1 := a[2:5] //输出第3个元素到第5个元素,不包含2 s2 := a[1:3] //输出第2个元素到第3个元素,不包含1 fmt.Println(s1, s2) s1[0] = 9 //此处的改变同时指向s1,s2底层的数组元素3,重新赋值为9 fmt.Println(s1, s2) s3 := append(s2, 4, 5, 6, 7, 8, 9) //如果追加的元素超过最大容量,则底层数组重新分配空间; s1[0] = 23 //则给一个数组的元素重新赋值,另一个相同元素不会再改变 fmt.Println(s1,s3) } //输出结果: /* 定义3个元素的切片s1: [0 0 0] 3 6 追加3个元素: [0 0 0 1 2 3] 6 6 再追加3个元素: [0 0 0 1 2 3 4 5 6] 9 12 [3 4 5] [2 3] [9 4 5] [2 9] [23 4 5] [2 9 4 5 6 7 8 9] */
// copy package main import "fmt" func main() { s1 := []int{1,2,3,4,5,6,7,8,9} s2 := []int{7,8,9} copy(s1,s2) //将s2数组copy到s1,所以覆盖掉前面的元素,和后面的元素组合成新的数组 fmt.Println("s2->s1:",s1) s3 := []int{0,0,0,11,22,33} copy(s3,s1) //将s1数组copy到s3,剩余的元素都丢弃 fmt.Println("s1->s3:",s3) copy(s3,s1[1:3]) fmt.Println(s1) fmt.Println(s3) } //输出结果: /* s2->s1: [7 8 9 4 5 6 7 8 9] s1->s3: [7 8 9 4 5 6] [7 8 9 4 5 6 7 8 9] [8 9 9 4 5 6] */
package main import "fmt" func main() { s1 := []int{1, 2, 3, 4, 5, 6} s2 := []int{7, 8, 9, 10, 1, 1, 1, 1, 1, 1} copy(s2, s1[1:3]) // 表示把s1中索引为1--2的元素2,3,复制到s2的起始位置 fmt.Println(s2) copy(s2[2:4], s1[1:3]) //表示把s1中索引为1--2的元素2,3复制到s2中索引为2,3的元素9,10 fmt.Println(s2) } //输出结果: /* [2 3 9 10 1 1 1 1 1 1] [2 3 9 10 1 1 1 2 3 1] */
两个slice的完全copy
// 完全拷贝 package main import "fmt" func main() { s1 := []int{1, 2, 3, 4, 5} s2 := s1[0:5] //将s1完全拷贝到s2 s3 := s1[:] //省略起始位置和终止位置,默认也是将s1全部拷贝给s2 fmt.Println(s2) fmt.Println(s3) } //输出结果: /* [1 2 3 4 5] [1 2 3 4 5]
*/
8. map
1.类似其它语言中的哈希表或者字典,以key-value形式存储数据
2.Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice
3.Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍
4.Map使用make()创建,支持 := 这种简写方式
5.make([keyType]valueType, cap),cap表示容量,可省略
6.超出容量时会自动扩容,但尽量提供一个合理的初始值
7.使用len()获取元素个数
8.键值对不存在时自动添加,使用delete()删除某键值对
9.使用 for range 对map和slice进行迭代操作
创建map的多种形式:
//第一种形式 package main import "fmt" func main() { var m map[int]string //map的声明:int是key的类型,string是value的类型 m = map[int]string{} //必须用var关键字声明后,此处才可创建map;必须与上一行联合使用 fmt.Println(m) }
//第二种形式 package main import "fmt" func main() { var m map[int]string m = make(map[int]string) //必须用var关键字声明后,此处才可创建map;必须与上一行联合使用 fmt.Println(m) }
//第三种形式 package main import "fmt" func main() { var m map[int]string = make(map[int]string) //仅一行创建map fmt.Println(m) }
//第四种形式 package main import "fmt" func main() { m := make(map[int]string) fmt.Println(m) }
简单map的操作:
//简单map的操作 package main import "fmt" func main() { m := make(map[int]string) //创建map m[1] = "OK" //把“OK”存入key为1的键值中 //delete(m,1) //把m中键值为1的值删掉,那么下面输出为空 a := m[1] //把键值赋值给a,如果没有value值,则输出为空 fmt.Println(a) } //输出结果:OK
复杂map的操作:
//复杂map的操作一 package main import "fmt" func main() { var m map[int]map[int]string m = make(map[int]map[int]string) //只对外层map进行了初始化,第二层map未被初始化 m[1] = make(map[int]string) //对第二层map中key为1的键值进行初始化,否则无法输出结果 //m[2] = make(map[int]string) //对第二层map中key为2的键值进行初始化,否则无法输出结果;必须对不同的键值都初始化,才可输出结果;否则报错 m[1][1] = "OK" //m[2][1] = "OK" //对键值为2的赋值 a := m[1][1] //a := m[2][1] fmt.Println(a) }
//复杂map的操作二 package main import "fmt" func main() { var m map[int]map[int]string m = make(map[int]map[int]string) a := m[2][1] //返回空值,表示未被初始化;但此种方法不建议使用,也可能初始化为空值 fmt.Println(a) }
//复杂map的操作三 package main import "fmt" func main() { var m map[int]map[int]string m = make(map[int]map[int]string) //当有多个嵌套map时,每一级都要make初始化,否则会报错 a, ok := m[2][1] //第一个返回值为value值,第二个返回值为bool值,表示是否初始化;true:初始化,键值对存在;false:未初始化,键值对不存在 fmt.Println(a,ok) //未初始化,ok输出:false if !ok { m[2] = make(map[int]string) } m[2][1] = "GOOD" a,ok = m[2][1] fmt.Println(a, ok) //初始化,ok输出true } //输出结果: /* false GOOD true */
使用for range对map和slice进行迭代操作
//使用for range对map和slice进行迭代操作 package main func main() { for i, v := range slice { // i返回slice的索引值,v返回slice的元素值 } for k, v := range map{ // k返回map的键值,v返回map的value值 } }
//示例1:未初始化 package main import "fmt" func main() { sm := make([]map[int]string,5) for _,v := range sm { v = make(map[int]string,1) v[1] = "OK" fmt.Println(v) } fmt.Println(sm) } //输出结果: /* map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[] map[] map[] map[] map[]] */
//示例2:初始化map package main import "fmt" func main() { sm := make([]map[int]string, 5) for i := range sm { sm[i] = make(map[int]string, 1) sm[i][1] = "OK" fmt.Println(sm[i]) } fmt.Println(sm) } /* map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]] */
//对无序的map进行排序 package main import ( "fmt" "sort" ) func main() { m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "e"} s := make([]int, len(m)) i := 0 for k,_ := range m { s[i] = k i++ } sort.Ints(s) //加上sort排序后,每次运行都是有序的,不加则每次运行都是无序输出 fmt.Println(s) }
9. 函数function
- 函数的基本操作
// 函数function package main import "fmt" func main() { } func A(a int, b string) int { //定义函数A,两个参数int型的a,string型的b; 函数返回值为int型,单个返回值不需要括号 } func B(a int, b string) (int, string, int) { //同上,函数返回值为三个,用括号括起来 } func C(a, b, c int) (a, b, c int) { //表示三个参数均为int型,返回值也均为int型 a,b,c = 1,2,3 //此处不需要 := 为三个变量赋值,因为在命名返回值时三个变量已经定义 return //因为函数C已经命名返回值a,b,c ;所以此处不需要跟返回值,默认为上面的a,b,c } func D() (int,int,int) { //如果函数没有返回值,下面return必须跟上三个返回值 a,b,c := 1,2,3 return a,b,c }
- 不定长变参的操作
//不定量变参 a ...int 的操作 package main import "fmt" func main() { A("ok",2,3,4,5) } func A(b string,a ...int) { //此处的a ...int参数在接收一系列实参传递后变为一个slice;是一个不定长变参,后面不可以再加任何参数;但前面可以加参数 fmt.Println(a,b) } //输出结果: /* [1 2 3 4 5] ok */
- 匿名函数
//匿名函数 package main import "fmt" func main() { a := func() { fmt.Println("无函数名的称为:匿名函数") } a() }
- 闭包函数
//闭包函数 package main import "fmt" func main() { f := closure(10) fmt.Println(f(1)) fmt.Println(f(2)) } func closure(x int) func(int) int { //地址相同,表示调用的同一个x,而不是值的拷贝 fmt.Println(&x) return func(y int) int { fmt.Println(&x) return x + y } } //输出结果: /* 0xc042050080 0xc042050080 11 0xc042050080 12 */
- defer函数
//defer /* 1.defer的执行方式类似其它语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行 2.即使函数发生严重错误也会执行 3.支持匿名函数的调用 4.常用于资源清理、文件关闭、解锁以及记录时间等操作 5.通过与匿名函数配合可在return之后修改函数计算结果 6.如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址 7.Go 没有异常机制,但有 panic/recover 模式来处理错误 8.Panic可以在任何地方引发,但recover只有在defer调用的函数中有效 */ package main import "fmt" func main() { A() B() C() } func A() { fmt.Println("A") } func B() { defer func() { if err := recover();err != nil{ //recover只有在defer调用的函数中有效,此处用来回复panic语句中的错误机制 fmt.Println("Recover in B") } }() panic("panic in B") //Panic可以在任何地方引发错误机制,执行到此处,下面的代码不再执行 } func C() { fmt.Println("C") } //输出 结果: /* A Recover in B C */
10. 结构struct
- struct结构的基本操作
//结构struct的基本操作 package main import "fmt" type person1 struct { //定义一个结构体person1 Name string Age int } type person2 struct { //定义一个结构体person2 Name string Age int } func main() { a := person1{ } b := person2{ Name: "zgf", //在结构体里面初始化属性值,末尾加逗号;在go语言中常用此方法进行初始化 Age: 23, } a.Name = "raymond" //调用结构,a相当于由类创建的对象;由对象调用结构中的属性并赋值 a.Age = 35 fmt.Println(a, b) //没调用结构中属性时,输出int型age的默认值{0}, A(b) //调用函数A输出的结果 B(b) fmt.Println("b->",b) } func A(per person2) { per.Age = 13 fmt.Println("A->",per) } func B(per person2) { per.Age = 15 fmt.Println("A->",per) } //输出结果: /* {raymond 35} {zgf 23} A-> {zgf 13} A-> {zgf 15} b-> {zgf 23} */
- struct结构中指针、地址的操作
//结构struct中指针、地址的操作 package main import "fmt" type person1 struct { //定义一个结构体person1 Name string Age int } type person2 struct { //定义一个结构体person2 Name string Age int Addr string } func main() { a := person1{ } b := &person2{ //person2加上取地址符号,b就变成指向peron2结构的一个指针 Name: "zgf", //在结构体里面初始化属性值,末尾加逗号;在go语言中常用此方法进行初始化 Age: 23, Addr:"b111", } a.Name = "raymond" //调用结构,a相当于由类创建的对象;由对象调用结构中的属性并赋值 a.Age = 35 b.Addr = "b222" //此处更改了初始化的值 fmt.Println(a, b) //没调用结构中属性时,输出int型age的默认值{0}, A(b) //调用函数A输出的结果,取b的地址 fmt.Println("b->",b) B(b) fmt.Println("b->",b) } func A(per *person2) { //定义person2是指针,把结构person2作为参数传递 per.Age = 13 fmt.Println("A->",per) } func B(per *person2) { per.Age = 15 fmt.Println("B->",per) } //输出结果: /* {raymond 35} &{zgf 23} A-> &{zgf 13} b-> &{zgf 13} B-> &{zgf 15} b-> &{zgf 15} */
- struct的匿名结构基本操作一
//struct的匿名结构基本操作一 package main import "fmt" func main() { a := struct { //没有为struct进行命名,是匿名结构 Name string //需初始化属性 Age int }{ Name:"zgf", // 赋值 Age: 35, } fmt.Println(a) } //输出结果:{zgf 35} //如果改为: a := &struct {},则a为取结构struct地址的指针,输出结果如下: // &{zgf 35}
- struct的嵌套结构基本操作二
//struct的嵌套结构基本操作二 package main import "fmt" type person struct { //结构嵌套使用 Name string Age int Contact struct { //此处Contact是person的字段名称,不是结构名称;所以下面不可直接调用。此处是一个匿名结构 Phone, City string } } func main() { a := person{} //调用结构person,输出结构形式 b := person{Name:"zgf",Age:35} //初始化一级结构体内字段 b.Contact.Phone = "123456" //初始化二级结构体内的字段 b.Contact.City ="beijing" fmt.Println(a) fmt.Println(b) } //输出结果: /* { 0 { }} {zgf 35 {123456 beijing}} */
- struct的匿名字段
//struct的匿名字段 package main import "fmt" type person struct { string //匿名字段 int } func main() { a := person{"zgf",35} //必须严格按照结构体内匿名字段的顺序进行初始化,否则报错! fmt.Println(a) } //输出结果:{zgf 35}
- struct字段之间的赋值操作
//struct字段之间的赋值操作 package main import "fmt" type person struct { Name string Age int } func main() { a := person{"zgf",35} //必须严格按照结构体内匿名字段的顺序进行初始化,否则报错! var b person b = a fmt.Println(b) } //输出结果:{zgf 35}
- struct结构之间的类型比较
//struct结构之间的类型比较 package main import "fmt" type person struct { Name string Age int } type person1 struct { Name string Age int } type person2 struct { Name string Age int } func main() { a := person{Name:"zgf",Age:35} //结构名称和字段值完全相同,则为true b := person{Name:"zgf",Age:35} fmt.Println(a == b) c := person{Name:"zgf",Age:35} //结构名称相同,字段值不同,则为false d := person{Name:"zgf",Age:12} fmt.Println(c == d) /* e := person1{Name:"zgf",Age:35} //结构名称不同,字段值相同,则报错!因为两个结构名称不同,则类型就不同;不同类型没有可比性,所以报错 f := person2{Name:"zgf",Age:35} fmt.Println(e == f) */ } //输出结果: /* true false */
- struct嵌入结构及结构体内字段初始化的操作形式
//嵌入结构及结构体内字段初始化的操作形式 package main import "fmt" type human struct { Sex int } type teacher struct { Name string Age int human //此处human是一个嵌入结构作为匿名字段,自身结构体内的字段也赋予了该结构构体teacher内部 } type student struct { Name string Age int human } func main() { a := teacher{Name:"zgf",Age:35,human:human{Sex:0}} //嵌入结构体内的字段初始化形式 b := student{Name:"zgh",Age:32,human:human{Sex:1}} /* a.Name = "aaa" //重新给两个结构体内的字段赋值 a.Age = 23 a.Sex = 2 //也可以写为:a.human.Sex = 2,human内的字段直接赋予teacher和student,所以可以直接调用 b.Name = "bbb" b.Age = 24 b.Sex = 3 */ fmt.Println(a) fmt.Println(b) } //输出结果: /* {zgf 35 {0}} {zgh 32 {1}} */ //加上注释部分的代码后,输出结果: /* {aaa 23 {2}} {bbb 24 {3}} */
11. 方法method
12. 接口interface
13. 反射reflection
14.并发concurrency
15. 项目与坑
二. GO语言Web基础
1. 博客项目设计
2. 初窥web开发
3. 模板用法讲解
4. 登录及分类管理
5. 文章的添加与删除
6. 评论与分类显示
7. 为文章添加标签
8. 文章附件上传
9. 国际化支持
10. 自建HTTP中间件
11. 简易的RPC实现
12. Go Web扩展学习
三. GO语言名库讲解
1.
2.
3.
请发表评论