zoukankan      html  css  js  c++  java
  • 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()下面的代码...")
    }
    
  • 相关阅读:
    Valid Anagram
    数据结构与算法2016-06-02
    数据结构与算法2016-06-03
    Delete Node in a Linked List
    Move Zeroes
    Javascript 常用的工具函数,更新中...
    有用的Javascript,长期更新...
    sql基本语法
    Javascript 有用的奇淫技巧
    关于 Vue 的一些问题(面试中面试官想听到的答案)
  • 原文地址:https://www.cnblogs.com/ygjzs/p/11779974.html
Copyright © 2011-2022 走看看