函数
格式:func 函数名(变量名 变量类型) 返回值 {...}
func test1(a int, b int) int {
return a+b
}
golang可以让函数有两个返回值,return后面用逗号隔开
函数返回值可以自己命名,这样就直接return就行
func test2(a int,b int)(c int,d int) {
c=a
d=b
return
}
func test3(a int, b int)(int, string){
return a+b,"successful"
}
函数也是一种数据类型,可以被赋值给变量。
函数可以作为形参传递给另一个函数,也可以作为一个函数的返回值(此时称为函数闭包)
func t1(a int)int{
return a
}
func t2(b func(int))int{
return b
}
type f1 func(int)
func t3(c f1)int{
return c
}
func main(){
a := test
t2(a)
t3(a)
}
匿名函数
定义的时候调用
func main(){
res := func(a int)int{
return a+10
}(10)
fmt.Println(res)
}
结合前文内容,匿名函数可以作为数据类型赋值给一个变量,然后调用
闭包
闭包(Closure)在某些编程语言中也被称为 Lambda 表达式。
闭包的函数内的变量不会被垃圾回收,可以重复使用。
闭包就是一个函数和与其相关的引用环境组合的一个整体。即引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量。被捕获到闭包中的变量让闭包本身拥有了记忆效应,变量会跟随闭包生命期一直存在。
func AddUpper() func(int)int{
var n int = 0
return func(x int) int {
n = n + x
return n
}
}
func main() {
fmt.Println("闭包返回:",f(1)) //>>1
fmt.Println("闭包返回:",f(2)) //>>3
fmt.Println("闭包返回:",f(3)) //>>6
}
init函数
- init函数是初始化函数,会在main函数被调用之前被自动调用
- 每个源文件都可以包含一个init函数
- 执行顺序:全局变量的定义->init函数调用->main函数调用
- 包导进来的时候,该包下的init函数就会被调用
new函数
- new函数分配内存
- 返回对应类型的指针,因此获取其中的值时要加
*
指针
相比于c和c++,golang的指针操作非常有限,只能对传入参数进行修改
func add1ptr(n *int){
*n += 1
}
func main(){
n := 5
fmt.Printf(add1prt( &n )) // >>11
}
传入时记得取址,操作时记得*号
结构体
格式:type 结构体名 struct{...}
初始化时:变量名 := 结构体名:{ 结构体元素名 : 值 ,结构体元素名 : 值 }
访问时用 “.” 访问,使用结构体的指针时也是 "." 不需要 "->",在函数内修改结构体内容时需要用指针传入
type test struct{
a string
b string
}
func main(){
temp := test{a:"a",b:"b"}
temp.a = "c"
}
根据开闭原则,应该对结构体进行封装,将元素设置为私有(首字母小写),然后使用Get、Set函数访问。
结构体方法
类似于类成员函数
创建:func (结构体) 函数名 (传入参数) 返回值 {...}
调用:结构体变量.方法名(参数名)
func(temp test)testtest(input string) bool {
return test.a == input
}
func main(){
fmt.Printf(temp.testtest("a"))
}
结构体类型是值类型,方法调用时是值拷贝的方式。因此,直接在方法里对结构体内的变量赋值,不会改变结构体变量的值
若要改变结构体的变量的值,要使用结构体指针
func(u *User)test(){ //结构体指针
(*u).Name = "user" //*可写可不写,go内部帮我们优化了
}
func main(){
var u User
(&u).test() //&也是可写可不写
}
String()方法
和java的toString()作用一致,当打印该结构体时自动调用String()函数
func (u *User)String() string{
str := fmt.Sprintf("Name=%v , Age=%v",u.Name,u.Age)
return str
}
继承
golang不支持extend关键字,而是使用组合的方式实现继承
// 模拟动物行为的接口
type IAnimal interface {
Eat() // 描述吃的行为
}
// 动物 所有动物的父类
type Animal struct {
Name string
}
// 动物去实现IAnimal中描述的吃的接口
func (a *Animal) Eat() {
fmt.Printf("%v is eating\n", a.Name)
}
// 动物的构造函数
func newAnimal(name string) *Animal {
return &Animal{
Name: name,
}
}
// 猫的结构体 组合了animal
type Cat struct {
*Animal
}
// 实现猫的构造函数 初始化animal结构体
func newCat(name string) *Cat {
return &Cat{
Animal: newAnimal(name),
}
}
cat := newCat("cat")
cat.Eat() // cat is eating
重写
// 猫结构体IAnimal的Eat方法
func (cat *Cat) Eat() {
fmt.Printf("children %v is eating\n", cat.Name)
}
cat.Eat()
// children cat is eating
多态
传入的是接口,可以处理所有组合了Animal的单位类型
func check(animal IAnimal) {
animal.Eat()
}
断言
方法1 通过是否能赋值判断是否是这个类型
user1,flag := person1.(User) //判断person1是否是User类型
//是的话赋值给user1变量
//flag判断是否成功
可以放在if语句中使用
if user1,flag := person1.(User);flag{
fmt.Println("是User")
}
方法2 通过type关键字判断类型
switch person1.(type){
case User:
break
}
异常处理
golang用error类型来储存错误
在使用函数时,返回值中加入error的变量来返回错误信息
func test(a int, b int) (c int, err error) {
if(b != 0){
return a/b, nil //没有错误时返回nil
}
return a, error.New("分母不能为0")
}
如果出现了错误想退出,可以将err传入panic()函数,这样之后的语句就不会执行
panic(err)
字符串操作
-
对字符串本身的操作 (import "strings")
s := "aaabbbccc" len(s) //长度 >>9 strings.Contains(s, "bbb") //s中是否存在”bbb” strings.Index(s, "bb") //bb的下标 >>3 strings.Join([]string{"aaa","ccc"},"bbb") //连接多个字符串 >> aaabbbccc strings.Repeat("aa", 3) //重复多个字符串 >>aaaaaa strings.Replace(a, "bbb", "ddd", -1) // 替换字符串 >>aaadddccc strings.Split(a, "bbb") //分割字符串(与java相同) -
字符串转其他类型 (import "strconv")
f, _ := strconv.ParseInt("111", 10, 64) //转十进制64位 -
Atoi 和 itoA
Atoi 是字符串转数字,itoA是数字转字符串(自动)
i1, err := strconv.Atoi("123") //il = 123当输入不合法时err储存错误信息
JSON
当结构体的每个变量名都是大写的时候,可以将其转为JSON类型(将json输出时要强转为string型)
a := data{Id:"001",Name:"aaa"}
js := json.Marshal(a)
fmt.Println(string(js)) // >>{"Id":"001","Name":"aaa"}
时间处理
now := time.Now() //获取当前时间
now.Unix() //获取时间戳
构造带时区的时间
date1 := time.Date(2023,1,15,21,30,50,0,time.UTC)
fmt.Println(date.Year(),date.Month(),date.Minute())
进程
os.Args() //获取命令行参数
os.Getenv() //获取环境变量
os.Setenv() //写入环境变量
buf,err := exec.Command("gred","127.0.0.1","/etc/hosts").CombinedOutput() //快速启动子进程
defer
defer:延迟执行机制,用于在函数执行完毕后及时释放资源
带defer关键字的语句不会被立即执行,而是压入栈中,待函数执行完毕后(return语句之后),再将defer栈中的语句取出执行
func add(a,b int)int{
defer fmt.Println(a)
defer fmt.Println(b)
a+=100 //压入栈中时a是1,所以打印出来的a还是1
sum := a+b
fmt.Println(sum)
return sum
}
func main(){
sum := add(1,2)
fmt.Println(sum)
}
结果:
>>103
>>2
>>1
>>103
defer+recover处理错误
recover是一个内置函数,可以捕获错误
func test(){
defer func(){
err := recover()
if err!=nil{
fmt.Println("发生错误")
}
}() //这个括号是调用该匿名函数,参阅上文
//正常代码
}