AI智能
改变未来

Go语言入门学习–基础篇


1.数据类型

1.1基础数据类型

布尔型 bool

整型 int

浮点型flaot32/64

字符型 byte

字符串 string

1.数据类型

1.1基础数据类型

布尔型 bool

整型 int

浮点型flaot32/64

字符型 byte

字符串 string

[code]//基础数据类型//定义一个布尔类型的变量avar a bool//定义一个int类型的变量bvar b int//float32类型var c float32//字符类型var d byte//字符串var e string

 

1.2派生类型

指针类型(pointer)

数组类型

结构化类型(struct)

Channel 类型

函数类型

切片类型

接口类型(interface)

Map 类型

[code]//定义一个长度为2的string数组fvar f [2]string//定义一个切片g 切片类似于list的结构var g []int//定义一个结构体h 结构体类似于Java中的entity的概念 但是使用更加灵活type h struct {   x string   y int   z float32}//定义一个Map i,示例中 string为map的key类型,int为map的value类型var i map[string[LL(1] ]int[LL(2] //指针类型 j是一个指向int值的指针var j *int
[code]//定义一个main方法,main方法为一个项目的主方法func main()  {
[code]//定义并赋值l,使用 := ,l的类型为所赋值的类型 l:=1
[code]// &l代表指向l变量的内存地址 j=&l
[code]// 在指针j前面加*用来获取指针j所指向的内容 m :=*j fmt.Print(j,m)[LL(3] }
[code]// 创建一个传递string类型的信道n
[code]var n chan  string

上述main函数运行结果如下

0xc00000a0c8 1

Interface和channel使用在后面单独章节

2.流程控制

2.1 for

类似于其他语言,for循环有三种形式,Break 关键字可以用于跳出循环

[code]//for 初始化条件; 循环控制条件; 赋值表达式 {循环体}for i := 0; i < 5; i++[LL(4]  {   fmt.Println(i)}
[code]  m := 5n := 0//for 循环控制条件{循环体}for n < m {   n++   fmt.Println(n)}//for range 用于遍历数组,切片,map 格式如下// for index/key,value :=range slice/map/数组 {fmt.Print(index/key,value)}x := []int{1, 2, 1, 5, 10}for y, z := range x {   fmt.Printf(\"第 %d 位 x 的值 = %d\\n\", y, z)}

 

运行结果如下

//第一个for循环运行结果

0

1

2

3

4

//第二个for循环运行结果

1

2

3

4

5

//第三个for循环运行结果

第 0 位 x 的值 = 1

第 1 位 x 的值 = 2

第 2 位 x 的值 = 1

第 3 位 x 的值 = 5

第 4 位 x 的值 = 10

2.2 if

[code]m := 5n := 0//if 布尔表达式{
[code]表达式为true时执行的语句
[code]//}else{ else必须和if结束的大括号同一行
[code]}if n < m {   fmt.Println(n)}else{   fmt.Println(m)}

 

If语句可以嵌套

2.3 swich

[code]grade := \"\"marks := 90switch marks {case 90[LL(5] :   grade = \"A\"case 80:   grade = \"B\"case 50, 60, 70:   grade = \"C\"
[code]//default表示如果没有匹配到case 则执行default内表达式default:   grade = \"D\"}fmt.Print(grade)

 

3.函数与方法

3.1函数与方法的定义

在很多语言中,函数和方法指的是同一种东西。但是在Go语言中有所区别

在 Go 语言中有一个概念和函数极其相似,叫做方法 。Go 语言的方法其实是作用在接收者(receiver)上的一个函数,接收者是某种非内置类型的变量。因此方法是一种特殊类型的函数。

 

[code]//定义一个结构体student
[code]type student struct {   Id   int   Name string}//定义一个函数getName() 返回值类型为stringfunc getName[LL(6] (stu student) string {
[code]   fmt.Println(\"函数被调用\")   return stu.Name}//定义一个方法 getNamefunc (stu student) getName() string {
[code]   fmt.Println(\"方法被调用\")   return stu.Name}

 

3.2函数和方法的调用

函数将变量作为参数:Function1(recv)

方法在变量上被调用:recv.Method1()

[code]func main() {   a := student{      Id:   12,      Name: \"Any\",   }[LL(7] 
[code]//调用getName()函数   getName(a)
[code]//调用getName()方法   a.getName()}

 

运行结果如下

函数被调用

方法被调用

 

4.接口

4.1定义一个接口

Go 语言并没有类和继承的概念。但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。接口定义了一组方法集合,但是这些方法不包含具体的实现代码,接口定义中不能包含变量。

[code]//使用interface关键字定义接口,Phone为接口名,call()为接口中定义的方法
[code]type Phone interface {   call()   //message()}

4.2实现接口

实现一个接口需要实现接口中的所有方法。

[code]type Nokia struct {   name  string   model string}type Iphone struct {   name string   color string}//func (nokia Nokia)call()  {   fmt.Printf(\"nokia call ! %v ,%v\",nokia.name,nokia.model)}func (iphone Iphone)call()  {   fmt.Printf(\"iPhone call! %v,%v\",iphone.name,iphone.color)}

 

4.3使用接口

[code]func main() {   /* var phone Phone      phone = Nokia{         name:  \"机皇\",         model: \"N97\",      }      fmt.Println(reflect.TypeOf(phone))      phone.call()*/   phone := Nokia{      name:  \"机皇\",      model: \"N97\",   }   phone.call()   iphone := Iphone{      name:  \"5s\",      color: \"rich gold\",   }   iphone.call()}

 

4.4接口嵌套

接口可以嵌套,子接口拥有父接口所有方法,使用子接口时,需要实现父接口和子接口中所有的方法。

[code]type Phone interface {   Call   Message}type Call interface {   answer()}type Message interface {   sendMessage()   receiceMessage()}

 

4.5空接口

Go语言空接口(interface{})不包含任何的method,所有的类型都实现了空interface,空interface可以存储任意类型的数值。[LL(8] 

[code]slice := make([]interface{}, 10)map1 := make(map[string]string)map2 := make(map[string]int)map2[\"TaskID\"] = 1map1[\"Command\"] = \"ping\"map3 := make(map[string]map[string]string)map3[\"mapvalue\"] = map1slice[0] = map2slice[1] = map1slice[3] = map3fmt.Println(slice[0])fmt.Println(slice[1])fmt.Println(slice[3])

 

5.反射[LL(9] 

反射需要用到reflect包

5.1利用反射获取数据类型和值

reflect.ValueOf()用于获取参数的值,获取的参数类型为空接口,可以获取所有类型的参数

reflect.TypeOf()用于获取参数的类型

[code]var circle float64 = 6.28[LL(10] fmt.Println(\"Reflect : circle.Value = \", reflect.ValueOf(circle))fmt.Println(\"Reflect : circle.Type  = \", reflect.TypeOf(circle))

 

5.2用反射进行变量修改

[code]var circle float64 = 6.28//CanSet()函数确认变量是否是可修改的value := reflect.ValueOf(circle)fmt.Println(\"Reflect : value = \", value)fmt.Println(\"Settability of value : \", value.CanSet())//value2表示指向circle的指针value2 := reflect.ValueOf(&circle)fmt.Println(\"Settability of value : \", value2.CanSet())//value3是表示circle的指针的反射   reflect.Elem() 获取这个指针指向的元素类型value3 := value2.Elem()fmt.Println(\"Settability of value : \", value3.CanSet())//修改circle的指针的反射类型的值value3.SetFloat(3.14)fmt.Println(\"Value of value3: \", value3)fmt.Println(\"value of circle: \", circle)

 

 

6.并发

6.1 goroutine(协程)

在Go中,应用程序并发处理的部分被称作 goroutines(go协程),它可以进行更有效的并发运算。在协程和操作系统线程之间并无一对一的关系:协程是根据一个或多个线程的可用性,映射(多路复用,执行于)在它们之上的;协程调度器在 Go 运行时很好的完成了这个工作。

Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine,一个 goroutine 必定对应一个函数。

[code]func main() {
[code]//创建一个loop方法的协程   go loop()   loop()}func loop() {   for i := 0; i < 10; i++ {      fmt.Printf(\"%d \", i)   }}

 

6.2 channel(信道)

Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。

它的操作符是箭头 <-

[code]var x boolc := make(chan bool) //创建一个无缓冲的bool型Channelc <- x        //向一个Channel发送一个值x<- c          //从一个Channel中接收一个值x = <- c      //从Channel c接收一个值并将其存储到x中x, ok = <- c  //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false

 

信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。如果不用信道来阻塞主线的话,主线程就会过早跑完,loop线程都没有机会执行。

[code]var complete := make(chan int)[LL(11] func loop() {   for i := 0; i < 10; i++ {      fmt.Printf(\"%d \", i)   }   complete <- 0 // 执行完毕了,发出消息}func main() {   go loop()   <-complete // 直到线程跑完, 取到消息. main在此阻塞住}

 

 

7.错误和异常处理

7.1 error(错误)

一般情况下,如果函数需要返回错误,就将 error 作为多个返回值中的最后一个(但并非是强制要求)。

[code]func Sqrt(f float64) (float64, error) {   if f < 0 {      return -1, errors.New(\"参数必须不小于0\")   }
[code]// 执行开方计算   return math.Sqrt(f) , nil}func main(){   result, err:= Sqrt(-2)   if err != nil   {      fmt.Println(err)   }else{      fmt.Println(result)   }}

 

7.2 panic(异常)

错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。错误是业务过程的一部分,而异常不是 。

[code]func main() {   fmt.Println(\"Starting the program\")   panic(\"A severe error occurred: stopping the program!\")   fmt.Println(\"Ending the program\")}

 

recover是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入panic,调用recover可以捕获到panic的输入值,并且恢复正常的执行。[LL(12] 

 

可以使用关键字defer向函数注册退出调用,即主调函数退出时,defer后的函数才会被调用。

defer语句的作用是不管程序是否出现异常,均在函数退出时自动执行相关代码(类似于Java中的finally)。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。

[code]func badCall() {   panic(\"bad end\")}func test() {   defer func() {      if e := recover(); e != nil {         fmt.Printf(\"Panicing %s\\n\", e)      }   }()   badCall()   fmt.Printf(\"After bad call\\n\")}func main() {   fmt.Printf(\"Calling test\\n\")   test()   fmt.Printf(\"Test completed\\n\")}

 

 

 [LL(1]string为map的key类型

 [LL(2]int为map的value类型

 [LL(3]使用Print函数日志输出

 [LL(4]Go语言中自增i++不是一个操作符而是语句,所以i=i++会导致编译错误,不存在++i,–也一样

 [LL(5]case 90 [LL(5]表示marks==90是否成立,若结果为true则执行下面的 grade = \”A\”,若不成立则执行下一个case

 [LL(6]在同一个package下允许函数和方法同名,但不允许函数之间同名。

Go语言中允许方法同名,但不允许同名方法同参,类似于Java中方法重载的概念

 [LL(7]声明一个结构体变量a

 [LL(8]类似于Java中的Object。

 [LL(9]反射机制也类似于Java,其中reflect包功能类似于.Class

 [LL(10]这里是我们已经定义的一个值,但是反射机制可以让我们获取空接口传来的任意参数的类型和值

 

 [LL(11]创建一个无缓冲的int型 channel

 [LL(12]类似于Java中的try catch模块

 

1.2派生类型

指针类型(pointer)

数组类型

结构化类型(struct)

Channel 类型

函数类型

切片类型

接口类型(interface)

Map 类型

[code]//定义一个长度为2的string数组fvar f [2]string//定义一个切片g 切片类似于list的结构var g []int//定义一个结构体h 结构体类似于Java中的entity的概念 但是使用更加灵活type h struct {   x string   y int   z float32}//定义一个Map i,示例中 string为map的key类型,int为map的value类型var i map[string[LL(1] ]int[LL(2] //指针类型 j是一个指向int值的指针var j *int
[code]//定义一个main方法,main方法为一个项目的主方法func main()  {
[code]//定义并赋值l,使用 := ,l的类型为所赋值的类型 l:=1
[code]// &l代表指向l变量的内存地址 j=&l
[code]// 在指针j前面加*用来获取指针j所指向的内容 m :=*j fmt.Print(j,m)[LL(3] }
[code]// 创建一个传递string类型的信道n
[code]var n chan  string

上述main函数运行结果如下

0xc00000a0c8 1

Interface和channel使用在后面单独章节

2.流程控制

2.1 for

类似于其他语言,for循环有三种形式,Break 关键字可以用于跳出循环

[code]//for 初始化条件; 循环控制条件; 赋值表达式 {循环体}for i := 0; i < 5; i++[LL(4]  {   fmt.Println(i)}
[code]  m := 5n := 0//for 循环控制条件{循环体}for n < m {   n++   fmt.Println(n)}//for range 用于遍历数组,切片,map 格式如下// for index/key,value :=range slice/map/数组 {fmt.Print(index/key,value)}x := []int{1, 2, 1, 5, 10}for y, z := range x {   fmt.Printf(\"第 %d 位 x 的值 = %d\\n\", y, z)}

 

运行结果如下

//第一个for循环运行结果

0

1

2

3

4

//第二个for循环运行结果

1

2

3

4

5

//第三个for循环运行结果

第 0 位 x 的值 = 1

第 1 位 x 的值 = 2

第 2 位 x 的值 = 1

第 3 位 x 的值 = 5

第 4 位 x 的值 = 10

2.2 if

[code]m := 5n := 0//if 布尔表达式{
[code]表达式为true时执行的语句
[code]//}else{ else必须和if结束的大括号同一行
[code]}if n < m {   fmt.Println(n)}else{   fmt.Println(m)}

 

If语句可以嵌套

2.3 swich

[code]grade := \"\"marks := 90switch marks {case 90[LL(5] :   grade = \"A\"case 80:   grade = \"B\"case 50, 60, 70:   grade = \"C\"
[code]//default表示如果没有匹配到case 则执行default内表达式default:   grade = \"D\"}fmt.Print(grade)

 

3.函数与方法

3.1函数与方法的定义

在很多语言中,函数和方法指的是同一种东西。但是在Go语言中有所区别

在 Go 语言中有一个概念和函数极其相似,叫做方法 。Go 语言的方法其实是作用在接收者(receiver)上的一个函数,接收者是某种非内置类型的变量。因此方法是一种特殊类型的函数。

 

[code]//定义一个结构体student
[code]type student struct {   Id   int   Name string}//定义一个函数getName() 返回值类型为stringfunc getName[LL(6] (stu student) string {
[code]   fmt.Println(\"函数被调用\")   return stu.Name}//定义一个方法 getNamefunc (stu student) getName() string {
[code]   fmt.Println(\"方法被调用\")   return stu.Name}

 

3.2函数和方法的调用

函数将变量作为参数:Function1(recv)

方法在变量上被调用:recv.Method1()

[code]func main() {   a := student{      Id:   12,      Name: \"Any\",   }[LL(7] 
[code]//调用getName()函数   getName(a)
[code]//调用getName()方法   a.getName()}

 

运行结果如下

函数被调用

方法被调用

 

4.接口

4.1定义一个接口

Go 语言并没有类和继承的概念。但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。接口定义了一组方法集合,但是这些方法不包含具体的实现代码,接口定义中不能包含变量。

[code]//使用interface关键字定义接口,Phone为接口名,call()为接口中定义的方法
[code]type Phone interface {   call()   //message()}

4.2实现接口

实现一个接口需要实现接口中的所有方法。

[code]type Nokia struct {   name  string   model string}type Iphone struct {   name string   color string}//func (nokia Nokia)call()  {   fmt.Printf(\"nokia call ! %v ,%v\",nokia.name,nokia.model)}func (iphone Iphone)call()  {   fmt.Printf(\"iPhone call! %v,%v\",iphone.name,iphone.color)}

 

4.3使用接口

[code]func main() {   /* var phone Phone      phone = Nokia{         name:  \"机皇\",         model: \"N97\",      }      fmt.Println(reflect.TypeOf(phone))      phone.call()*/   phone := Nokia{      name:  \"机皇\",      model: \"N97\",   }   phone.call()   iphone := Iphone{      name:  \"5s\",      color: \"rich gold\",   }   iphone.call()}

 

4.4接口嵌套

接口可以嵌套,子接口拥有父接口所有方法,使用子接口时,需要实现父接口和子接口中所有的方法。

[code]type Phone interface {   Call   Message}type Call interface {   answer()}type Message interface {   sendMessage()   receiceMessage()}

 

4.5空接口

Go语言空接口(interface{})不包含任何的method,所有的类型都实现了空interface,空interface可以存储任意类型的数值。[LL(8] 

[code]slice := make([]interface{}, 10)map1 := make(map[string]string)map2 := make(map[string]int)map2[\"TaskID\"] = 1map1[\"Command\"] = \"ping\"map3 := make(map[string]map[string]string)map3[\"mapvalue\"] = map1slice[0] = map2slice[1] = map1slice[3] = map3fmt.Println(slice[0])fmt.Println(slice[1])fmt.Println(slice[3])

 

5.反射[LL(9] 

反射需要用到reflect包

5.1利用反射获取数据类型和值

reflect.ValueOf()用于获取参数的值,获取的参数类型为空接口,可以获取所有类型的参数

reflect.TypeOf()用于获取参数的类型

[code]var circle float64 = 6.28[LL(10] fmt.Println(\"Reflect : circle.Value = \", reflect.ValueOf(circle))fmt.Println(\"Reflect : circle.Type  = \", reflect.TypeOf(circle))

 

5.2用反射进行变量修改

[code]var circle float64 = 6.28//CanSet()函数确认变量是否是可修改的value := reflect.ValueOf(circle)fmt.Println(\"Reflect : value = \", value)fmt.Println(\"Settability of value : \", value.CanSet())//value2表示指向circle的指针value2 := reflect.ValueOf(&circle)fmt.Println(\"Settability of value : \", value2.CanSet())//value3是表示circle的指针的反射   reflect.Elem() 获取这个指针指向的元素类型value3 := value2.Elem()fmt.Println(\"Settability of value : \", value3.CanSet())//修改circle的指针的反射类型的值value3.SetFloat(3.14)fmt.Println(\"Value of value3: \", value3)fmt.Println(\"value of circle: \", circle)

 

 

6.并发

6.1 goroutine(协程)

在Go中,应用程序并发处理的部分被称作 goroutines(go协程),它可以进行更有效的并发运算。在协程和操作系统线程之间并无一对一的关系:协程是根据一个或多个线程的可用性,映射(多路复用,执行于)在它们之上的;协程调度器在 Go 运行时很好的完成了这个工作。

Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine,一个 goroutine 必定对应一个函数。

[code]func main() {
[code]//创建一个loop方法的协程   go loop()   loop()}func loop() {   for i := 0; i < 10; i++ {      fmt.Printf(\"%d \", i)   }}

 

 

 

 
   

 

Main()程序入口

 
   

 

 

 

 

                                               go running()

 

                                                       

 

                                                running()                                              running()

 

 

 

 

                                               主程序执行完毕

 

 

6.2 channel(信道)

Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。

它的操作符是箭头 <-

[code]var x boolc := make(chan bool) //创建一个无缓冲的bool型Channelc <- x        //向一个Channel发送一个值x<- c          //从一个Channel中接收一个值x = <- c      //从Channel c接收一个值并将其存储到x中x, ok = <- c  //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false

 

信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。如果不用信道来阻塞主线的话,主线程就会过早跑完,loop线程都没有机会执行。

[code]var complete := make(chan int)[LL(11] func loop() {   for i := 0; i < 10; i++ {      fmt.Printf(\"%d \", i)   }   complete <- 0 // 执行完毕了,发出消息}func main() {   go loop()   <-complete // 直到线程跑完, 取到消息. main在此阻塞住}

 

 

7.错误和异常处理

7.1 error(错误)

一般情况下,如果函数需要返回错误,就将 error 作为多个返回值中的最后一个(但并非是强制要求)。

[code]func Sqrt(f float64) (float64, error) {   if f < 0 {      return -1, errors.New(\"参数必须不小于0\")   }
[code]// 执行开方计算   return math.Sqrt(f) , nil}func main(){   result, err:= Sqrt(-2)   if err != nil   {      fmt.Println(err)   }else{      fmt.Println(result)   }}

 

7.2 panic(异常)

错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。错误是业务过程的一部分,而异常不是 。

[code]func main() {   fmt.Println(\"Starting the program\")   panic(\"A severe error occurred: stopping the program!\")   fmt.Println(\"Ending the program\")}

 

recover是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入panic,调用recover可以捕获到panic的输入值,并且恢复正常的执行。[LL(12] 

 

可以使用关键字defer向函数注册退出调用,即主调函数退出时,defer后的函数才会被调用。

defer语句的作用是不管程序是否出现异常,均在函数退出时自动执行相关代码(类似于Java中的finally)。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。

[code]func badCall() {   panic(\"bad end\")}func test() {   defer func() {      if e := recover(); e != nil {         fmt.Printf(\"Panicing %s\\n\", e)      }   }()   badCall()   fmt.Printf(\"After bad call\\n\")}func main() {   fmt.Printf(\"Calling test\\n\")   test()   fmt.Printf(\"Test completed\\n\")}

 

 

 [LL(1]string为map的key类型

 [LL(2]int为map的value类型

 [LL(3]使用Print函数日志输出

 [LL(4]Go语言中自增i++不是一个操作符而是语句,所以i=i++会导致编译错误,不存在++i,–也一样

 [LL(5]case 90 [LL(5]表示marks==90是否成立,若结果为true则执行下面的 grade = \”A\”,若不成立则执行下一个case

 [LL(6]在同一个package下允许函数和方法同名,但不允许函数之间同名。

Go语言中允许方法同名,但不允许同名方法同参,类似于Java中方法重载的概念

 [LL(7]声明一个结构体变量a

 [LL(8]类似于Java中的Object。

 [LL(9]反射机制也类似于Java,其中reflect包功能类似于.Class

 [LL(10]这里是我们已经定义的一个值,但是反射机制可以让我们获取空接口传来的任意参数的类型和值

 [LL(11]创建一个无缓冲的int型 channel

 [LL(12]类似于Java中的try catch模块

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Go语言入门学习–基础篇