一、函数语法
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
}
![](https://img-blog.csdnimg.cn/direct/0f1359c6b2ab4b7298cf983bf1617dd1.png)
(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时,执行流程
![](https://img-blog.csdnimg.cn/direct/2acc7da39903415d966d7f3e36d9f065.png)
七、匿名函数
(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
![](https://img-blog.csdnimg.cn/direct/0e36e6b613514cf5a18a92e3b293e25c.png)
2)
引用类型
:指针、
slice
切片、
map
、管道
chan
、
interface
![](https://img-blog.csdnimg.cn/direct/8983af33ac2e459faee1d05facd5d501.png)
3)
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址
&
,函数内以指针的方式操作变量。
![](https://img-blog.csdnimg.cn/direct/62df6cb86b3349b6b767b08c035e646e.png)
十一、变量作用域
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"] //
将右边
!
和 “ "去掉
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
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...
返回的是指针
![](https://img-blog.csdnimg.cn/direct/d2bf1d60c9b84aa0a3fbc6d680516f81.png)
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
类
型的变量,
输出错误信息
,
并退出程序
.
![](https://img-blog.csdnimg.cn/direct/9319385748f642248024564972b56d64.png)