终端中运行命令
go run hello.go
标识符
由字母、数字和下划线组成
关键字 25个
break default func interface select case defer go map struct chan else goto
package switch const fallthrough range type continue for import return var
常见关键字解释
1.select
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
// 每个 case 都必须是一个通信
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
// 默认执行
default:
fmt.Printf("no communication\n")
}
}
2.defer
可以将一个方法延迟到包裹该方法的方法返回时执行
可以用来处理关闭文件句柄等收尾操作
defer触发时机
- 包裹defer的函数返回时
- 包裹defer的函数执行到末尾时
- 所在的goroutine发生panic时
package main
import "fmt"
// 当一个方法中有多个defer时
// defer会将要延迟执行的方法“压栈”
// 当defer被触发时,将所有“压栈”的方法“出栈”并执行
func stackingDefers() {
defer func() {
fmt.Printf("1")
}()
defer func() {
fmt.Printf("2")
}()
defer func() {
fmt.Printf("3")
}()
}
func main() {
stackingDefers() // 执行结果为 321
}
// 匿名返回值
func returnValues() int {
var result int
defer func() {
// 返回刚才创建的返回值(retValue)
result++
fmt.Println("defer")
}()
// 将result赋值给返回值 可以理解成Go自动创建了一个返回值retValue,相当于执行retValue = result
// 检查是否有defer,如果有则执行
return result
}
// 命名返回值
func namedReturnValues() (result int) {
// return和defer是“同时”执行的
defer func() {
result++
fmt.Println("defer")
}()
// 由于返回值在方法定义时已经被定义,所以没有创建retValue的过程
// result就是retValue,defer对于result的修改也会被直接返回
return result
}
func main() {
r := returnValues()
fmt.Println(r) // 0
k := namedReturnValues()
fmt.Println(k) // 1
}
循环中定义defer可能导致大量的资源开销
判断执行没有err之后,再defer释放资源
resp, err := http.Get(url)
// 先判断操作是否成功
if err != nil {
return err
}
// 如果操作成功,再进行Close操作
defer resp.Body.Close()
调用os.Exit时defer不会被执行
func deferExit() {
defer func() {
fmt.Println("defer")
}()
os.Exit(0)
}
3.chan
- 如果说 goroutine 是 Go语言程序的并发体的话
- 那么 channels 就是它们之间的通信机制
- 通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序
- 通道是引用类型,需要使用 make 进行创建
ch1 := make(chan int)
package main
import (
"fmt"
"time"
)
func testChan() {
// 构建一个通道
ch := make(chan int)
// 开启一个并发匿名函数
go func() {
fmt.Println("start goroutine")
// 通过通道通知main的goroutine
ch <- 0
fmt.Println("exit goroutine")
}()
fmt.Println("wait goroutine")
// 等待匿名goroutine
<-ch
fmt.Println("all done")
}
func main() {
testChan()
// 构建一个通道
ch := make(chan int)
// 开启一个并发匿名函数
go func() {
// 从3循环到0
for i := 3; i >= 0; i-- {
// 发送3到0之间的数值
ch <- i
// 每次发送完时等待
time.Sleep(time.Second)
}
}()
// 遍历接收通道数据
for data := range ch {
// 打印通道数据
fmt.Println(data)
// 当遇到数据0时, 退出接收循环
if data == 0 {
break
}
}
}
// 运行结果为 3 2 1 0
4.goto通过标签进行代码间的无条件跳转
package main
import "fmt"
func main() {
for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
if y == 2 {
// 跳转到标签
goto breakHere
}
}
}
// 手动返回, 避免执行进入标签
return
// 标签
breakHere:
fmt.Println("done") // 输出 done
}
5.switch
Go语言的 switch 要比C语言的更加通用
表达式不需要为常量,甚至不需要为整数
case 按照从上到下的顺序进行求值,直到找到匹配的项
package main
import "fmt"
func main() {
var a = "hello"
switch a {
case "hello":
fmt.Println(1)
case "world":
fmt.Println(2)
default:
fmt.Println(0)
}
}
预定义标识符 36个
append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int8 int16 uint32 int32
int64 iota len make new nil panic uint64 print println real recover
string true uint uint8 uintptr
变量声明
var age int;
Go语言数据类型
布尔型
true 或者 false
var b bool = true
数字类型
int float32 float64
uint8 无符号8位整型
uintptr
complex64 complex128
字符串类型
一串固定长度的字符连接起来的字符序列
派生类型
指针类型 Pointer
数组类型
结构化类型 struct
Channel 类型
函数类型
切片类型
接口类型 interface
Map类型
变量声明
如果没有初始化则变量默认为零值
数值类型(包括complex64/128)为 0
布尔类型为 false
字符串为 “”(空字符串)
以下几种类型为 nil
:= 声明新的变量
iota 特殊常量 可以被认为是一个被编译器修改的常量
运算符
算术运算符 + - * / % ++ --
逻辑运算符 &&
(and) ||
(or) !
(not)
位运算符 & | ^
<<
(乘以2的n次方) >>
(除以2的n次方)
*
(指针变量)&
(返回变量存储地址)
运算符优先级
优先级5 * / % << >> & &^
优先级4 + - | ^
优先级3 == != < <= > >=
优先级2 &&
优先级1 ||
指针
- 一个指针变量指向了一个值的内存地址
- 当一个指针被定义后没有分配到任何变量时值为 nil
数组和结构体
- 数组可以存储同一类型的数据
- 但在结构体中我们可以为不同项定义不同的数据类型
切片
cap()
可以测量切片最长可以达到多少
range
关键字用于 for 循环中
- 迭代数组(array)
- 切片(slice)
- 通道(channel)
- 集合(map)的元素
go 语言代码
package main
import (
"WorldProject/stringutil"
"fmt"
)
func main() {
// 这是我的第一个程序将结果输出到控制台
/*
这是多行注释的写法
这是多行注释的写法
这是多行注释的写法
*/
fmt.Printf(stringutil.Reverse("Hello World"))
var a *int
var b []int
var c map[string] int
var d chan int
var e func(string) int
var f error
fmt.Printf("%v %v %v %v %v %v", a, b, c, d, e, f)
const (
a1 = iota
b1
c1
d1 = "solomon"
e1
f1 = 100
g1
h1 = iota
i1
)
fmt.Println(a1, b1, c1, d1, e1, f1, g1, h1, i1)
const (
a2 = 1 << iota
b2 = 3 << iota
c2
d2
)
fmt.Println("a2 = ", a2)
fmt.Println("b2 = ", b2)
fmt.Println("c2 = ", c2)
fmt.Println("d2 = ", d2)
var a3 int=20
var ip *int
ip = &a3
fmt.Printf("a3变量的地址是:%x\n", &a3)
fmt.Printf("ip变量储存的指针地址是:%x\n", ip)
fmt.Printf("ip变量的值是:%x\n", *ip)
type Books struct {
title string
author string
subject string
bookID int
}
fmt.Println(Books{"Go 语言", "www.go.org", "Go 语言教程", 6495407})
var number = make([]int, 3, 5)
printSlice(number)
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum = ", sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index = ", i)
}
}
strs := map[string]string{"a": "apple", "c": "banana"}
for k, v := range strs {
fmt.Printf("%s -> %s\n", k, v)
}
for i, c := range "go" {
fmt.Println(i, c)
}
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}