GO语言学习之函数,包和错误处理(2)

GO语言学习之函数,异常

1.init函数

1》基本介绍

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用,也就是说Init会在main函数执行前被调用

2》.代码示例

package main

func init() {
	println("我爱这世界,因为我爱你。。。")
}
func main() {
	println("hello world")
}

3》init函数的注意事项和细节

<1如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程全局变量定义–>init函数–>main函数

package main
var a=test()
func test() int{
	println("走向远方,走向那充满想象的未知。。。")
	return 100
}
func init() {
	println("我爱这世界,因为我爱你。。。")
}
func main() {

	println("温暖,安详,笼罩着大地。。。")
}
/*
走向远方,走向那充满想象的未知。。。
我爱这世界,因为我爱你。。。
温暖,安详,笼罩着大地。。。
 */

<2.init最主要的作用,就是完成一些初始化工作,

package util

import (
	"fmt"
)
var Name string
var pwd string


func init() {
	fmt.Println("init()")
	Name ="durex"
	pwd="123"
}
package main

import (
	"byteDemo02/util"
	"fmt"
)
var name=test()

func test() string{
	fmt.Println("test哈哈。。。")
	return "隔壁老王"
}
func init()  {
	fmt.Println("爱是世界上最美的东西。。。")
}

func main()  {

	fmt.Println("name",name)
	fmt.Println("name",util.Name)
	/*运行结果:
	init()
	test哈哈。。。
	爱是世界上最美的东西。。。
	name 隔壁老王
	name durex
	 */
}

执行流程总结:先执行被导入包的初始化,再执行导入包的初始化,再按照代码顺序执行调用

2.匿名函数

GO支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用

1》匿名函数调用方式1:

package main

import "fmt"

func main() {
	//没有名字,直接调用,这种匿名函数只能调用一次
	res:=func(a int,b int)int{
		return a+b
	}(10,30)

	fmt.Println("res=",res)
}

2》匿名函数调用方式2:

package main

import "fmt"

func main() {
	res:=func(a int,b int)int{
		return a-b
	}
	a:=res(100,76)

	fmt.Println("a=",a)
	a=res(200,100)
	fmt.Println("a=",a)

}

3》全局匿名函数

如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序中有效

package main

import "fmt"

var(
	Fun1= func(a int,b int) int {
		return a*b
	}
)
func main() {

	res:=Fun1(8,9)
	fmt.Println("res=",res)

}

3.闭包

1》闭包就是一个函数和与他相关的引用环境组合的一个整体(实体)

代码演示:

package main

import "fmt"

func addPackage() func(int)int  {

	n:=10
	return func(i int) int {

		n=n+i
		return n
	}
}
func main() {
	res:=addPackage()

	fmt.Println(res(1))
	fmt.Println(res(2))
	fmt.Println(res(3))
}

》addPackage 是一个函数,其返回值类型是 func(int)int

》闭包的说明:

	n:=10
	return func(i int) int {

		n=n+i
		return n
	}

该段代码返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成以整体,构成闭包

》大家可以这样理解:闭包是类,函数时操作,n是字段,函数和它使用到n构成闭包

》当我们反复的调用f函数是,因为n是只初始化一次,因此每调用一次就进行累计

》我们要搞清楚闭包的关键,就是要分析出返回的函数,它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包

package main

import "fmt"

func addPackage() func(int)int  {

	n:=10
	fmt.Println("哈哈哈哈哈哈")
	str:="hello"
	return func(i int) int {
		str+=string(36)
		fmt.Println(str)
		n=n+i
		return n
	}
}
func main() {
	res:=addPackage()

	fmt.Println(res(1))
	fmt.Println(res(2))
	fmt.Println(res(3))
}
//结果:
哈哈哈哈哈哈
hello$
11
hello$$
13
hello$$$
16
可以知道 n:=10只执行一次

2》闭包的最佳实践

编写一个程序,具体要求如下:

<编写一个函数 makeSuffix(suffix string),可以接收一个文件后缀名(比如.jpg),并返回一个闭包

<调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如:jpg,如果已经有.jpg后缀,则返回原文件名

<要求使用闭包的方式完成

<string.HasSuffix,该函数可以判断某个字符串是否有指定的后缀

package main

import (
	"fmt"
	"strings"
)

func makeSuffix(suffix string)func(string)string{
	return func(name string) string {
		//如果该文件名没有指定的后缀(
		if !strings.HasSuffix(name,suffix){
			return name+suffix
		}
		return name
	}
}
func main(){
	res:=makeSuffix(".jpg")
	fmt.Println("文件名处理后:",res("hello"))
	fmt.Println("文件名处理后:",res("哈哈哈哈"))
}

代码解读:

返回的匿名函数和makeSuffix(suffix String)的suffix变量,组合成一个闭包,因为返回的函数引用到suffix这个变量

我们能通过这个案例体会到闭包的好处,如果用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入后缀名,比如:.jpg,而闭包因为可以保留上次引用的某个值,所以我么传入一次就可以反复使用。

4函数·的defer

1.为什么需要defer

在函数中,我们经常需要创建资源(比如:数据库连接,文件句柄,锁)

为了在函数执行完毕后,及时释放资源,GO设计者提供defer(延时机制)

2.代码演示:

package main

import "fmt"

func sum(n1 int,n2 int)int {
	//当执行到defer语句时,暂时不执行,会将defer后面的语句压入到独立的栈内存中
	//当函数执行完毕以后,再从defer栈,按照先入后出的方式,进行执行
	defer fmt.Println("i love you <1>",n1)
	defer fmt.Println("i love you <2>",n2)

	n3:=n1+n2
	fmt.Println("i love you <3>",n3)

	return n3
}
func main() {

	res:=sum(88,99)
	fmt.Println("res=",res)

}

结果:

i love you <3> 187
i love you <2> 99
i love you <1> 88
res= 187

3.defer的最佳实践

defer的价值是:当函数执行完毕后,可以及时的释放函数创建的资源,看下模拟代码

func test01()  {
	//关闭文件资源
	file=openfile("文件名")
	defer file.close()
}
func test02()  {
	//释放数据库连接
	connect=openDatabase("文件名")
	defer connect.close()
}

1》在开发过程中,创建资源后,比如:(打开文件,建立数据库连接,或者锁资源后)可以执行defer file.Close()

defer connect.Close()

2》在defer后,可以继续使用创建资源

3》当函数完毕后,系统会依次从defer栈中,取出数据,关闭资源

4》这种方式简洁方便,不用再为什么时间结束资源而操心

5.函数参数传递方式

1.两种方式

值类型参数默认就是值传递

值类型:(基本数据类型,int系列,float系列,Bool,string,struct,数组)

引用类型参数默认就是引用传递

引用类型:(指针,slice切片,map,管道chain,interface等都是引用类型)

其实:不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的·是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低

2.两种传递方式的使用特点;

1》值类型默认是值传递,变量直接存储值,内存通常在栈中分配

2》引用类型默认是引用传递:变量存储的是一个地址,这个地址对应的空间才真正能存储数据的值,引用类型的内存通常在堆上分配,当没有任何变量使用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收

3》如果希望函数内的变量能修改函数外的变量,可以传入变量的地址值,&变量,函数内以指针的方式操作变量。从效果是看类似引用。

6.变量的作用域

1》函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部

package main

import "fmt"

func test(){
	a :=100
	n :="hello liuyifei"
	fmt.Println("a:",a," n:",n)
}
func main() {
	//a 和n 只能在 test()中使用,这叫局域变量
}

2》函数外部声明/定义的变量叫全局变量,作用域在整个包中都有效,如果首字母为大写,则作用域在整个程序中有效

package main

import "fmt"
//全局变量,整个文件中都能使用
var a=111
var n="佳人"
func main() {

	fmt.Println("a:",a," n:",n)
}

3》如果变量是在一个代码块,比如;for/if中,那么这个变量的作用域就在该代码块

package main

import "fmt"

func main() {

	for i:=0;i<5;i++{
		fmt.Println("i love the world")
	}
	var i =0
	fmt.Println(i)
	for i=8;i>5;i--{
		fmt.Println("Because i love you")
	}
}

4》小练习

package main

import "fmt"

var name="神仙姐姐"

func func01(){
	fmt.Println("name:"+name)
}
func func02(){
	name="嫦娥"
	fmt.Println("name:"+name)
}
func main() {

	func01()
	func02()
	func01()

}
//猜一下输出结果

7.函数小练习

1》将之前打印金字塔的案例,封装成函数

2》编写一个函数,从终端输入(1-9),打印出相应乘法表

8.字符串常用的系统函数

字符串在我们开发过程使用非常多,常用函数需要我们掌握

1》len(str)

统计字符串长度,按字节

	str :="hello"
	println("str的长度为:",len(str))
2》[]rune(str)

字符串遍历,同时处理有中文的问题 r:=[]rune(str)

	str2:="hello 神仙姐姐"
	s:=[]rune(str2)
	for i:=0;i<len(s);i++{

		fmt.Printf("字符为:%c \n",s[i])
	}
3》n,err=strconv.Atoi(“88”)

字符串转整数:

	x,err:=strconv.Atoi("12")
	fmt.Println(x,err)//   12<nil> err=nil代表转换成功
	y,err2:=strconv.Atoi("hello")

	if err2!=nil{
		fmt.Println("转换错误",err2)//转换错误 strconv.Atoi: parsing "hello": invalid syntax
	}else {
		fmt.Println("转换结果:",y)
	}
4》str=strconv.Itoa(123)

整数转成字符串

	st01:=strconv.Itoa(123)
	fmt.Printf("st01的类型为%T,值为 %v\n",st01,st01)
	//st01的类型为string,值为 123
5》[]byte(“str”)

字符串转[]byte

var bytes=[]byte(“hello world”)

var bytes=[]byte("hello world")
fmt.Println("bytes=",bytes)
6》[]byte 转字符串:

str=string([]byte(98,99,100))

str=string([]byte{98,99,100})
fmt.Printf("str=%v\n",str )
7》10进制转 2,8,16进制

str=strconv.FormatInt(123,2)

	str=strconv.FormatInt(123,2)
	fmt.Printf("str=%v\n",str )
8》查找子串是否在指定的字符串中:

string.Contains(“hello,liuyifei”,“liu”)//true

	bo:=strings.Contains("hello,liuyifei","liu")
	fmt.Println("bo=",bo)
9》统计字符串中有几个指定的子串:

strings.Count(“chinaese is janpanese‘s dad”,“ese”)

a:=strings.Count("chinaese is janpanese‘s dad","ese")
fmt.Println("a=",a)
10》不区分大小写比较字符串

(==是区分大小写的相等)

fmt.Println(strings.EqualFold("ABC","Abc"))//true
fmt.Println("ABC"=="Abc")//false
11》返回子串在字符串最后一次出现的index,

如果没有返回-1;

strings.LastIndex(“longlong ago,go is not in the world”,“go”)

inde:=strings.LastIndex("longlong ago,go is not in the world","go")
fmt.Println("inde=",inde)
12》返回子串在字符串第一次出现的index,
inde:=strings.Index("longlong ago,go is not in the world","go")
fmt.Println("inde=",inde)
13》将指定的字串替换成另外一个字串,

strings.Replace(“go hello”,“hello go”,“gogogo”,n)

n表示你准备替换几个,如果n=-1则代表全部替换

	// non-overlapping: "123" repeat 6 times in s
	sx := "123lafaldsjglad123lkfasdf123djfal123lkdjga123lksjfla123l"
	old := "123"
	new := "888"

	fmt.Println("non-overlapping: ")
	// n < 0 ,用 new 替换所有匹配上的 old;n=-1:  888lafaldsjglad888lkfasdf888djfal888lkdjga888lksjfla888l
	fmt.Println("n=-1: ", strings.Replace(sx, old, new, -1 ))

	// 不替换任何匹配的 old;n=0:  123lafaldsjglad123lkfasdf123djfal123lkdjga123lksjfla123l
	fmt.Println("n=0: ", strings.Replace(sx, old, new, 0 ))

	// 用 new 替换第一个匹配的 old;n=1:  888lafaldsjglad123lkfasdf123djfal123lkdjga123lksjfla123l
	fmt.Println("n=1: ", strings.Replace(sx, old, new, 1 ))
	// 用 new 替换前 7 个匹配上的 old(实际没那么多);n=7:  888lafaldsjglad888lkfasdf888djfal888lkdjga888lksjfla888l
	fmt.Println("n=7: ", strings.Replace(sx, old, new, 7 ))

14》按照指定的某个字符,为分隔标识,将一个字符串拆分成字符串数组
	strx4:="hello,liuyifei,do you know ,i love you"
	strArr:=strings.Split(strx4,",")
	fmt.Println("strArr",strArr)
15》将字符串的字母进行大小写转换:

strings.ToLower(“LIUYIFEI”)

strings.ToUpper(“liuyifei”)

	x01:=strings.ToLower("LIUYIFEI")
	fmt.Println("x01=",x01)
	x02:=strings.ToUpper("liuyifei")
	fmt.Println("x02=",x02)
16》将字符串左右两边空格去掉

strings.TrimSpace(“long long ago”)

	x03:=strings.TrimSpace(" long long ago ")
	fmt.Println("x03=",x03)
17》去掉字符串左右两边的字符

string.TrimLeft("!hello!!","!")

string.TrimRight("!hello!!","!")

	x01=strings.TrimLeft("!hello!!","!")
	fmt.Println("x01=",x01)
	x02=strings.TrimRight("!hello!!","!")
	fmt.Println("x02=",x02)
18》判断字符串是否以指定的字符串开头:

strings.HasPrefix(“http://www.baidu.com”,“http”)

boo:=strings.HasPrefix("http://www.baidu.com","http")
fmt.Println(boo)
19》判断字符串是否以指定的字符串结束
	boo=strings.HasSuffix("http://www.baidu.com","http")
	fmt.Println(boo)

9.时间日期相关函数

1》时间和日期函数,需要导入time包

type Time

time包里提供了时间的显示和测量用的函数。

2》time.Time类型,用于表示时间

package main

import (
	"fmt"
	"time"
)

func main() {

	now:=time.Now()
	fmt.Printf("now类型:%T,值:%v",now,now)
}

3》如何获取到其他的日期信息

	now:=time.Now()
	fmt.Printf("now类型:%T,值:%v",now,now)
	fmt.Printf("year=%v",now.Year())
	fmt.Printf("month=%v",now.Month())
	fmt.Printf("day=%v",now.Day())
	fmt.Printf("Hour=%v",now.Hour())
	fmt.Printf("Minutes=%v",now.Minute())
	fmt.Printf(" Second=%v",now.Second())

4》格式化日期时间

方式1:使用Printf或者SPrintf

fmt.Printf("%d-%d%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second())

方式2:使用time.Format()


5》时间的常量

const{
	//每种秒之间的倍率是 1000
	Nanosecond //纳秒
	Microsecond //微秒
	Millisecond //毫秒
	second    //秒
	Minute		//分
	Hour 	//	时
}

常量的作用在程序中可用于获取指定时间单位的时间,比如想得到100毫秒

100*time.Millisecond

6》结合Sleep来使用一下时间常量

package main

import (
	"fmt"
	"time"
)

func main() {

	i:=0
	for{
		i++
		fmt.Println(i)
		//休眠
		time.Sleep(time.Millisecond*100)

		if i>100{
			break
		}
	}
}

7》time的Unix和UnixNano方法

​ Unix将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒)。

  • func (t Time) Unix() int64

    UnixNano将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位纳秒)。如果纳秒为单位的unix时间超出了int64能表示的范围,结果是未定义的。注意这就意味着Time零值调用UnixNano方法的话,结果是未定义的。

  • func (t Time) UnixNano() int64

now:=time.Now()
fmt.Printf("unix时间戳:%v\n,UNIXNANO时间戳为%v\n:",now.Unix(),now.UnixNano())

8:编写代码测试一个应用执行的时间

package main

import (
	"fmt"
	"time"
)
func funx(){
	i:=0
	for{
		i++
		fmt.Println(i)
		//休眠
		time.Sleep(time.Millisecond*100)

		if i>100{
			break
		}
	}

}
func main() {
	now:=time.Now()
	start:=now.Unix()
	funx()
	end:=now.Unix()
	fmt.Printf("funx()函数执行时间为:%v 秒",end-start)
}

10.内置函数

Go设计者为了编程方便,提供了一些函数,这些函数可以直接使用

我们称为GO内置函数,

1>len:用来求长度:string,array,slice,map,channel

2>new:用来分配内存,主要用来分配值类型,比如:int,float32,struct…返回的是指针

举例说明new的使用

	num1:=100
	fmt.Printf("num1类型 %T,地址值为:%v,值为%v\n",num1,&num1,num1)

	num2:=new(int)

	*num2=100
	fmt.Printf("num2类型 %T,地址值为:%v,值为%v\n",num2,&num2,num2)

3>make:用来分配内存,主要用来分配引用类型,比如:channel,slice,map,这个后面讲解

11.错误处理

1.代码:

package main

import "fmt"

func test()  {
	n1:=3
	n2:=0
	res:=n1/n2
	fmt.Println(res)
}
func main() {

	test()
	fmt.Println("发生了啥。。。")
}

1》在默认情况下,当发生错误(panic)后,程序就会退出(崩溃)

2》如果我们希望,当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行,还可以捕获到错误后,给管理员一个提示(邮件,短信。。)

3》这里我们引出错误处理机制

2.基本说明

1》go语言追求简洁优雅,所以go不支持传统的try …catch…finally这种处理

2》Go中引入的处理方式;defer,panic,recover

3》这几个异常的使用场景可以这么简单描述:

Go中可以抛出一个Panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理

3》使用defer+recover处理错误

package main

import "fmt"

func test()  {
	defer func() {
		err:=recover()
		if err!=nil{
			fmt.Println("err:",err)
		}
	}()
	n1:=3
	n2:=0
	res:=n1/n2
	fmt.Println(res)
}
func main() {

	test()
	fmt.Println("发生了啥。。。")
}
/*结果
err: runtime error: integer divide by zero
发生了啥。。。
 */

4.错误处理的好处

进行错误处理后,程序不会轻易挂掉,如果加入预警代码,就可以让程序更加健壮

func test()  {
	defer func() {
		err:=recover()
		if err!=nil{
			fmt.Println("err:",err)
		}
	}()
	n1:=3
	n2:=0
	res:=n1/n2
	fmt.Println(res)
	//这里可以接入发邮件的功能
	fmt.Println("发送邮件给管理员")
}

5.自定义错误

GO中支持自定义错误,使用errors.New和panic内置函数

1》errors.New(“错误说明”),会返回一个error的类型的值,表示一个错误

2》panic内置函数,接收一个interface{}类型的值(也就是任何值了)作为参数,可以接收error类型的变量,输出错误信息,并退出程序

package main

import (
	"errors"
	"fmt"
)

func readConf(name string)(err error){
	if name=="config.ini"{
		//
		return nil
	}else {
		//返回一个自定义错误
		return errors.New("读取文件错误。。。")
	}
}
func funcx(){
	err:=readConf("configx.ini")
	if err!=nil{
		//如果读取文件错误,就输出这个错误,并终止程序
		panic(err)
	}
	fmt.Println("funcx...hello world")
}
func main() {
	funcx()
	fmt.Println("这世界,我来了,,,,,")
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值