1.数据类型
1.1基础数据类型
布尔型 bool
整型 int
浮点型flaot32/64
字符型 byte
字符串 string
1.数据类型 1.1基础数据类型 布尔型 bool 整型 int 浮点型flaot32/64 字符型 byte 字符串 string
//基础数据类型
//定义一个布尔类型的变量a
var a bool
//定义一个int类型的变量b
var b int
//float32类型
var c float32
//字符类型
var d byte
//字符串
var e string |
1.2派生类型 指针类型(pointer) 数组类型 结构化类型(struct) Channel 类型 函数类型 切片类型 接口类型(interface) Map 类型
//定义一个长度为2的string数组f
var 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 //定义一个main方法,main方法为一个项目的主方法
func main() { //定义并赋值l,使用 := ,l的类型为所赋值的类型
l:=1 // &l代表指向l变量的内存地址
j=&l // 在指针j前面加*用来获取指针j所指向的内容
m :=*j
fmt.Print(j,m)[LL(3]
} // 创建一个传递string类型的信道n var n chan string |
上述main函数运行结果如下
Interface和channel使用在后面单独章节 2.流程控制 2.1 for 类似于其他语言,for循环有三种形式,Break 关键字可以用于跳出循环
//for 初始化条件; 循环控制条件; 赋值表达式 {循环体}
for i := 0; i < 5; i++[LL(4] {
fmt.Println(i)
} m := 5
n := 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
m := 5
n := 0
//if 布尔表达式{ 表达式为true时执行的语句 //}else{ else必须和if结束的大括号同一行 }
if n < m {
fmt.Println(n)
}else{
fmt.Println(m)
} |
If语句可以嵌套 2.3 swich
grade := ""
marks := 90
switch marks {
case 90[LL(5] :
grade = "A"
case 80:
grade = "B"
case 50, 60, 70:
grade = "C" //default表示如果没有匹配到case 则执行default内表达式
default:
grade = "D"
}
fmt.Print(grade) |
3.函数与方法 3.1函数与方法的定义 在很多语言中,函数和方法指的是同一种东西。但是在Go语言中有所区别 在 Go 语言中有一个概念和函数极其相似,叫做方法 。Go 语言的方法其实是作用在接收者(receiver)上的一个函数,接收者是某种非内置类型的变量。因此方法是一种特殊类型的函数。
//定义一个结构体student type student struct {
Id int
Name string
}
//定义一个函数getName() 返回值类型为string
func getName[LL(6] (stu student) string { fmt.Println("函数被调用")
return stu.Name
}
//定义一个方法 getName
func (stu student) getName() string { fmt.Println("方法被调用")
return stu.Name
} |
3.2函数和方法的调用 函数将变量作为参数:Function1(recv) 方法在变量上被调用:recv.Method1()
func main() {
a := student{
Id: 12,
Name: "Any",
}[LL(7] //调用getName()函数
getName(a) //调用getName()方法
a.getName()
} |
运行结果如下
4.接口 4.1定义一个接口 Go 语言并没有类和继承的概念。但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。接口定义了一组方法集合,但是这些方法不包含具体的实现代码,接口定义中不能包含变量。
//使用interface关键字定义接口,Phone为接口名,call()为接口中定义的方法 type Phone interface {
call()
//message()
} |
4.2实现接口 实现一个接口需要实现接口中的所有方法。
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使用接口
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接口嵌套 接口可以嵌套,子接口拥有父接口所有方法,使用子接口时,需要实现父接口和子接口中所有的方法。
type Phone interface {
Call
Message
}
type Call interface {
answer()
}
type Message interface {
sendMessage()
receiceMessage()
} |
4.5空接口 Go语言空接口(interface{})不包含任何的method,所有的类型都实现了空interface,空interface可以存储任意类型的数值。[LL(8]
slice := make([]interface{}, 10)
map1 := make(map[string]string)
map2 := make(map[string]int)
map2["TaskID"] = 1
map1["Command"] = "ping"
map3 := make(map[string]map[string]string)
map3["mapvalue"] = map1
slice[0] = map2
slice[1] = map1
slice[3] = map3
fmt.Println(slice[0])
fmt.Println(slice[1])
fmt.Println(slice[3]) |
反射需要用到reflect包 5.1利用反射获取数据类型和值 reflect.ValueOf()用于获取参数的值,获取的参数类型为空接口,可以获取所有类型的参数 reflect.TypeOf()用于获取参数的类型
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用反射进行变量修改
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 必定对应一个函数。
func main() { //创建一个loop方法的协程
go loop()
loop()
}
func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
} |
6.2 channel(信道) Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。 它的操作符是箭头 <-
var x bool
c := make(chan bool) //创建一个无缓冲的bool型Channel
c <- x //向一个Channel发送一个值x
<- c //从一个Channel中接收一个值
x = <- c //从Channel c接收一个值并将其存储到x中
x, ok = <- c //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false |
信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。如果不用信道来阻塞主线的话,主线程就会过早跑完,loop线程都没有机会执行。
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 作为多个返回值中的最后一个(但并非是强制要求)。
func Sqrt(f float64) (float64, error) {
if f < 0 {
return -1, errors.New("参数必须不小于0")
} // 执行开方计算
return math.Sqrt(f) , nil
}
func main(){
result, err:= Sqrt(-2)
if err != nil {
fmt.Println(err)
}else{
fmt.Println(result)
}
} |
7.2 panic(异常) 错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。错误是业务过程的一部分,而异常不是 。
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语句会按照逆序执行,最后该函数返回。
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(4]Go语言中自增i++不是一个操作符而是语句,所以i=i++会导致编译错误,不存在++i,--也一样
[LL(5]case 90 [LL(5]表示marks==90是否成立,若结果为true则执行下面的 grade = "A",若不成立则执行下一个case
[LL(6]在同一个package下允许函数和方法同名,但不允许函数之间同名。
Go语言中允许方法同名,但不允许同名方法同参,类似于Java中方法重载的概念
[LL(9]反射机制也类似于Java,其中reflect包功能类似于.Class
[LL(10]这里是我们已经定义的一个值,但是反射机制可以让我们获取空接口传来的任意参数的类型和值
|
1.2派生类型
指针类型(pointer)
数组类型
结构化类型(struct)
Channel 类型
函数类型
切片类型
接口类型(interface)
Map 类型
//定义一个长度为2的string数组f
var 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 //定义一个main方法,main方法为一个项目的主方法
func main() { //定义并赋值l,使用 := ,l的类型为所赋值的类型
l:=1 // &l代表指向l变量的内存地址
j=&l // 在指针j前面加*用来获取指针j所指向的内容
m :=*j
fmt.Print(j,m)[LL(3]
} // 创建一个传递string类型的信道n var n chan string |
上述main函数运行结果如下
Interface和channel使用在后面单独章节
2.流程控制
2.1 for
类似于其他语言,for循环有三种形式,Break 关键字可以用于跳出循环
//for 初始化条件; 循环控制条件; 赋值表达式 {循环体}
for i := 0; i < 5; i++[LL(4] {
fmt.Println(i)
} m := 5
n := 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
m := 5
n := 0
//if 布尔表达式{ 表达式为true时执行的语句 //}else{ else必须和if结束的大括号同一行 }
if n < m {
fmt.Println(n)
}else{
fmt.Println(m)
} |
If语句可以嵌套
2.3 swich
grade := ""
marks := 90
switch marks {
case 90[LL(5] :
grade = "A"
case 80:
grade = "B"
case 50, 60, 70:
grade = "C" //default表示如果没有匹配到case 则执行default内表达式
default:
grade = "D"
}
fmt.Print(grade) |
3.函数与方法
3.1函数与方法的定义
在很多语言中,函数和方法指的是同一种东西。但是在Go语言中有所区别
在 Go 语言中有一个概念和函数极其相似,叫做方法 。Go 语言的方法其实是作用在接收者(receiver)上的一个函数,接收者是某种非内置类型的变量。因此方法是一种特殊类型的函数。
//定义一个结构体student type student struct {
Id int
Name string
}
//定义一个函数getName() 返回值类型为string
func getName[LL(6] (stu student) string { fmt.Println("函数被调用")
return stu.Name
}
//定义一个方法 getName
func (stu student) getName() string { fmt.Println("方法被调用")
return stu.Name
} |
3.2函数和方法的调用
函数将变量作为参数:Function1(recv)
方法在变量上被调用:recv.Method1()
func main() {
a := student{
Id: 12,
Name: "Any",
}[LL(7] //调用getName()函数
getName(a) //调用getName()方法
a.getName()
} |
运行结果如下
4.接口
4.1定义一个接口
Go 语言并没有类和继承的概念。但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。接口定义了一组方法集合,但是这些方法不包含具体的实现代码,接口定义中不能包含变量。
//使用interface关键字定义接口,Phone为接口名,call()为接口中定义的方法 type Phone interface {
call()
//message()
} |
4.2实现接口
实现一个接口需要实现接口中的所有方法。
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使用接口
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接口嵌套
接口可以嵌套,子接口拥有父接口所有方法,使用子接口时,需要实现父接口和子接口中所有的方法。
type Phone interface {
Call
Message
}
type Call interface {
answer()
}
type Message interface {
sendMessage()
receiceMessage()
} |
4.5空接口
Go语言空接口(interface{})不包含任何的method,所有的类型都实现了空interface,空interface可以存储任意类型的数值。[LL(8]
slice := make([]interface{}, 10)
map1 := make(map[string]string)
map2 := make(map[string]int)
map2["TaskID"] = 1
map1["Command"] = "ping"
map3 := make(map[string]map[string]string)
map3["mapvalue"] = map1
slice[0] = map2
slice[1] = map1
slice[3] = map3
fmt.Println(slice[0])
fmt.Println(slice[1])
fmt.Println(slice[3]) |
反射需要用到reflect包
5.1利用反射获取数据类型和值
reflect.ValueOf()用于获取参数的值,获取的参数类型为空接口,可以获取所有类型的参数
reflect.TypeOf()用于获取参数的类型
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用反射进行变量修改
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 必定对应一个函数。
func main() { //创建一个loop方法的协程
go loop()
loop()
}
func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
} |
go running()
running() running()
主程序执行完毕
6.2 channel(信道)
Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。
它的操作符是箭头 <-
var x bool
c := make(chan bool) //创建一个无缓冲的bool型Channel
c <- x //向一个Channel发送一个值x
<- c //从一个Channel中接收一个值
x = <- c //从Channel c接收一个值并将其存储到x中
x, ok = <- c //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false |
信道在取消息和存消息的时候都会挂起当前的goroutine,除非另一端已经准备好。如果不用信道来阻塞主线的话,主线程就会过早跑完,loop线程都没有机会执行。
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 作为多个返回值中的最后一个(但并非是强制要求)。
func Sqrt(f float64) (float64, error) {
if f < 0 {
return -1, errors.New("参数必须不小于0")
} // 执行开方计算
return math.Sqrt(f) , nil
}
func main(){
result, err:= Sqrt(-2)
if err != nil {
fmt.Println(err)
}else{
fmt.Println(result)
}
} |
7.2 panic(异常)
错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。错误是业务过程的一部分,而异常不是 。
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语句会按照逆序执行,最后该函数返回。
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(4]Go语言中自增i++不是一个操作符而是语句,所以i=i++会导致编译错误,不存在++i,--也一样
[LL(5]case 90 [LL(5]表示marks==90是否成立,若结果为true则执行下面的 grade = "A",若不成立则执行下一个case
[LL(6]在同一个package下允许函数和方法同名,但不允许函数之间同名。
Go语言中允许方法同名,但不允许同名方法同参,类似于Java中方法重载的概念
[LL(9]反射机制也类似于Java,其中reflect包功能类似于.Class
[LL(10]这里是我们已经定义的一个值,但是反射机制可以让我们获取空接口传来的任意参数的类型和值