05 函数、包和错误处理

函数

完成某一功能程序指令集合
定义语法

func 函数名(形参列表)(返回值列表){
	 执行语句
	 return 返回值列表
}

示例代码

func main () {
	fmt.Println("结果==",cal(1.1,3.0,'+'))//4.1
}
func cal ( n1 float64, n2 float64,opreator byte)( float64){
	var res float64
	switch opreator {
		case '+':
			res = n1 + n2
		case '-':
			res = n1 - n2
		case '*':
			res = n1 * n2
		case '/':
			res = n1 / n2
		default:
			fmt.Println("操作符不正确")

	}
	return res

}

多值返回代码示例

func main () {
	//fmt.Println("结果==",util.Cal(1.1,3.0,'+'))//4.1
	res1,res2 := getSumAndSub(1,2)
	res3,_ := getSumAndSub(1,2)
	fmt.Println(res1,res2,res3)
}
func getSumAndSub(n1 int ,n2 int)(int,int){
	sum := n1 + n2
	sub := n1 - n2
	return sum,sub
}
//声明返回值名称
func getSumAndSub2(n1 int ,n2 int)(sum int,sub int){
	sum = n1 + n2
	sub = n1 - n2
	return 
}
//可变参数
func getSum(n1 int,args... int) int {
	var res int = n1
	for i :=0 ; i<len(args); i++{
		res += args[i]
	}
	return res
	
}

注意点

  1. 函数传递中,基本数据类型和数组默认都是值传递,即进行值拷贝,在函数内修改不影响原来的值,如果希望函数中值的更改影响函数外的值,可以传入地址 &num,函数内通过指针的方式操作变量
  2. Go不支持函数重载
  3. Go中函数也是一种类型可以赋值给变量,通过变量可以调用函数
  4. 函数可以作为形参传入函数
  5. 支持 对函数的返回值命名,命名后 return 后不用 追加 值返回。自动返回声明的返回值变量
  6. Go函数支持可变参数

用来区分同名的函数变量,更好的管理代码文件,控制 函数变量等的作用域
go中每个文件夹就是一个包

注意点

  1. 给一个文件打包时,该包对应一个文件夹,比如 示例中的package utils 和 package main,通常和文件夹一致一般为小写字母
  2. 当一个文件要使用其他包的函数或变量时,需要先引入对应的路径,如示例中 import go01/utils.引入包时,Go编译器自动在从GOPATH下的src开始查找路径并引入内容
  3. 包中的方法 变量 如果要被其他包引用到,必须首字母大写
  4. 调用其他包内容时 采用 包名.函数名的方式
  5. 如果包名很长 可以 import 时 起别名 如:import util “go01/utils” ,取包名后原来的包名就不能用了
  6. 同一包下,不能有相同的函数名或变量名,否则会提示重复定义
  7. 如果要编译成一个可执行程序文件,就需要把包声明为main,这个是语法规范,如果是编译成一个库,包名可以自定义

代码示例
go01/main下 main.go

package main
import (
	"fmt"
	util "go01/utils"
)
func main () {
	fmt.Println("结果==",util.Cal(1.1,3.0,'+'))//4.1
}

go01/utils下 util.go

package utils

import (
	"fmt"
)

func Cal ( n1 float64, n2 float64,opreator byte)( float64){
	var res float64
	switch opreator {
		case '+':
			res = n1 + n2
		case '-':
			res = n1 - n2
		case '*':
			res = n1 * n2
		case '/':
			res = n1 / n2
		default:
			fmt.Println("操作符不正确")

	}
	return res

}

init函数

每个文件都可以包含一个init函数,用于执行前的初始化工作,在函数在main函数执行前,会被Go运行框架调用。
注意点

  1. 如果一个文件同时包含全局变量定义,init函数和main函数 则执行顺序为:全局变量定义->init函数->main函数
  2. 如果main.go 调用了 util.go 二者都包含 init 方法和全局变量定义,那么执行顺序为:uitl变量定义->util init ->main 变量定义->main init 方法-> main main 方法

示例

func init(){
	fmt.Println(" main init被执行...")
}

匿名函数

匿名函数就是没有名字的函数,如果一个函数只使用一次,可以考虑使用匿名函数,匿名函数也可以被多次调用
示例程序

func test1(){
	//定义好函数后直接调用
	res1 := func(n1 int,n2 int) int {
		return n1 + n2
	}(1,2)
	fmt.Println(res1)
	//将匿名函数赋值给 变量 然后通过变量调用,
	//如果将函数赋值给 全局变量那么 函数可以全局调用
	a := func(n1 int,n2 int) int {
		return n1 + n2
	}
	fmt.Println(a(1,2))
}

闭包

闭包就是一个函数和它相关的引用环境组合成的一个整体。
测试代码

func test2(){
	f1 := AddUpper(5);
	fmt.Println(f1(1))
	fmt.Println(f1(2))
}

// 定义一个函数 AddUpper 传入一个初始值 ,返回一个函数 
// 函数调用时在 初始值基础上增加固定值


func AddUpper(n1 int) func(int) int{
	var n int = n1
	return func (x int) int{
		n = n + x
		return n
	}
}

函数的defer

函数中程序员常需要创建资源(数据库连接、锁等),函数执行完毕后必须释放相应的资源,Go提供了defer 延迟机制来实现。更加方便
注意点

  1. 当go执行到refer时,不会立即执行defer后的语句,而是将defer后的语句压如到一个栈中,然后执行下一个语句
  2. 函数执行完毕后再从defer栈中,依次取出语句执行。
  3. defer将语句放入栈中时,也会将相关的值复制到栈中。方法中值类型变量的改变不会影响栈中的值

示例代码

func test3(n1 int,n2 int ) int {
	defer fmt.Println("ok1 n1=",n1)//10
	defer fmt.Println("ok2 n2=",n2)//20
	n1++
	n2++
	res := n1 + n2
	fmt.Println("res =",res)//32
	return res
}

变量作用域

  1. 函数内部或语句块中定义的变量 作用域是本语句块
  2. 函数外声明的变量时全局变量,作用域是真个包,如果首字母大写则作用域是整个程序

常用函数

字符串常用函数

func test02(){

	var str = "hahaz中文1243";
	fmt.Println("字符串长度",len(str))
	//字符串遍历 同时处理有中文的问题
	r := []rune(str)
	for i :=0;i<len(r);i++{
		fmt.Printf("字符=%c",r[i]) 
		fmt.Println("") 
	}
	//字符串 转整型
	n,err := strconv.Atoi("12")
	fmt.Println("转整型:",n,err) 
	//整数转字符串
	n2 := strconv.Itoa(12)
	fmt.Println("转字符串:",n2) 
	//字符串 转 byte数组
	var tytes = []byte(str)
	fmt.Println("转字[]byte:",tytes) 
	//  byte数组 转 字符串
	var str2 = string(tytes)
	fmt.Println("[]byte转字符串:",str2) 
	//10进制转 2、8、16进制
	str2 = strconv.FormatInt(16,2)
	fmt.Println("2进制:",str2) 
	str2 = strconv.FormatInt(16,8)
	fmt.Println("8进制:",str2) 
	str2 = strconv.FormatInt(16,16)
	fmt.Println("16进制:",str2) 

	//字符串包含
	fmt.Println("是否包含:",strings.Contains("seafood","foo")) 
	fmt.Println("包含子字符串数量:",strings.Count("seafood","foo")) 
	fmt.Println("是否相同:",strings.EqualFold("Foo","foo")) 
	fmt.Println("子字符串第一次出现位置:",strings.Index("seafood","foo")) 
	fmt.Println("转大写:",strings.ToLower("sSEafood")) 
	fmt.Println("转小写:",strings.ToUpper("seafood")) 
	fmt.Println("去除两端空格:",strings.TrimSpace("  seafood  ")) 
	fmt.Println("去除两边空格和指定字符:",strings.Trim("sssesafoodsss","s")) 
	fmt.Println("去除左边空格和指定字符:",strings.TrimLeft("sssseafoodsss","s")) 
	fmt.Println("去除右边空格和指定字符:",strings.TrimRight("sssseafoodsss","s")) 
	fmt.Println("子字符串最后出现的位置:",strings.LastIndex("se123afood123","123")) 
	fmt.Println("替换字符串:",strings.Replace("se123afood123","123","1~3",-1))
	fmt.Println("判断字符串开头:",strings.HasPrefix("http://123.com","http"))
	fmt.Println("判断字符串结尾",strings.HasSuffix("http://123.com","com"))
	fmt.Println("字符串拆分========")
	strArr := strings.Split("zhagnsna,lisi",",")
	for i:=0;i<len(strArr);i++{
		fmt.Println(strArr[i])
	}

}

时间和日期函数

func test03(){
   now := time.Now()
   fmt.Printf("当前时间 :%v,type: %T",now,now)
   fmt.Println()
   fmt.Println("当前时间信息:",now.Year(),now.Month(),now.Day(),
   now.Hour(),now.Minute(),now.Second())

	//Printf格式化时间 
	fmt.Printf("当前时间 :%d年%d月%d日%d时%d分%d秒",
	now.Year(),now.Month(),now.Day(),now.Hour(),
	now.Minute(),now.Second())
	fmt.Println()
	//time.Format 格式化日期 格式中的数字不能变 2006-01-02 15:04:05
	fmt.Println("当前时间:"+now.Format("2006-01-02 15:04:05"))

	//每100毫秒打印一个数字 
	for i:=0;i<10;i++{
		fmt.Println(i)
		time.Sleep(time.Millisecond * 100)
	}
	
	//h获取 从 1970年1月1日到指定时间 经过的时间  单位分别为秒和纳秒
	fmt.Printf("unix时间戳:%v unixnano时间戳:%v \n",now.Unix(),now.UnixNano())
	fmt.Println()

	fmt.Println()
}

内置函数
Golang 为了方便编程提供了一些函数,可以直接使用,我们称之为内置函数,

  1. len:用来求长度 比如:string,array,slice,map,chnnel
  2. new:用来分配内存,主要用来分配值类型,如:int float32 ,struc 返回的是指针
  3. make:用来分配内存,主要用来为引用类型分配,比如 channel map slice
func test04(){
	fmt.Println(len("123"))

	num := new(int) // *int 类型
	*num = 100
	fmt.Printf("num 类型 %T,num 的值 %v,num 地址 %v  %v \n",
	num,*num,&num,*num)

}

错误处理

默认情况下发生错误后,程序会退出,我们可以在发生错误后进行捕获和处理,设置给管理员一个提示(邮件\短信)

  1. Golang追求优雅简介,所以不支持 传统的try catch finally 语句
  2. 通过defer panic recover ,程序跑出一个 panic异常,在defer中通过recover捕获这个异常并进行处理
  3. Golang支持自定义错误,
func test05(){
	defer func(){
		err := recover()
		if err != nil{
			fmt.Println("err=",err)
		}
	}()
	num1 :=10
	num2 :=0
	res :=num1/num2
	fmt.Println("res=",res)
}

//抛出自定义异常
func test06(){
	defer func(){//处理异常
		err := recover()
		if err != nil{ 
			fmt.Println("test 06 err=",err)
		}
	}()
	err :=errTest("hei")
	if err != nil {
		panic(err)//抛出异常
	}
	fmt.Println("抛出异常后还能执行吗")

}
func errTest( name string)(err error){
	defer func(){
		err := recover()
		if err != nil{
			fmt.Println("error Test err=",err)
		}
	}()
	if name == "zhangsan"{
		return nil
	}else{
		return errors.New("参数不合法")
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catch that elf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值