[go-06] 函数、包和错误处理

一、函数语法
fun 函数名(参数列表) (返回值列表)  {
    // 执行语句
    return 返回值列表
}
函数名大写外包可以访问 类似 public 小写就是私有的
二、包
1)  Go文件必须归属于一个包,不能单独存在
2) 打包基本语法
package  包名
3) 引入包的基本语法
import “包的路径”
4)示例案例 
package main // 尽量保持 package 的名字和目录名一致

import "fmt" // 引入fmt包

func main() {
    // 函数名大写外包可以访问 类似 public 小写就是私有的
    fmt.Printf("hello go") // 使用fmt包里面的Printf函数
}
二、包的使用细节
1)  尽量保持 package 的名字和目录名一致,一般为小写字母。
2) 当一个文件要使用其它包函数或变量时,需要先引入对应的包
方式 1 import " 包名 "
方式 2
import (
"包名"
"包名"
)
package meaningfulTagController  // 第一行package

import ( // 然后是import
    "github.com/gin-gonic/gin"
    ut "hy/utils" // 给包取别名,取别名后原名不可用
    
    // 包名是从 $GOPATH/src/ 后开始计算的,使用 / 进行路径分隔

)
3) 包里面的函数:函数名大写外包可以访问类似public 小写就是私有的
4) 在访问其它包函数,变量时,其语法是 包名 . 函数名
5)  一个目录下不能有重复的函数和全局变量
func main() {
    n1 := 88
    // _忽略返回值
    _ = test(n1)
    _, n3 := test2(n1) // 88
    fmt.Println(n1, n3) // 22
}

// 返回值列表只有1个,不用()
func test(n1 int) int {
    n2 := n1 + 11
    fmt.Println(n2) // 99
    return n2
}

// 函数多返回值
func test2(n1 int) (int, int) {
    return n1, 22
}

(1) 遇到函数,开辟一个新栈区,每一个栈区都是独立的,数据也是相互隔离的
(2) 当一个函数调用完毕 ( 执行完毕 ) 后,程序会销毁这个函数对应的栈空间
四、函数递归
(1) 自身调用自身,一定要注意终止退出
五、函数使用注意细节
1) 函数的形参列表,和返回值可以是多个,可以说是值类型和引用类型
2) 函数的命名,首字母不能是数字,首字母大写是public 首字母小写是private
3) 函数中的变量是局部的,函数外不生效
4) 基本数据类型 数组 默认都是 值传递的 ,即进行值拷贝。在函数内修改,不会影响到原来的值。
5)  引用传值用&
6) Go 函数不支持函数重载
(重载:同名不同参的函数,重写,子类重新定义父类同名同参的函数,从而实现多态)
8) Go 中, 函数也是一种数据类型 ,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该 变量可以对函数调
9) 函数既然是一种数据类型,因此在 Go 中,函数可以作为形参,并且调用
10) Go 支持自定义数据类型 基本语法:type 自定义数据类型名 数据类型
// 函数也是一种数据类型
func f1() {
    // 匿名函数赋值给f3,f3就是函数类型的变量
    f3 := func(n1 int, n2 int) int {
       return n1 + n2
    }

    // go中函数可以作为一个形参,并且调用(php js一样)
    res := f2(f3, 20, 30)
    fmt.Println(res)

    // 类型取别名
    type myint int
    var n myint = 1
    fmt.Printf("n的类型是%T,值是%v", n, n)
}

func f2(f3 func(int, int) int, n1 int, n2 int) int {
    return f3(n1, n2)
}

// 自定义函数类型
type fun3 func(int, int) int

func f4(f3 fun3, n1 int, n2 int) int {
    return f3(n1, n2)
}

11) 支持对函数返回值命名
func sumAndSub(a float64, b float64) (sum float64, sub float64) {
    sum = a + b // 中间是个等号哦
    sub = a - b
    return // 不需要写具体发返回值
}
12) 使用 _ 标识符,忽略返回值
13) Go 支持可变参数
// 函数的可变参数
func allSum(args ...int) int {
    // args 是一个切片,通过args[index]可以访问到对应的值
    sum := 0
    for _, v := range args {
       sum = sum + v
    }


    fmt.Println(sum)
    return sum
}
六、init函数(有点像PHP的__controuct())
(1) 每一个源文件都可以包含一个 init 函数 ,执行顺序,全局变量->init函数->main函数
2)  init 函数最主要的作用,就是完成一些 初始化的工作
3) 多个包有init时,执行流程
七、匿名函数
(1) 在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。
(2) 将 匿名函数赋给一个变量 ( 函数变量 ) ,再通过该 变量来调用匿名函数
(3) 赋值给全局变量,则全局有效
func anonymou() {
    // 直接调用
    res1 := func(n1 int, n2 int) int {
       return n1 + n2
    }(10, 11)


    fmt.Println(res1)


    // 通过变量调用
    a := func(n1 int, n2 int) int {
       return n1 + n2
    }


    res2 := a(1, 77)
    fmt.Println(res2)
}
八、闭包
闭包就是 一个函数 和与 其相关的引用环境 组合的 一个整体 ( 实体 )
闭包 = 匿名函数 + 外部变量 | 参数
func bibao() {
    f := add()
    fmt.Println(f(1)) // 11
    fmt.Println(f(2)) // 13
    fmt.Println(f(3)) // 16
}


func add() func(int) int {
    n := 10
    return func(i int) int {
       n = n + i
       return n
    }
}
1) 闭包 返回的数据类型是 fun (int) int
2)  闭包中使用的变量 / 参数会一直保持在内存中,所以会一直使用 ---> 意味着闭包不可滥用
3) 当我们反复的调用 f 函数时,因为 n 是初始化一次,因此每调用一次就进行累计。
九、函数的defer
1) go 执行遇到defer先入栈 [认  是 defer ],函数执行完,再依次执行(栈,先入后出)
2) defer入栈时,连同参数值一同拷贝入栈
func godefer() {
    m := 10
    n := 22
    defer fmt.Println("第一个打印m", m)
    defer fmt.Println("第二个打印n", n)


    m++
    n++
    fmt.Println("第三个打印m, n", m, n)


    // 第三个打印m, n 11 23
    // 第二个打印n 22
    // 第一个打印m 10
}
defer的主要作用
1) 主要用来释放资源,比如 数据库,redis连接,文件句柄等
2) defer 后,可以继续使用创建资源 .
3) 当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源 .
4) 这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心。
十、函数的值传递
1) 值类型 :基本数据类型 int 系列 , float 系列 , bool, string 、数组和结构体 struct
2) 引用类型 :指针、 slice 切片、 map 、管道 chan interface
3) 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址 & ,函数内以指针的方式操作变量。
十一、变量作用域
1) 函数内部声明 / 定义的变量叫局部变量, 作用域仅限于函数内
2) 函数外部声明 / 定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
3) 如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块
十二、字符串常用的系统函数
1) 统计字符串的长度 len(str)
str := "hou子"
fmt.Println(len(str)) // 6 一个中文三个字节
2) 字符串遍历,同时处理有中文的问题 r := []rune(str)
// 方式1 for i,v := range str {}
for i, val := range str {
    //fmt.Println("i的值是", i, "val的值是", val)
    /*
       i的值是 0 val的值是 104
       i的值是 1 val的值是 111
       i的值是 2 val的值是 117
       i的值是 3 val的值是 23376
    */
    fmt.Printf("索引%d,值%c \n", i, val)
    /*
       %c 把整数转成Unicode字符
       索引0,值h
       索引1,值o
       索引2,值u
       索引3,值子
    */
}

// 方式2 r := [] rune(str)
r := []rune(str)
for i := 0; i < len(r); i++ {
    fmt.Println(r[i]) // 同样输出的是整数
    fmt.Printf("%c", r[i])
}
3) 字符串转整数 :n, err := strconv.Atoi("12”)
str := "123"
fmt.Printf("类型是:%T", str) // 类型是:string

num, _ := strconv.Atoi(str)
fmt.Printf("类型是:%T", num) // 类型是:int
func Atoi
func Atoi(s string) (i int, err error)
4) 整数转字符串 str = strconv.Itoa(12345)
num2 := 111
str2 := fmt.Sprintf("%d", num2)
fmt.Printf("%T\n", str2) // string
fmt.Printf("%T\n", strconv.Itoa(num2)) // string
func  Itoa
func Itoa(i int) string  
5) 字符串 转 []byte: var bytes = []byte("hello go”)
str3 := "hello"
byte1 := []byte(str3)
fmt.Println(byte1) // [104 101 108 108 111]
fmt.Printf("%c", byte1) // [h e l l o]
6) []byte 转 字符串 : str = string([]byte{97, 98, 99})
 
str3 := "hello"
byte1 := []byte(str3)
str4 := string(byte1)
fmt.Println(str4) // hello
7) 10 进制转 2, 8, 16 进制 : str = strconv.FormatInt(123, 2) // 2-> 8 , 16
func  FormatInt
func FormatInt(I int64, base int) string
base 必须在 2 到 36 之间
8) 查找子串是否在指定的字符串中 : strings.Contains("seafood", "foo") //true
func Contains
func Contains(s, substr string) bool
PHP: strstr(‘字符串’, ‘包含的字符')  //  搜索一个字符串在另一个字符串中的第一次出现
9) 统计一个字符串有几个指定的子串 : strings.Count("ceheese", "e") //4
func Count
func Count(s, sep string) int
10) 不区分大小写的字符串比较 (== 是区分字母大小写的 ): strings.EqualFold("abc","Abc") // true
func  EqualFold
func   EqualFold(s, t string) bool
11) 返回子串在字符串第一次出现的 index 值,如果没有返回 -1 : strings.Index("NLT_abc", "abc") // 4
12) 返回子串在字符串最后一次出现的 index ,如没有返回-1 : strings.LastIndex("go golang", "go”)
13) 将指定的子串替换成 另外一个子串 : strings.Replace("go go hello", "go", "go 语言 ", n) n 定替换几个, n=-1 全替换
14) 按 照 指 定 的 某 个 字 符  为 分 割 标 识 , 将 一 个 字 符 串 拆 分 成 字 符 串 数 组 strings.Split("hello,wrold,ok", ",”)
15) 将字符串的字母进行大小写的转换 : strings.ToLower("Go") // go strings.ToUpper("Go") // GO
16) 将字符串左右两边的空格去掉: strings.TrimSpace(" tn a lone gopher ntrn    “)
17) 将字符串左右两边指定的字符去掉 : strings.Trim("! hello! ", " !") // ["hello"] // 将左右两边 !和 " “去掉
18) 将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! ", " !") // ["hello"] // 将左边 !  “ "去掉
19) 将字符串右边指定的字符去掉 : strings.TrimRight("! hello! ", " !") // ["hello"] // 将右边 !  “ "去掉
20) 判断字符串是否以指定的字符串开头 : strings.HasPrefix(" ftp://192.168.10.1 ", "ftp") // true
21) 判断字符串是否以指定的字符串结束 : strings.HasSuffix("NLT_abc.jpg", "abc") //false
十三、时间日期函数
1) time包
2) 当前时间 time.Now()  // 2023-10-18 17:17:38.880028 +0800 CST m=+0.000075126
3) 获取其他格式
now := time.Now()
fmt.Printf("now = %v   now type = %T \n", now, now)
// now = 2023-10-18 17:19:51.866644 +0800 CST m=+0.000069460   now type = time.Time

fmt.Printf("年=%v\n", now.Year())       // 年=2023
fmt.Printf("月=%v\n", now.Month())      // 月=October
fmt.Printf("月=%v\n", int(now.Month())) // 月=10
fmt.Printf("日=%v\n", now.Day())        // 日=18
fmt.Printf("时=%v\n", now.Hour())       // 时=17
fmt.Printf("分=%v\n", now.Minute())     // 分=22
fmt.Printf("秒=%v\n", now.Second())     // 秒=25
4) 格式化日期时间
方式 1: 就是使用 Printf 或者 SPrintf
方式二 : 使用 time.Format() 方法完成
fmt.Printf("%d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
//2023-10-18 17:35:46
date := fmt.Sprintf("%d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
fmt.Printf("%v\n", date)
//2023-10-18 17:35:46

fmt.Println(now.Format("2006-01-02 15:04:05")) // 这个字符串的各个数字是固定的,必须是这样写。数字可以自由组合 2023-10-18 17:35:46
fmt.Println(now.Format("2006-01-02")) // 023-10-18
5) 时间的常量
const (
Nanosecond Duration = 1 // 纳秒
Microsecond = 1000 * Nanosecond //微秒
Millisecond  = 1000 * Microsecond //毫秒
Second  = 1000 * Millisecond //秒
Minute  = 60 * Second //分钟
Hour  = 60 * Minute //小时
)
常量的作用 : 在程序中可用于获取指定时间单位的时间,比如想得到 100 毫秒100 * time. Millisecond
//需求,每隔1秒中打印一个数字,打印到100时就退出
//需求2:每隔0.1秒中打印一个数字,打印到100时就退出
i := 0
for {
    i++
    fmt.Println(i)
    //休眠
    //time.Sleep(time.Second)
    time.Sleep(time.Millisecond * 100)
    if i == 100 {
       break
    }
}
func Unix
func Unix(sec int64, nsec int64) Time
Unix 创建一个本地时间,对应 sec 和 nsec 表示的 Unix 时间(从 January 1, 1970 UTC 至该时间的秒数和纳秒数)。
 
// 获取当前时间戳
timeUnix := time.Now().Unix()
fmt.Println(timeUnix)
十三、内置函数
1) len :用来求长度,比如 string array slice map channel
2) new :用来分配内存,主要用来分配值类型,比如 int float32,struct... 返回的是指针
3) make :用来 分配内存 ,主要用来 分配引用类型 ,比如 channel map slice
十四、错误处理
1) Go 语言追求简洁优雅,所以, Go 语言不支持传统的 try catch finally 这种处理。
2) Go 中引入的处理方式为: defer , panic , recover
3) 这几个异常的使用场景可以这么简单描述: Go 中可以抛出一个 panic 的异常,然后在 defer 通过 recover 捕获这个异常,然后正常处理
defer func() {
    err := recover() // 内置函数捕获异常
    if err != nil {
       fmt.Println("err:", err) // err: runtime error: integer divide by zero
    }
}()

num := 5
num2 := 0
num3 := num / num2
fmt.Println(num3)
十五、自定义错误
Go 程序中,也支持自定义错误, 使用 errors.New panic 内置函数。
1) errors.New(" 错误说明 ") , 会返回一个 error 类型的值,表示一个错误
2) panic 内置函数 , 接收一个 interface{} 类型的值(也就是任何值了)作为参数。可以接收 error
型的变量, 输出错误信息 并退出程序 .
  • 43
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值