一、​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
}

【基础】Go语言函数_JSON编解码

  • 示例

​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 ===