一、main
函数
- 每个Go语言程序都应该有个
main package
-
main package
里的main函数是Go语言程序入口
package main
import (
"os"
)
func main() {
//Go语言通过os.Args获取入参
args := os.Args
if len(args) != 0 {
println("Do not accept any argument")
os.Exit(1)
}
println("Hello world")
}
二、参数解析
- 请注意Go语言main函数与其它语言不同,没有类似Java的
[]string args
参数 - Go语言如何传入参数呢?
- 方法1
fmt.Println("os args is:", os.Args)
- 方法2
name := flag.String("name", "world", "specify the name you want to say hi")
flag.Parse()
fmt.Printf("name: %v", *name)
三、init
函数
- init函数:会在包初始化时运行
- 有时候程序运行之前会有一些初始化动作,需要给变量赋值,或者有一些预先要去运行的程序要先去跑,此时就需要做初始化操作,所以Go语言提供了init函数,也是像main函数一样是一种特殊函数,init函数会在main函数之前被运行。
- 谨慎用init函数
- 当多个依赖项目引用的初始化在init中完成,并且不可重复运行时,会导致启动错误
package main
var myVariable = 0
func init(){
myVariable = 1
}
- 示例
https://github.com/liushiju/golang_examples/tree/main/moudle1/init
输出结果:
init from b
init from a
main init
main function
init方法在main.go和a包中都被引用了一次,b的init的方法只会执行一次。
init方法只会被执行一次,即使有多个包依赖了同一个包。
四、返回值
- 多值返回
- 函数可以返回任意数量的返回值
- 命名返回值
- Go的返回值可被命名,它们会被视作定义在函数顶部的变量
- 返回值的名称应当具有一定的意义,它可以作为文档使用
- 没有参数的return语句返回已命名的返回值,也就是直接返回
func passValue(a,b int)(x,y int){
x=a
y=b
return x,y
}
- 调用者忽略部分返回值
return,_ = strconv.Atoi(origStr)
五、传递变长参数
Go语言中的可变长参数允许调用方传递任意多个相同类型的参数
- 函数定义
func append(slice []Type, elems ... Type) []Type
- 调用方法
myArray := []string{}
myArray = append(myArray,"a","b","c")
六、内置函数
函数名 | 作用 |
close | 管道关闭 |
len,cap | 返回数组,切片,map的长度或容量 |
new,make | 内存分配 |
copy,append | 切片操作 |
panic,recover | 错误处理 |
print,println | 打印 |
complex,real,imag | 操作复数 |
七、回调函数(callback)
- 函数作为参数传入其他函数,并在其他函数内部调用执行
strings.IndexFunc(line,unicode.IsSpace)
Kubernetes controller的leaderelection
- 示例
package main
import "fmt"
func main() {
DoDperation(1,increase)
DoDperation(1,decrease)
}
func increase(a,b int) {
fmt.Println("increase result is:",a+b)
}
func DoDperation(y int,f func(int,int)) {
f(y,1)
}
func decrease(a,b int) {
fmt.Println("decrease result is:",a-b)
}
八、闭包
8.1、匿名函数
- 不能独立存在
- 可以赋值给其他变量
x:=func(){}
- 可以直接调用
func(x,y int){println(x+y)}(1,2)
- 可作为函数返回值
func Add()(func(b int) int)
- 使用场景
defer func(){
if r:= recover();r!=nil{
println("recovered in FuncX")
}
}()
九、方法
9.1、方法:作用在接收者上的函数
func(recv receiver_type) methodName(parameter_list)(return_value_list)
9.2、使用场景
- 很多场景下,函数需要的上下文可以保存在receiver属性中,通过定义receiver的方法,该方法可以直接访问receiver属性,减少参数传递需求
func (s *Server) StartTLS() {
if s.URL != "" {
panic("Server already started")
}
if s.client == nil {
s.client = &http.Client{Transport: &http.Transport{}}
}
}
十、传值还是传指针
- Go语言只有一种规则——传值
- 函数内修改参数的值不会影响函数外原始变量的值
- 可以传递指针参数将变量地址传递给调用函数,Go语言会复制该指针作为函数内的地址,但指向同一地址
十一、接口
- 接口定义一组方法集合
type IF interface{
Method1(param_list) return_type
}
- 适用场景:kubernetes中有大量的接口抽象和多种实现
- struct无需显示声明实现interface,只需直接实现方法
- struct除实现interface定义的接口外,还可以有额外的方法
- 一个类型可实现多个接口(Go语言的多重继承)
- Go语言中接口不接受属性定义
- 接口可以嵌套其他接口
- 示例
package main
import "fmt"
type IF interface {
getName() string
}
type Human struct {
firstName string
lastName string
}
func (h *Human) getName() string {
return h.firstName + "," + h.lastName
}
type Car struct {
factory, model string
}
func (c *Car) getName() string {
return c.factory + "-" + c.model
}
func main() {
interfaces := []IF{}
h := new(Human)
h.firstName = "first"
h.lastName = "last"
interfaces = append(interfaces, h)
c := new(Car)
c.factory = "benz"
c.model = "s"
interfaces = append(interfaces, c)
for _, f := range interfaces {
fmt.Println(f.getName())
}
}
- 注意
interface可能为nil,所以针对interface的使用一定要预先判空,否则会引起程序crash(nil panic)
struct初始化意味着空间分配,对struct的引用不会出现空指针
十二、反射机制
-
reflect.TypeOf()
返回被检查对象的类型 -
reflect.ValueOf()
返回被检查对象的值 - 示例
package main
import (
"fmt"
"reflect"
)
func main() {
// basic type
myMap := make(map[string]string, 10)
myMap["a"] = "b"
t := reflect.TypeOf(myMap)
fmt.Println("type:", t)
v := reflect.ValueOf(myMap)
fmt.Println("value:", v)
// struct
myStruct := T{A: "a"}
v1 := reflect.ValueOf(myStruct)
for i := 0; i < v1.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, v1.Field(i))
}
for i := 0; i < v1.NumMethod(); i++ {
fmt.Printf("Method %d: %v\n", i, v1.Method(i))
}
// 需要注意receive是struct还是指针
result := v1.Method(0).Call(nil)
fmt.Println("result:", result)
}
type T struct {
A string
}
// 需要注意receive是struct还是指针
func (t T) String() string {
return t.A + "1"
}
十三、基于struct
的反射
// struct
myStruct := T{A: "a"}
v1 := reflect.ValueOf(myStruct)
for i := 0; i < v1.NumField(); i++ {
fmt.Printf("Field %d: %v\n"
, i, v1.Field(i))
}
for i := 0; i < v1.NumMethod(); i++ {
fmt.Printf("Method %d: %v\n"
, i, v1.Method(i))
}
// 需要注意 receive 是 struct 还是指针
result := v1.Method(0).Call(nil)
fmt.Println("result:", result)
十四、Go语言中的面向对象编程
- 可见性控制
- public:常量、变量、类型、接口、结构、函数等的名称大写
- private:非大写就只能在包内使用
- 继承
- 通过组合实现,内嵌一个或多个struct
- 多态
- 通过接口实现,通过接口定义方法集,编写多态实现
十五、JSON编解码
package main
import (
"encoding/json"
"fmt"
)
func main() {
humanStr := `{"Name":"liushiju","Age":26}`
res := unmarshal2Struct(humanStr)
fmt.Printf("json.Unmarshal 从string转换至struct解码结果: %v, %v\n", res.Name, res.Age)
h := Human{}
m2s := marshal2JsonString(h)
fmt.Printf("json.Marshal 从struct转换至string编码结果: %v\n", m2s)
}
type Human struct {
Name string `json:"name"`
Age int `json:"age"`
}
// Unmarshal:从string转换值struct
func unmarshal2Struct(humanStr string) Human {
// json.Unmarshal 需要字节数组参数, 需要把字符串转为 []byte 类型
// 用来接收解码后的结果
h := Human{}
// []byte(humanStr字符串转为字节数组
err := json.Unmarshal([]byte(humanStr), &h)
if err != nil {
fmt.Println(err)
}
return h
}
// Marshal:从struct转换至string
func marshal2JsonString(h Human) string {
h.Age = 30
h.Name = "张三"
updateBytes, err := json.Marshal(&h)
if err != nil {
fmt.Println(err)
}
return string(updateBytes)
}
- json包使用map[string]interface{}和[]interface{}类型保存任意对象
- 可通过如下逻辑解析任意JSON
package main
import (
"encoding/json"
"fmt"
)
func main() {
var obj interface{}
humanStr := `{"Name":"liushiju","Age":26}`
err := json.Unmarshal([]byte(humanStr), &obj)
if err == nil {
fmt.Printf("string to struct: %v", obj)
}
//obj.(map[string]interface{})是将obj转换称为map类型
objMap, ok := obj.(map[string]interface{})
for k, v := range objMap {
switch value := v.(type) {
case string:
fmt.Printf("type of %s is string is %v\n", k, value)
case interface{}:
fmt.Printf("type of %s is interface{},value is %v\n", k, value)
default:
fmt.Printf("type of %s is wrong,value is %v\n", k, value)
}
}
fmt.Println(ok)
}
十六、错误处理
- Go语言无内置exception机制,只提供error接口供定义错误
type error interface{
Error() string
}
- 可通过errors.New或fmt.Errorf创建新的error
- 通常应用程序对error的处理大部分是判断error是否为nil
如需将error归类,通常交给应用程序定义,比如kubernetes自定义了与apiserver交互的不同类型错误
type StatusError struct{
ErrStatus metav1Status
}
var _error = &StatusError{}
// Error implements the Error interface.
func (e *StatusError) Error() string {
return e.ErrStatus.Message
}
十七、defer
- 函数返回之前执行某个语句或函数
- 等同于Java和c#的finally
- 常见的defer使用场景:记得关闭打开的资源
defer file.Close()
defer mu.Unlock()
defer println("")
- 示例
package main
import (
"fmt"
"sync"
"time"
)
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
loopFunc()
time.Sleep(time.Second)
}
func loopFunc() {
lock := sync.Mutex{}
for i := 0; i < 3; i++ {
go func(i int) {
lock.Lock()
defer lock.Unlock()
fmt.Printf("loopFunc: %v\n", i)
}(i)
}
}
十八、panic和recover
- panic:可在系统出现不可恢复错误时主动调用panic,panic会使当前线程直接crash
- defer:保证执行并把控制权交给接受到panic的函数调用者
- recover:函数从panic或错误场景中恢复
defer func(){
fmt.Println("defer func is called")
if err := recover();err != nil{
fmt.Println(err)
}
}()
panic("a panic is triggered")
=== END ===