最近看了一下GO语言,学习一下。记录一下基础代码块,方便查阅。
参考教程:
https://www.runoob.com/go
http://c.biancheng.net/golang
变量声明
package main
import "fmt"
// 多行声明
var (
a int
b bool
c = "123"
)
func main() {
x := 1
fmt.Println(x)
test := "test"
fmt.Println(test)
var x1, x2, x3 = "1", "2", "3"
fmt.Println(x1, x2, x3)
y1, y2, y3 := "4", "5", "6" // 这种不带声明的只能出现在函数体
fmt.Println(y1, y2, y3)
fmt.Println(a, b, c)
/*
在Go中,声明的变量必须使用,否则不能通过编译。
下划线"_"在Go中表示一个空白标识符,它是一个只写变量,可以把不需要的变量赋值给它。
*/
_, a := 1, 2
fmt.Println(a)
}
变量打印
package main
import "fmt"
func main() {
var test string = "test" // string变量类型可以省略,编译器会根据所赋的值来识别
fmt.Println(test)
var x, y int = 1, 2
fmt.Println(x, y)
}
变量空值
package main
import "fmt"
func main() {
var varInt int
var varStr string
var varFloat64 float64
var varBool bool
var varList []int
fmt.Println(varInt, varStr, varFloat64, varBool, varList)
}
类型转换
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
a := 123
b := float32(a)
// 数字转字符串
c := strconv.Itoa(a)
// 字符串转数字
d, _ := strconv.Atoi(c)
e, _ := strconv.ParseInt(c, 10, 64)
fmt.Println(reflect.TypeOf(a), a)
fmt.Println(reflect.TypeOf(b), b)
fmt.Println(reflect.TypeOf(c), c)
fmt.Println(reflect.TypeOf(d), d)
fmt.Println(reflect.TypeOf(e), e)
}
常量
package main
import "fmt"
// 用作枚举
const (
Unknown = 0
Female = 1
Male = 2
)
// 常量是一个简单值的标识符,在程序运行时,不会被修改的量
func main() {
const a = "test" // string类型,使用双引号
const b = 'A' // char类型,使用单引号
const c, d = '1', '2' // 常量可以声明定义,并且不使用
fmt.Println(a)
fmt.Println(b) // 打印的是ASCII码
// iota常量索引,每次 const 出现时,都会让 iota 初始化为0
const x = iota // x=0
const ( // 出现const,iota初始化为0
y = iota //y=0
z //z=1 相当于z=iota
)
}
数组
package main
import "fmt"
func printGroup(group []int) { // 这里接收参数的类型必须和定义时一样
fmt.Println("数组长度:", len(group))
for i := range group {
fmt.Println(i)
}
}
// 与Python的数组不同,GO语言中的数组长度不可改变
func main() {
group1 := [5]int{1, 2, 3} // 不足元素补充空值
fmt.Println(group1)
group1[3] = 4 // 赋值
fmt.Println(group1)
group2 := []int{1, 2, 3} // 不定长度使用...,编译器自动计算长度
group3 := []int{1, 2, 3, 4, 5} // 不定长度使用...,编译器自动计算长度
printGroup(group2)
printGroup(group3)
}
切片slice
package main
import "fmt"
// slice相当于动态数组
func main() {
var s []int // 未指定长度的数组其实就是切片
if s == nil { // 判断空切片
fmt.Println("空切片:", s)
}
s = make([]int, 10, 20) // 数组长度10,容量20
fmt.Println("初始化切片:", s)
for i, _ := range s {
s[i] = i
}
fmt.Println("切片循环赋值:", s)
fmt.Println("切片截取:")
fmt.Println(s[2:5])
fmt.Println(s[:5])
fmt.Println(s[5:])
fmt.Println(s[5 : len(s)-2]) // 好像没有直接s[5:-2]的写法
fmt.Println(s)
fmt.Println("cap容量:", cap(s))
fmt.Println("len长度:", len(s))
for i := 0; i < 50; i++ {
// 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
s = append(s, 10+i) // 插入元素
}
fmt.Println(s)
fmt.Println("cap容量:", cap(s)) // 容量不足时会自动扩容,但是容量和长度不是同等的计算方式
fmt.Println("len长度:", len(s))
// 拷贝切片,目标在前
var n []int
n = make([]int, 10, 20)
copy(n, s)
fmt.Println("拷贝的切片n:", n)
copy(n, []int{0, 0, 0, 0, 0})
fmt.Println("拷贝的切片n:", n)
fmt.Println("拷贝的切片s:", s)
// 删除元素
slice1 := []int{1, 2, 3, 4, 5}
fmt.Println(slice1)
// 删除第一个元素
slice2 := slice1[1:]
fmt.Println(slice2)
// 删除最后一个元素
slice3 := slice1[:len(slice1)-1]
fmt.Println(slice3)
// 删除第x个元素(n<=len)
x := 2
slice4 := append(slice1[:x-1], slice1[x:]...)
fmt.Println(slice4)
}
range
package main
import "fmt"
// range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
func main() {
// 遍历数组
numArr := [...]int{1, 2, 3}
var sum int
for index, num := range numArr {
fmt.Printf("第%d个数字是:%d\n", index+1, num)
sum += num
}
fmt.Println("sum:", sum)
// 遍历切片,循环10次
count := 0
for range make([]int, 10) {
fmt.Println(count)
count++
}
// 遍历字符串
for _, c := range "hello world" {
fmt.Println(c)
}
}
map
package main
import "fmt"
func main() {
// 声明一个map,key为string类型,value也是string类型
weekMap := map[string]string{
"Mon": "星期一",
"Tues": "星期二",
"Wen": "星期三",
"Thur": "星期四",
"Fri": "星期五",
"Sat": "星期六",
"Sun": "星期天",
}
fmt.Println(weekMap)
fmt.Println(weekMap["Mon"])
// 删除元素
delete(weekMap, "Sun")
fmt.Println(weekMap)
// 插入元素
weekMap["Sun"] = "星期天"
fmt.Println(weekMap)
}
并发map
package main
import (
"fmt"
"sync"
)
func main() {
syncMap := sync.Map{}
// 存储值
syncMap.Store("test1", 1)
// 可以储存不同的类型
syncMap.Store("test2", "2")
// 读取值
fmt.Println(syncMap.Load("test1"))
fmt.Println(syncMap.Load("test2"))
// 遍历
syncMap.Range(func(key, value interface{}) bool {
fmt.Println(key)
fmt.Println(value)
return true // 返回false就不继续循环
})
}
指针
package main
import "fmt"
func swapPoint(a, b *int) {
var tmp int
tmp = *a
*a = *b
*b = tmp
}
func main() {
a := 10
var ip *int
fmt.Println("空指针:", ip)
ip = &a
fmt.Println("a的值:", a)
fmt.Println("a的地址:", &a)
fmt.Println("ip的值:", ip)
fmt.Println("ip的指向:", *ip)
// 指针数组
const MAX = 3
arr := [MAX]int{1, 2, 3}
var ptr [MAX]*int
for i := 0; i < MAX; i++ {
ptr[i] = &arr[i]
}
fmt.Println("指针数组的值:", ptr)
for i := range ptr {
fmt.Println("指向的值:", i)
}
// 指向指针的指针
var v int
var vptr *int
var vpptr **int
v = 300
vptr = &v
vpptr = &vptr
fmt.Println("v的值", v)
fmt.Println("ptr的值", *vptr)
fmt.Println("pptr的值", **vpptr)
// 向函数传递指针
x1, x2 := 1, 9
var ptr1, ptr2 *int
ptr1 = &x1
ptr2 = &x2
fmt.Println(x1, x2)
swapPoint(ptr1, ptr2)
fmt.Println(x1, x2)
}
结构体
package main
import "fmt"
type Books struct {
name string
author string
pages uint8
}
func (b Books) print() {
fmt.Printf("书本名称: %s\n作者: %s\n页码: %d\n", b.name, b.author, b.pages)
}
func main() {
b1 := Books{
name: "Hello",
author: "FreezeJ",
pages: 80,
}
b2 := Books{
name: "World",
author: "FreezeJ",
pages: 38,
}
b1.print()
b2.print()
// 结构体指针
var bp1, bp2 *Books
bp1, bp2 = &b1, &b2
// 地址
fmt.Println(bp1)
fmt.Println(bp2)
// 值
fmt.Println(*bp1)
fmt.Println(*bp2)
// 引用内容
fmt.Println(bp1.name)
fmt.Println(b1.name)
fmt.Println(bp2.name)
fmt.Println(b2.name)
}
switch
package main
import (
"fmt"
"strconv"
)
const (
Mon = "Monday"
Tues = "Tuesday"
Wen = "Wednesday"
Thur = "Thursday"
Fri = "Friday"
Sat = "Saturday"
Sun = "Sunday"
)
func main() {
var day string
fmt.Println("请输入:")
fmt.Scanf("%s", &day)
// 形式一
switch day {
case Mon:
fmt.Println("星期一")
case Tues:
fmt.Println("星期二")
case Wen:
fmt.Println("星期三")
case Thur:
fmt.Println("星期四")
case Fri:
fmt.Println("星期五")
case Sat:
fmt.Println("星期六")
case Sun:
fmt.Println("星期天")
default:
fmt.Println("日期错误")
return
}
// 形式二,类似于使用if...else...
switch {
case day == Sat, day == Sun:
fmt.Println("周末")
default:
fmt.Println("要上班")
}
// fallthrough关键字,使用后case匹配不跳出,继续执行下一个case
leftDay := 0
switch day {
case Mon:
leftDay++
fallthrough
case Tues:
leftDay++
fallthrough
case Wen:
leftDay++
fallthrough
case Thur:
leftDay++
fallthrough
case Fri:
leftDay++
}
if leftDay > 0 {
fmt.Println("还有" + strconv.Itoa(leftDay) + "天放假!")
}
}
if语句
package main
import "fmt"
func main() {
var value int
fmt.Scan(&value) // 输入数字
if value > 1 {
fmt.Println("大于1")
} else if value > 999 {
fmt.Println("大于999")
} else {
fmt.Println("小于或等于1")
}
}
循环
package main
import "fmt"
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
// 类似于while
count := 1
for count < 10 {
fmt.Printf("计数器:%d\n", count)
count += 1
}
// for each 形式
strings := []string{"google", "baidu", "bing"}
for i, x := range strings {
fmt.Println(i, x)
}
// 嵌套循环:九九乘法表
for x := 1; x < 10; x++ {
for y := 1; y <= x; y++ {
z := x * y
fmt.Printf("%d*%d=%d ", x, y, z)
}
fmt.Println("")
}
// break语句
fmt.Println("等于5时跳出:")
for i := 0; true; i++ {
fmt.Printf("%d\n", i)
if i == 5 {
break
}
}
// continue语句
fmt.Println("10以内的单数:")
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue
}
fmt.Printf("%d\n", i)
}
// goto语句
n := 1
AddN:
n++
if n < 10 {
goto AddN
} else {
fmt.Println(n)
}
}
函数
package main
import "fmt"
// 数字乘以2
func double(i int) int {
return 2 * i
}
func swap(a *int, b *int) {
fmt.Println("a的内存地址:", a)
fmt.Println("a的值:", *a)
fmt.Println("b的内存地址:", b)
fmt.Println("b的值:", *b)
var c int
c = *b
*b = *a
*a = c
fmt.Println("a的内存地址:", a)
fmt.Println("a的值:", *a)
fmt.Println("b的内存地址:", b)
fmt.Println("b的值:", *b)
}
func hello() {
fmt.Println("hello!")
}
func doSomething(data string, work func(string)) {
work(data)
}
// 可变参数
func myFunc(args ...string) {
for _, arg := range args {
fmt.Println(arg)
}
}
func main() {
x, y, z := 2, 6, 3
fmt.Println("x, y, z:")
fmt.Printf("%d %d %d\n", x, y, z)
// 函数返回值
fmt.Println("double x, y, z:")
fmt.Printf("%d %d %d\n", double(x), double(y), double(z))
// 使用引用修改值
a, b := 33, 44
fmt.Println("交换前", a, b)
swap(&a, &b)
fmt.Println("交换后", a, b)
// 函数保存为变量
var h func()
h = hello
h()
// 匿名函数声明并调用
func(data string) {
fmt.Println("hello!", data)
}("world")
// 匿名函数保存为变量
f := func() {
fmt.Println("good!")
}
f() // 调用匿名函数
// 匿名函数用作回调函数
doSomething("test", func(data string) {
fmt.Println(data)
})
// 可变参数
myFunc("1", "2", "3")
strArray := []string{"4", "5", "6"}
myFunc(strArray...)
}
接口
package main
import "fmt"
type Phone interface {
call()
}
type IOS struct {
version int
}
func (ios IOS) call() {
fmt.Println("IOS version:", ios.version)
}
type Android struct {
version int
}
func (android Android) call() {
fmt.Println("Android version:", android.version)
}
func main() {
var p1 Phone
var p2 Phone
p1 = IOS{version: 15}
p2 = Android{version: 14}
p1.call()
p2.call()
}
错误处理
package main
import (
"fmt"
)
// DIV_ERR 自定义错误信息结构
type DIV_ERR struct {
etype int // 错误类型
v1 int // 记录下出错时的除数、被除数
v2 int
}
// 实现接口方法 error.Error()
func (div_err DIV_ERR) Error() string {
if 0 == div_err.etype {
return "除零错误"
} else {
return "其他未知错误"
}
}
// 除法
func div(a int, b int) (int, *DIV_ERR) {
if b == 0 {
// 返回错误信息
return 0, &DIV_ERR{0, a, b}
} else {
// 返回正确的商
return a / b, nil
}
}
func main() {
// 正确调用
v, r := div(100, 2)
if nil != r {
fmt.Println("(1)fail:", r)
} else {
fmt.Println("(1)succeed:", v)
}
// 错误调用
v, r = div(100, 0)
if nil != r {
fmt.Println("(2)fail:", r)
} else {
fmt.Println("(2)succeed:", v)
}
}
panic和recover
package main
import "fmt"
// 任何崩溃都表明了我们的代码中可能存在漏洞,所以对于大部分漏洞,我们应该使用Go语言提供的错误机制,而不是 panic。
func testPanic() {
defer func() {
err := recover()
if err != nil {
fmt.Println("recover")
fmt.Println(err)
}
}()
fmt.Println("start")
panic("raise panic")
fmt.Println("end")
}
func main() {
testPanic()
}
并发
package main
import (
"fmt"
"math/rand"
"time"
)
// 数据生产者
func producer(header string, channel chan<- string) {
// 无限循环, 不停地生产数据
for {
// 将随机数和字符串格式化为字符串发送给通道
channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
// 等待1秒
time.Sleep(time.Second)
}
}
// 数据消费者
func customer(channel <-chan string) {
// 不停地获取数据
for {
// 从通道中取出数据, 此处会阻塞直到信道中返回数据
message := <-channel
// 打印数据
fmt.Println(message)
}
}
func main() {
// 创建一个字符串类型的通道
channel := make(chan string)
// 创建producer()函数的并发goroutine
go producer("cat", channel)
go producer("dog", channel)
// 数据消费函数
customer(channel)
}