欢迎来到 跌倒的小黄瓜 的博客

♪(^∇^*)我要当大佬,(#^.^#)哈哈哈哈,(。-ω-)zzz我要成为优秀的人,(*^▽^*)٩(๑>◡<๑)۶O(∩_∩)O哈哈~~~~~~~~欢迎━(*`∀´*)ノ亻!

go-函数和错误处理

函数基本语法

func 函数名(形参列表)(返回值列表){
   执行语句
   return 返回值列表
}//返回值可以没有可以有多个可以有一个

引入

为了解决两个程序员取得函数名同名的情况

原理

本质就是创建不同的文件夹

概念

go的每一个文件都属于一个包,即go是以包来管理文件和项目目录结构的

作用

区分相同名字的函数,变量等标识符
程序文件很多时,很好的管理项目
控制变量,函数等访问范围,即作用域,大写公有,小写私有

说明

package:包名
import:包的路径

使用注意事项

给一个文件打包,该包对应一个文件夹,文件的包名通常和文件所在的文件夹名一致,一般为小写字母

要用包,先引包

import(
"包名"
”包名“
)
import “包名”
package指令在第一行,然后是import
在import包时,路径从$GOPATH的src下开始,不用带src,编译器会自己从src下引入
函数首字母大写才可以让其他包文件访问本包文件
访问:包名.函数名
如果包名较长,支持给包取别名,但是,取别名后原来的包名不能再用
在import里:别名 包名

同一包下,不能有相同函数名,否则报错

语法规范

编译生成一个可执行文件,需要将这个包名声明称main,即package main,但如果写的是一个库,包名可以自己定义
go build go_code/...../main
不需要带src编译器会自动带上
编译需编译main所在的文件夹
项目的目录结构最好按照规范来写

函数调用

说明

基本数据类型一般存放在栈区,编译器存在逃逸分析
引用类型存放在堆区,编译器存在逃逸分析
另外还有一个代码区
调用时分配新的空间,和其他栈区分开,每个空间独立不会混淆,函数执行完毕后销毁该空间

return语句

可接受多个,不想要可用占位符_忽略
若返回值有多个加(返回值类型列表),一个则不用_

res1, res2 := getsunAndsub(1,2)//该函数两个返回值
_, res2 := getsunAndsub(1,2)//占位符_忽略

递归

总结

执行函数,创建一个新的受保护的独立空间(新函数栈)
函数局部变量独立,不会相互影响
递归向退出递归条件的逼近,否则无限递归
函数执行完毕或遇到return就会返回,谁调用返回睡,自身销毁

小细节

数组是值传递,函数内修改,不会影响原来的值

希望函数可以改变函数外变量可以用&

函数内以指针操作

效果上看类似于引用

​ funct test(n1 *int){...}
​ test(&n1)

不同于c++的引用

​ int fun(int &a,int &b){...}
​ fun(a,b);

go中函数不支持重载,会报函数重复定义的错误

go中函数也是一种数据类型,可以赋值给一个变量,则该变量就是函数类型变量,可以通过改变量来对函数进行调用

func test(n int ,m,int){...}
a :=tset
res := a(10,20)//等价于res := test(10,20)

函数既然是一种数据类型,因此可以作为形参,并且调用

res2 :=myFun(getSun,n1,n2)
func myFun( funcvar fun(int, int) int,   num1 int,  num2 int) int{
   return funcvar(num1,num2)
}
### go

支持自定义数据类型,可化简定义

type 自定数据类型名  数据类型
type myIny int
type myFuncType  func(int,  int )int
这是可用myFunType代替形参 func(int,int)int

支持函数返回值命名

func getSunAndSub( n1 int, n2 int) (sum int ,sub int){
   sub = n1-n2
   sum = n1+n2
   return
}

_表示忽略

go支持可变参数

//支持0到多个参数
func sun(args...int) sun int{...}
//支持1到多个参数
func sum(n int, args...int) sun int{...}
//args是切片,通过args[index]访问到各个值

func sum(n1,n2 float)float{...}//n1 type=float

给函数类型的变量赋值函数:形参个数一样,返回值个数一样才算同种类型的即一个形参对应于一种函数类型

type myFuncType  func(int,  int )int
不能把func sum(n1,n2,n3 int)int{...}赋值给myFuncType

类型要匹配

init函数

介绍
每一个源文件都可以有一个init函数,会在main前执行,被go运行框架调用
通常在init中完成初始化工作
注意事项和细节
全局变量在init前执行
若import有utils.go则执行顺序
1.utils.go中的变量定义
2.utils.go中的init
3.main.go中的全局变量定义
4.main.go中的init
5.main.main函数

匿名函数(是一种类型)

没有名字,若只希望用一次,可以考虑使用匿名函数,当然也可调用多次

	res := func( n1 int, n2 int) int {
    return n1 +n2
}(10 ,20)
		//这种方式为定义时调用,只能调用一次
	res := func( n1 int, n2 int) int {
    return n1 +n2
}
res2 := res(10 ,20)
		//将函数付给一个变量,通过变量可多次调用匿名函数

全局匿名函数

将匿名函数付给一个全局变量,那吗可在程序中有效

		var(
   Fun1 = func (n1 int ,n2 int) int {
    return   n1*n2
  }
)

闭包

就是一个函数和其相关的引用环境组合的一个整体

func AddUpper() func (int) int {
   var n int = 10
   return func (x int) int {
  n = n+x
  return n
}
 }
		//AddUpper是一个函数,返回类型为func(int) int
		//n的值类似于一个静态变量
f := AddUpper()
fmt.println(f(1))//11
fmt.println(f(2))//13

上面匿名函数和函数外的形成了一个整体,构成闭包
函数和他引用到的变量构成了闭包

案例

判断文件后缀名是否为jpg,给文件加或不加
闭包完成
内函数引用到外函数的形参或定义的变量构成闭包
闭包可以保留上次引用的值,所以
优点:传入一次就可以反复使用

package main
import (
	"fmt"
	"strings"
)
func makeSuffix(suffix string) func (string) string {

	return func (name string) string {
		//如果 name 没有指定后缀,则加上,否则就返回原来的名字
		if !strings.HasSuffix(name, suffix)  {
			return name + suffix
		}

		return name
	}
}


func makeSuffix2(suffix string, name string)  string {


	//如果 name 没有指定后缀,则加上,否则就返回原来的名字
	if !strings.HasSuffix(name, suffix)  {
		return name + suffix
	}

	return name
	
}
func main(){
    //测试makeSuffix 的使用
	//返回一个闭包
	f2 := makeSuffix(".jpg") //如果使用闭包完成,好处是只需要传入一次后缀。
	fmt.Println("文件名处理后=", f2("winter")) // winter.jgp
	fmt.Println("文件名处理后=", f2("bird.jpg")) // bird.jpg

	fmt.Println("文件名处理后=", makeSuffix2("jpg", "winter")) // winter.jgp
	fmt.Println("文件名处理后=", makeSuffix2("jpg", "bird.jpg")) // bird.jpg

}

defer

why

函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时释放资源,go的设计组提供了defer(延时机制)
执行到defer时将defer后面的语句压入到独立的栈中(defer栈)
当函数执行完毕后,再从defer栈中按先入后出的方式出栈

注意事项

defer入栈时,会将相关值进行拷贝一起放入栈,注意,此值不收其他表达式影响

其价值在于函数执行完毕后,及时释放函数创建的资源

创建资源
defer 释放资源,其他代码不用再担心在什么时候关闭资源

函数传参机制

值类型值拷贝,引用类型地址拷贝

一般来说地址拷贝效率高,因为数据小,而值拷贝,数据越大,效率越低

变量作用域

函数内部的作用域仅限于函数内
函数外部的定义变量叫作全局变量在整个包内有效,若首字母大写在整个程序有效
在一个代码块中定义,则只在代码块中有效
编译器会采用就近原则
name := "tom"//错误,因为等价于var Name string Name = "tom"

字符串常用的系统函数

统计字符串长度
len(str)
字符串遍历,可带中文
r :=[]rune(str)
字符串转整数
n,err := strconv.Atoi("12")
整数转字符串
str = strconv.Itoa(12345)
字符串转[]byte
var bytes = []byte("hello go")
[]byte转字符串
str = string([]byte{97,98,99})
10进制转2 8 16进制
str = strconv.FormatInt(132,2)
在母串中查子串
strings.Contains("seafood","food")
母串中统计子串
strings.Counts("seafood","food")
不区分大小写的比较
stringsEqualFold("abc",""ABC)
返回子串在字符串第一次出现的index若没有返回-1
strings.Index("NLT_abc","abc")
指定子串替换为另一个子串
strings.Replace("go go hello","go","go语言",n)//n可指定几个-1表示全部替换
按照特定的某个字符为标识符将一个字符串拆分成字符数组
strings.Split("hello,word,ok",",")
字符串大小写转换
strings.TOLower("Go")
strings.ToUpper("Go")
将字符串两边的空格去掉
strings.TrimSpace(" asassdf ")
将字符串两边指定字符去掉
strings.Trim("! hello!","!")//["!"],将“!”去掉
strings.TrimLeft()去左边
strings.TrimRight()去右边
判断是否已指定的字符串开头
strings.HasPrefix("ftp://192.168.10.1","ftp")
strings.HasSuffix()判断是否已字符串结束

时间和日期相关的函数

需先引入time包
time.Time获取时间
now := time.now()
now.year()
分秒以此类推,返回类型为字符串型可转换
格式化时间
法一:用Print或Sprintf
法二: 用time.Format()完成
注意格式
单位
Nanosecond纳秒
Microsecond微妙
Millisecond毫秒
在程序中获取指定单位的时间 100*time.Millisecond
结合sleep使用:time.Sleep(100*time.Millisecond)

time的UnixNano和Unix

时间戳
获取时间的单位为妙
单位为纳秒
Now.Unix() 从1970年1月1日到现在的时间差
可用来统计一个函数执行的时间。利用时间戳

内置函数

len用来求长度
new用来分配内存,主要分配值类型,返回的是指针返回值是一个地址的数值,地址又是一个数值,都是系统分配的
make用来分配地址主要用于引用类型

错误处理

希望错误出现后可以捕获到错误并进行处理保证程序执行,还可以不货到后给管理员一个提示(邮件或短信),而不是崩溃,所以引出错误处理机制
go追求优雅,所以不支持try..catch...finally处理方式为defer,panic,recover
抛出一个panic错误然后在defer中通过recover捕获这个异常,然后正常处理

defer+recover处理

defer fucn() {
  err := recover()
  if err != nil {
   fmt.Println("err=",err)
  }
}

recover内置函数可以捕获到异常

错误处理好处

程序不会轻易挂掉,加入警戒代码,让代码更健壮

自定义错误

使用errows.New和panic内置函数
errors.New("错误说明"),会返回一个error类型的值,表示一个错误
panic内置函数接受一个interface{}类型的值(也就是任和值了),可以接受error类型变量,输出错误信息,并退出程序

package main
import (
	"fmt"
	_ "time"
	"errors"
)

func test() {
	//使用defer + recover 来捕获和处理异常
	defer func() {
		err := recover()  // recover()内置函数,可以捕获到异常
		if err != nil {  // 说明捕获到错误
			fmt.Println("err=", err)
			//这里就可以将错误信息发送给管理员....
			fmt.Println("发送邮件给admin@sohu.com~")
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}

//函数去读取以配置文件init.conf的信息
//如果文件名传入不正确,我们就返回一个自定义的错误
func readConf(name string) (err error) {
	if name == "config.ini" {
		//读取...
		return nil
	} else {
		//返回一个自定义错误
		return errors.New("读取文件错误..")
	}
}

func test02() {

	err := readConf("config2.ini")
	if err != nil {
		//如果读取文件发送错误,就输出这个错误,并终止程序
		panic(err)
	}
	fmt.Println("test02()继续执行....")
}
	

func main() {

	//测试
	// test()
	// for {
	// 	fmt.Println("main()下面的代码...")
	// 	time.Sleep(time.Second)
	// }

	//测试自定义错误的使用

	test02()
	fmt.Println("main()下面的代码...")
}
posted @ 2019-11-01 22:30  跌倒的小黄瓜  阅读(460)  评论(0编辑  收藏  举报