GO 快速教程
所属分类 go
浏览量 650
Go是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
go run main.go 或 go run .
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
auto
go build main.go
./main
var a int // 没有赋值,默认为0
var a int = 1 // 声明时赋值
var a = 1 // 声明时赋值
a := 1
msg := "Hello World!"
简单类型
空值 nil
整型 int(取决于操作系统), int8, int16, int32, int64, uint8, uint16, …
浮点数 float32, float64
字节 byte (等价于uint8)
字符串 string
布尔值 boolean,(true 或 false)
var a int8 = 10
var c1 byte = 'a'
var b float32 = 12.2
var msg = "Hello World"
ok := false
字符串 使用 UTF8 编码
英文,每个字符占 1 byte,和 ASCII 编码一样 节省空间
中文一般占3字节
package main
import (
"fmt"
"reflect"
)
func main() {
str1 := "Golang"
str2 := "Go语言"
fmt.Println(len(str1)) // 6
fmt.Println(len(str2)) // 8
fmt.Println(reflect.TypeOf(str2[2]).Kind()) // uint8
fmt.Println(str1[2], string(str1[2])) // 108 l
fmt.Printf("%d %c\n", str2[2], str2[2]) // 232 è
runeArr := []rune(str2)
fmt.Println(len(runeArr)) // 4
fmt.Println(reflect.TypeOf(runeArr[2]).Kind()) // int32
fmt.Println(runeArr[2], string(runeArr[2])) // 35821 语
}
reflect.TypeOf().Kind() 获取变量类型
字符串 byte 数组
中文处理 将 string 转为 rune 数组
rune 数组 字符串中的每个字符,用 int32 表示
数组(array)与切片(slice)
var arr1 [2]int // 一维
var arr2 [2][3]int // 二维
fmt.Println(arr1) // [0 0]
fmt.Println(arr2) // [[0 0 0] [0 0 0]]
var arr3 = [3]int{1, 2, 3}
for i := 0; i < len(arr3); i++ {
arr3[i] += 10
}
fmt.Println(arr3) // [11 12 13 ]
数组长度不可变
拼接2个数组,或 获取子数组,使用切片
切片是数组的抽象 ,使用数组作为底层结构
切片三要素 容量 长度和指向底层数组的指针
切片可以动态扩展
slice1 := make([]float32, 0) // 长度为0的切片
fmt.Println(len(slice1), cap(slice1), slice1) // 0 0 []
slice2 := make([]float32, 2, 3) // 长度为2容量为3的切片
fmt.Println(len(slice2), cap(slice2), slice2) // 2 3 [0,0]
// 添加元素,自动扩展
slice2 = append(slice2, 1, 2, 3)
fmt.Println(len(slice2), cap(slice2), slice2) // 5 6 [0 0 1 2 3]
// 子切片 [start, end)
sub1 := slice2[2:] // [1 2 3]
sub2 := slice2[:2] // [0 0]
sub3 := slice2[1:5] // [0 1 2 3]
// [1 2 3 0 0]
combined := append(sub1, sub2...)
fmt.Println(sub1, sub2, sub3, combined)
声明切片时可设置容量大小,为切片预分配空间 ,容量会自动扩展
sub2... 是切片解构的写法,将切片解构为 N 个独立的元素
字典(键值对,map)
map 类似 java HashMap,Python 字典(dict)
// 仅声明
map1 := make(map[string]int)
// 声明并初始化
map2 := map[string]string{
"A": "a",
"B": "b",
}
map1["dyyx"] = 9
fmt.Println(map1, map2)
指针 pointer
指针即某个值的地址
类型定义时使用符号*,对一个已知的变量,使用 & 获取该变量的地址
str := "Golang"
var pstr *string = &str
*pstr = "Hello"
fmt.Println(str)
指针的用途
函数传递参数
给某个类型定义新的方法
参数按值传递,如果不使用指针,函数内部将会拷贝一份参数的副本,对参数的修改并不会影响外部变量的值
func add(num int) {
num += 1
}
func realAdd(num *int) {
*num += 1
}
num := 8
add(num)
fmt.Println(num) // 8
realAdd(&num)
fmt.Println(num) // 9
流程控制(if, for, switch)
score := 70
if score < 60 {
fmt.Println("fail")
} else {
fmt.Println("pass")
}
type Gender int8
const (
MALE Gender = 1
FEMALE Gender = 2
)
gender := MALE
switch gender {
case FEMALE:
fmt.Println("female")
case MALE:
fmt.Println("male")
default:
fmt.Println("unknown")
}
type 关键字定义新类型
没有枚举(enum) 用常量模拟
switch 不需要 break , 如果需要继续执行 ,使用 fallthrough
sum := 0
for i := 0; i < 10; i++ {
if sum > 50 {
break
}
sum += i
}
for range 遍历 数组(arr) 切片(slice) 字典(map)
nums := []int{10, 20, 30}
for i, num := range nums {
fmt.Println(i, num)
}
map3 := map[string]string{
"a": "A",
"b": "B",
}
for key, value := range map3 {
fmt.Println(key, value)
}
函数(functions)
func funcName(param1 Type1, param2 Type2, ...) (return1 Type3, ...) {
// body
}
func func001(num1 int, num2 int) int {
return num1 + num2
}
func func002(num1 int, num2 int) (int, int) {
return num1 / num2, num1 % num2
}
value := func001(1, 2)
fmt.Println(value)
value1, value2 := func002(7, 3)
fmt.Println(value1, value2)
给返回值命名,简化 return
func func003(num1 int, num2 int) (result int) {
result = num1 + num2
return
}
value3 := func003(2, 3)
错误处理(error handling)
标准库函数 os.Open 读取文件,2个返回值,第一个 *File,第二个 error
调用成功,error 的值是 nil
调用失败,例如文件不存在,error 错误信息
open file_not_exist.txt: no such file or directory
_, err := os.Open("file_not_exist.txt")
if err != nil {
fmt.Println(err)
}
import errors
errors.New 返回自定义的错误
func hello(name string) error {
if len(name) == 0 {
return errors.New("error: name is null")
}
fmt.Println("Hello,", name)
return nil
}
fmt.Println(hello(""))
fmt.Println(hello("dyyx"))
error 一般是能预知的错误
一些不可预知的错误,例如数组越界,这种错误可能会导致程序非正常退出,称之为 panic
func get(index int) int {
arr := [3]int{2, 3, 4}
return arr[index]
}
fmt.Println(get(5))
Java try catch 机制
Go 提供类似机制 defer 和 recover
func get(index int) (ret int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Some error happened!", r)
ret = -1
}
}()
arr := [3]int{2, 3, 4}
return arr[index]
}
fmt.Println(get(1))
fmt.Println(get(7))
get 函数中,使用 defer 定义 异常处理函数
在协程退出前,会执行完 defer 挂载的任务
如果触发了 panic,控制权就交给 defer
在 defer 的处理逻辑中,使用 recover,使程序恢复正常,并且将返回值设置为 -1,
如果不处理返回值,返回值将被置为默认值 0
结构体,方法和接口
type Student struct {
name string
age int
}
func (stu *Student) hello(person string) string {
return fmt.Sprintf("hello %s, I am %s", person, stu.name)
}
student1 := &Student{
name: "dyyx",
}
fmt.Println(student1.hello("cat"))
student2 := new(Student)
fmt.Println(student2.hello("tiger"))
接口定义了一组方法的集合,接口不能被实例化,一个类型可以实现多个接口
type Person interface {
getName() string
}
func (stu *Student) getName() string {
return stu.name
}
type Worker struct {
name string
gender string
}
func (w *Worker) getName() string {
return w.name
}
var person Person = &Student{
name: "dyyx",
age: 18,
}
fmt.Println(person.getName())
(*Student).getName() 删掉 编译报错
*Student does not implement Person (missing getName method)
确保某个类型实现了某个接口的所有方法
var _ Person = (*Student)(nil)
var _ Person = (*Worker)(nil)
接口强制类型转换为实例
student3 := person.(*Student)
fmt.Println(student3)
fmt.Println(student3.hello("codefun007.xyz"))
空接口
没有任何方法 ,可表示任意类型
map4 := make(map[string]interface{})
map4["name"] = "codefun007.xyz"
map4["age"] = 5
map4["scores"] = [3]int{98, 99, 85}
fmt.Println(map4)
并发编程(goroutine)
Go 语言提供了 sync 和 channel 两种方式支持协程(goroutine)的并发
并发下载 N 个资源,多个并发协程之间不需要通信,使用 sync.WaitGroup,等待所有并发协程执行结束
var wg sync.WaitGroup
func download(url string) {
fmt.Println("start to download", url)
time.Sleep(time.Second) // 模拟耗时操作
wg.Done()
}
for i := 0; i < 3; i++ {
wg.Add(1)
go download("http://codefun007.xyz/" + string(i+'0'))
}
wg.Wait()
fmt.Println("Done!")
wg.Add(1) 增加计数
wg.Done() 计数减一
go download() 启动新的协程并发执行 download 函数
wg.Wait() 等待所有的协程执行结束
var ch = make(chan string, 10) // 创建大小为 10 的缓冲信道
func download2(url string) {
fmt.Println("start to download", url)
time.Sleep(time.Second)
ch <- url // 将 url 发送给信道
}
for i := 0; i < 3; i++ {
go download2("http://codefun007.xyz/" + string(i+'0'))
}
for i := 0; i < 3; i++ {
msg := <-ch // 等待信道返回消息
fmt.Println("finish", msg)
}
fmt.Println("Done!")
使用 channel 信道,在协程之间传递消息 ,阻塞等待并发协程返回消息
单元测试(unit test)
// calc.go
package main
func addForTest(num1 int, num2 int) int {
return num1 + num2
}
// calc_test.go
package main
import "testing"
func TestAddForTest(t *testing.T) {
if result := addForTest(1, 2); result != 3 {
t.Error("add(1, 2) should be equal to 3")
}
}
go test
go test -v
包(Package)和模块(Modules)
一个文件夹可以作为 package
// calc.go
package main
func add(num1 int, num2 int) int {
return num1 + num2
}
// main.go
package main
import "fmt"
func main() {
fmt.Println(add(3, 5)) // 8
}
go run main.go
报错 undefined: add
go run main.go calc.go
go run .
包可见性
Public Private
类型/接口/方法/函数/字段
首字母大写, Public ,对其他 package 可见
首字母小写 Private ,对其他 package 不可见
Go Modules 是 Go 1.11 版本之后引入的,Go 1.11 之前使用 $GOPATH 机制
空文件夹,初始化一个 Module
go mod init example
生成 go.mod
记录当前模块名以及所有依赖包的版本
import 模块名/子目录名
完整例子代码
https://gitee.com/dyyx/hellocode/blob/master/demo/go/goquick/main.go
上一篇
下一篇
GO操作Mysql
余杭护国山
职场暗语
go 数组和切片
高庄宸迹
go make 用法