• 函数、包和错误处理


    一、函数

    为完成某一功能的程序指令(语句)的集合,称为函数。在go中,函数分为:自定义函数、系统函数。

    func 函数名(形参列表) (返回值列表){

      执行语句

      return 返回值列表

    }

    形参列表表示函数的输入

    函数中的执行语句表示未来实现某一功能代码块

    函数可以有返回值,也可以没有返回值

    package main
    
    import "fmt"
    
    func main() {
    	var n1 float64 = 1.2
    	var n2 float64 = 2.3
    	var operator byte = '+'
    	result := cal(n1, n2, operator)
    	fmt.Println("result = ", result)
    }
    
    func cal(n1 float64, n2 float64, operator byte) float64 {
    	var res float64
    	switch operator {
    	case '+':
    		res = n1 + n2
    	case '-':
    		res = n1 - n2
    	case '*':
    		res = n1 * n2
    	case '/':
    		res = n1 / n2
    	default:
    		fmt.Println("操作符输入错误")
    	}
    	return res
    }
    

    1、函数的调用机制

    在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间 和其它的栈的空间区分开来
    在每个函数对应的栈中,数据空间是独立的,不会混淆
    当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间

    package main
    
    import "fmt"
    
    func test(n int) {
    	n = n + 1
    	fmt.Println("test() n = ", n)
    }
    
    func getSum(n1 int, n2 int) int {
    	sum := n1 + n2
    	fmt.Println("getSum sum = ", sum)
    	return sum
    }
    
    func main() {
    	n := 20
    	test(n)
    	fmt.Println("main() n = ", n)
    
    	sum := getSum(10, 20)
    	fmt.Println("main sum = ", sum)
    }
    

    2、return语句

    go函数支持返回多个值,这一点是其它编程语句没有的。

    func 函数名(形参列表) (返回值列表){

      执行语句

      return 返回值列表

    }

    如果返回多个值时,在接收时希望忽略某个返回值,则使用_符号表示占位忽略

    如果返回值只有一个,(返回值类型列表)可以不写()

    package main
    
    import "fmt"
    
    func test(n int) {
    	n = n + 1
    	fmt.Println("test() n = ", n)
    }
    
    func getSum(n1 int, n2 int) int {
    	sum := n1 + n2
    	fmt.Println("getSum sum = ", sum)
    	return sum
    }
    
    func getSumAndSub(n1 int, n2 int) (int, int) {
    	sum := n1 + n2
    	sub := n1 - n2
    	return sum, sub
    }
    
    func main() {
    	n := 20
    	test(n)
    	fmt.Println("main() n = ", n)
    
    	sum := getSum(10, 20)
    	fmt.Println("main sum = ", sum)
    
    	res1, res2 := getSumAndSub(1, 2)
    	fmt.Printf("res1 = %v res2 = %v
    ", res1, res2)
    
    	_, res3 := getSumAndSub(3, 5)
    	fmt.Printf("res3 = %v", res3)
    }
    

    3、 函数的递归调用

    一个函数在函数体内又调用了本身,称为递归调用。

    package main
    
    import "fmt"
    
    func test(n int) {
    	if n > 2 {
    		n--
    		test(n)
    	}
    	fmt.Println("n = ", n)
    }
    
    func main() {
    	test(4)
    }
    

     函数递归需要遵守的重要原则:

    (1)、执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
    (2)、函数的局部变量是独立的,不会相互影响
    (3)、递归必须向退出递归的条件逼近,否则就是无限递归
    (4)、当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁

    斐波那契数

    package main
    
    import "fmt"
    
    //斐波那契数
    func fib(n int) int {
    	if (n == 1 || n == 2) {
    		return 1
    	} else {
    		return fib(n-1) + fib(n-2)
    	}
    }
    
    func main() {
    	res := fib(5)
    	fmt.Println("res5 = ", res)
    	fmt.Println("res4 = ", fib(4))
    	fmt.Println("res3 = ", fib(3))
    	fmt.Println("res2 = ", fib(2))
    	fmt.Println("res1 = ", fib(1))
    }
    

    猴子吃桃子问题

    package main
    
    import "fmt"
    
    //猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。
    //当到第十天时,想再吃时(还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
    // 思路分析:
    //1、第 10 天只有一个桃子
    //2、第9天有几个桃子 = (第10天桃子数量 + 1) * 2
    //3、规律: 第 n 天的桃子数据 peach(n) = (peach(n+1) + 1) * 2
    func peach(n int) int {
    	if n > 10 || n < 1 {
    		fmt.Println("输入的天数错误")
    		return 0
    	}
    	if n == 10 {
    		return 1
    	} else {
    		return (peach(n+1) + 1) * 2
    	}
    }
    
    func main() {
    	fmt.Println("第1天桃子数量是", peach(1))
    }
    

     函数使用的注意事项

    (1)、函数的形参列表可以是多个,返回值列表也可以是多个。
    (2)、形参列表和返回值列表的数据类型可以是值类型和引用类型。
    (3)、函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似public,首字母小写,只能被本包文件使用,其它包文件不能使用,类似private
    (4)、函数中的变量是局部的,函数外不生效
    (5)、基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
    (6)、如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传 入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用。

    package main
    
    import "fmt"
    
    func test(n *int) {
    	*n = *n + 10
    
    	fmt.Println("test() n = ", *n)
    }
    
    func main() {
    	num := 20
    	test(&num)
    	fmt.Println("main() num = ", num)
    }
    

     (7)、Go函数不支持函数重载

    (8)、在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。

    package main
    
    import "fmt"
    
    func getSum(n1 int, n2 int) int {
    	return n1 + n2
    }
    
    func main() {
    	a := getSum
    
    	fmt.Printf("a的类型%T,getSum类型是%T
    ", a, getSum)
    
    	res := a(10, 20)
    	fmt.Println("res = ", res)
    }
    

    (9)、函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用。

    package main
    
    import "fmt"
    
    func getSum(n1 int, n2 int) int {
    	return n1 + n2
    }
    
    func myFun(funvar func(int, int) int, num1 int, num2 int) int {
    	return funvar(num1, num2)
    }
    
    func main() {
    	res := myFun(getSum, 50, 60)
    	fmt.Println("res = ", res)
    }
    

    (10)、为了简化数据类型定义,Go支持自定义数据类型

    基本语法:type 自定义数据类型名 数据类型
    type myInt int //这时myInt就等价int来使用了
    type mySum func (int, int) int //这时mySum就等价一个函数类型 func (int, int) int

    package main
    
    import "fmt"
    
    func getSum(n1 int, n2 int) int {
    	return n1 + n2
    }
    
    type myFunType func(int, int) int
    
    func myFun(funvar myFunType, num1 int, num2 int) int {
    	return funvar(num1, num2)
    }
    
    func main() {
    	res := myFun(getSum, 50, 60)
    	fmt.Println("res = ", res)
    }
    

     (11)、支持对函数返回值命名

    package main
    
    import "fmt"
    
    func getSumAndSub(n1 int, n2 int) (sum int, sub int) {
    	sub = n1 - n2
    	sum = n1 + n2
    	return
    }
    
    func main() {
    	a, b := getSumAndSub(1, 2)
    	fmt.Printf("a = %v b = %v
    ", a, b)
    }
    

     (12)、使用 _ 标识符,忽略返回值

    package main
    
    import "fmt"
    
    func cal(n1 int, n2 int) (sum int, sub int) {
    	sum = n1 + n2
    	sub = n1 - n2
    	return
    }
    
    func main() {
    	res, _ := cal(10, 20)
    	fmt.Printf("res = %d", res)
    }
    

     (13)、Go支持可变参数

     func sum(args...int) sum int{

      函数体

    }

    func sum(n1 int,args...int) sum int{

      函数体

    }

    args是slice切片,通过args[index]可以访问到各个值。

    如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。

    package main
    
    import "fmt"
    
    func sum(n1 int, args ...int) int {
    	sum := n1
    	for i := 0; i < len(args); i++ {
    		sum += args[i]
    	}
    	return sum
    }
    
    func main() {
    	res := sum(10, 0, -1, 90, 10, 100)
    	fmt.Println("res = ", res)
    }
    

     编写一个函数,实现交换两个数的值。

    package main
    
    import "fmt"
    
    func swap(n1 *int, n2 *int) {
    	t := *n1
    	*n1 = *n2
    	*n2 = t
    }
    
    func main() {
    	a := 1
    	b := 2
    	swap(&a, &b)
    	fmt.Printf("a = %v b = %v", a, b)
    }
    

    4、init函数

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

    package main
    
    import "fmt"
    
    func init() {
    	fmt.Println("init()...")
    }
    
    func main() {
    	fmt.Println("main()...")
    }
    

     init 函数的注意事项

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

    package main
    
    import "fmt"
    
    var age = test()
    
    func test() int {
    	fmt.Println("test()")
    	return 90
    }
    func init() {
    	fmt.Println("init()...")
    }
    
    func main() {
    	fmt.Println("main()...")
    }
    

     (2)、init函数最主要的作用,就是完成一些初始化的工作

    5、匿名函数

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

    在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

    package main
    
    import "fmt"
    
    func main() {
    	res := func(n1 int, n2 int) int {
    		return n1 + n2
    	}(10, 20)
    	fmt.Println("res = ", res)
    }
    

    将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数。

    package main
    
    import "fmt"
    
    func main() {
    	f := func(n1 int, n2 int) int {
    		return n1 + n2
    	}
    	res := f(10, 30)
    	fmt.Println("res = ", res)
    	res2 := f(20, 30)
    	fmt.Println("res2 = ", res2)
    }
    

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

    package main
    
    import "fmt"
    
    var (
    	fun1 = func(n1 int, n2 int) int {
    		return n1 * n2
    	}
    )
    
    func main() {
    	res := fun1(4, 9)
    	fmt.Println("res = ", res)
    }
    

    6、闭包

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

    package main
    
    import "fmt"
    
    //AddUpper是一个函数,返回的数据类型是fun (int) int
    func AddUpper() func(int) int {
    	var n int = 10
    	return func(x int) int {
    		n = n + x
    		return n
    	}
    }
    
    func main() {
    	f := AddUpper()
    	fmt.Println(f(1))
    	fmt.Println(f(2))
    	fmt.Println(f(3))
    }
    

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

    闭包可以理解成:闭包是类, 函数是操作,n是字段。函数和它使用到n构成闭包。
    当反复的调用f函数时,因为n是初始化一次,因此每调用一次就进行累计。
    要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。

    package main
    
    import (
    	"fmt"
    	"strings"
    )
    
    //编写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包。
    //调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。
    //strings.HasSuffix , 该函数可以判断某个字符串是否有指定的后缀。
    func makeSuffix(suffix string) func(string) string {
    	return func(name string) string {
    		if !strings.HasSuffix(name, suffix) {
    			return name + suffix
    		}
    		return name
    	}
    }
    
    func main() {
    	f := makeSuffix(".jpg")
    	fmt.Println("文件名处理后 = ", f("winter"))
    	fmt.Println("文件名处理后 = ", f("bird.jpg"))
    }
    

     7、函数的defer

    在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完 毕后,及时的释放资源,go的设计者提供defer(延时机制)。

    package main
    
    import (
    	"fmt"
    )
    
    func sum(n1 int, n2 int) int {
    	//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
    	//当函数执行完毕后,再从defer栈按照先入后出的方式出栈执行
    	defer fmt.Println("ok1 n1 = ", n1)
    	defer fmt.Println("ok2 n2 = ", n2)
    
    	res := n1 + n2
    	fmt.Println("ok3 res = ", res)
    	return res
    }
    
    func main() {
    	res := sum(10, 20)
    	fmt.Println("res = ", res)
    }
    

    在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。

    package main
    
    import (
    	"fmt"
    )
    
    func sum(n1 int, n2 int) int {
    	//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
    	//当函数执行完毕后,再从defer栈按照先入后出的方式出栈执行
    	defer fmt.Println("ok1 n1 = ", n1)
    	defer fmt.Println("ok2 n2 = ", n2)
    
    	n1++
    	n2++
    
    	res := n1 + n2
    	fmt.Println("ok3 res = ", res)
    	return res
    }
    
    func main() {
    	res := sum(10, 20)
    	fmt.Println("res = ", res)
    }
    

    defer最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。

    8、函数参数传递方式

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

    package main
    
    import "fmt"
    
    var name = "tom"
    
    func test01() {
    	fmt.Println(name)
    }
    
    func test02() {
    	name := "jack"
    	fmt.Println(name)
    }
    
    func main() {
    	fmt.Println(name)
    	test01()
    	test02()
    	test01()
    }
    

    值类型:基本数据类型int系列,float系列, bool,string,数组和结构体struct

    引用类型:指针、slice 切片、map、管道chan、interface 等都是引用类型

    9、变量作用域

    (1)、函数内部声明或定义的变量叫做局部变量,作用域仅限于函数内部。

    (2)、函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用 域在整个程序有效
    (3)、如果变量是在一个代码块,比如for/if中,那么这个变量的的作用域就在该代码块

    package main
    
    import "fmt"
    
    //编写一个函数,从终端输入一个整数打印出对应的金子塔
    func printPyramid(totalLevel int) {
    	for i := 1; i <= totalLevel; i++ {
    		for j := 1; j <= totalLevel-i; j++ {
    			fmt.Print("  ")
    		}
    		for k := 1; k <= 2*i-1; k++ {
    			fmt.Print("* ")
    		}
    		fmt.Println()
    	}
    }
    
    func main() {
    	var n int
    	fmt.Println("请输入打印金字塔的层数")
    	fmt.Scanln(&n)
    	printPyramid(n)
    }
    

    编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表

    package main
    
    import "fmt"
    
    //编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表
    func printMulti(num int) {
    	for i := 1; i <= num; i++ {
    		for j := 1; j <= i; j++ {
    			fmt.Printf("%v * %v = %v 	", j, i, j*i)
    		}
    		fmt.Println()
    	}
    }
    
    func main() {
    	var num int
    	fmt.Println("请输入一个整数")
    	fmt.Scanln(&num)
    	printMulti(num)
    }
    

    10、字符串常用的系统函数

    package main
    
    import (
    	"fmt"
    	"strconv"
    	"strings"
    )
    
    func main() {
    	//1、统计字符串的长度,按字节len(str)
    	//golang的编码统一为utf-8,字母和数字占一个字节,汉字占用3个字节
    	str := "hello北京"
    	fmt.Println("str len = ", len(str))
    
    	//2、字符串遍历
    	str2 := "hello北京"
    	r := []rune(str2)
    	for i := 0; i < len(r); i++ {
    		fmt.Printf("%c	", r[i])
    	}
    	fmt.Println()
    
    	//3、字符串转整数
    	n, err := strconv.Atoi("12")
    	if err != nil {
    		fmt.Println("转换错误", err)
    	} else {
    		fmt.Println("转换的结果是", n)
    	}
    
    	//4、整数转字符串
    	str = strconv.Itoa(12345)
    	fmt.Printf("str = %v str = %T
    ", str, str)
    
    	//5、字符串转[]byte
    	var bytes = []byte("hello go")
    	fmt.Printf("bytes = %v
    ", bytes)
    
    	//6、[]byte 转字符串
    	str = string([]byte{97, 98, 99})
    	fmt.Printf("str = %v
    ", str)
    
    	//7、10进制转2,8,16进制
    	str = strconv.FormatInt(123, 2)
    	fmt.Printf("123对应的2进制是 = %v
    ", str)
    	str = strconv.FormatInt(123, 16)
    	fmt.Printf("123对应的16进制是 = %v
    ", str)
    
    	//8、查找子串是否指定在指定的字符串中
    	b := strings.Contains("seafood", "mary")
    	fmt.Printf("b = %v
    ", b)
    
    	//9、统计一个字符串有几个指定的子串
    	num := strings.Count("ceheese", "Abc")
    	fmt.Printf("num = %v
    ", num)
    
    	//10、不区分大小写的字符串比较(==是区分字母大小写的比较)
    	b = strings.EqualFold("abc", "Abc")
    	fmt.Printf("b = %v
    ", b)
    	fmt.Println("结果", "abc" == "Abc")
    
    	//11、返回子串在字符串第一次出现的index值,如果没有返回-1
    	index := strings.Index("NLT_abcabcabc", "abc")
    	fmt.Printf("index = %v
    ", index)
    
    	//12、返回子串在字符串最后一次出现的index,如果没有返回-1
    	index = strings.LastIndex("go golang", "go")
    	fmt.Printf("index = %v
    ", index)
    
    	//13、将指定的子串替换成另一个子串
    	str2 = "go go hello"
    	str = strings.Replace(str2, "go", "北京", -1)
    	fmt.Printf("str = %v str2 = %v
    ", str, str2)
    
    	//14、按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组
    	strArr := strings.Split("hello,world,ok", ",")
    	for i := 0; i < len(strArr); i++ {
    		fmt.Printf("str[%v] = %v
    ", i, strArr[i])
    	}
    	fmt.Printf("strArr = %v
    ", strArr)
    
    	//15、将字符串的字母进行大小写的转换
    	str = "goLang Hello"
    	str = strings.ToLower(str)
    	fmt.Printf("str = %v
    ", str)
    	str = strings.ToUpper(str)
    	fmt.Printf("str = %v
    ", str)
    
    	//16、将字符串左右两边的空格去掉
    	str = strings.TrimSpace(" tn a lone gopher ntrn ")
    	fmt.Printf("str = %q
    ", str)
    
    	//17、将字符串左右两边指定的字符去掉
    	str = strings.Trim("! he!llo! ", " !")
    	fmt.Printf("str = %q
    ", str)
    
    	//18、将字符串左边指定的字符去掉
    	str = strings.TrimLeft("! he!llo! ", " !")
    	fmt.Printf("str = %q
    ", str)
    
    	//19、将字符串右边指定的字符去掉
    	str = strings.TrimRight("! he!llo! ", " !")
    	fmt.Printf("str = %q
    ", str)
    
    	//20、判断字符串是否以指定的字符串开头
    	fmt.Println(strings.HasPrefix("ftp://192.168.10.1", "ftp"))
    
    	//21、判断字符串是否以指定的字符串结束
    	fmt.Println(strings.HasSuffix("abc.jpg", "abc"))
    }
    

    11、时间和日期相关函数

    时间和日期相关函数,需要导入time包

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	//1、time.Time 类型,用于表示时间
    	now := time.Now()
    	fmt.Printf("now = %v now type = %T
    ", now, now)
    
    	//2、获取年月日,时分秒
    	fmt.Printf("年 = %v
    ", now.Year())
    	fmt.Printf("月 = %v
    ", now.Month())
    	fmt.Printf("月 = %v
    ", int(now.Month()))
    	fmt.Printf("日 = %v
    ", now.Day())
    	fmt.Printf("时 = %v
    ", now.Hour())
    	fmt.Printf("分 = %v
    ", now.Minute())
    	fmt.Printf("秒 = %v
    ", now.Second())
    
    	//3、fmt.Printf()格式化日期时间
    	fmt.Printf("当前时间 %d-%d-%d %d:%d:%d 
    ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    
    	//4、fmt.Sprintf()格式化日期时间
    	dateStr := fmt.Sprintf("当前时间 %d-%d-%d %d:%d:%d 
    ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    	fmt.Printf("dateStr = %v
    ", dateStr)
    
    	//5、time.Format()格式化日期时间
    	fmt.Printf(now.Format("2006-01-02 19:09:34"))
    	fmt.Println()
    	fmt.Printf(now.Format("2010-12-03"))
    	fmt.Println()
    	fmt.Printf(now.Format("15:09:43"))
    	fmt.Println()
    }
    

     12、内置函数

    golang提供了一些内置函数可以直接使用。

    len:用来求长度,比如string、array、slice、map、channel

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

    package main
    
    import "fmt"
    
    func main() {
    	num := 100
    	fmt.Printf("num的类型 = %T num的值 = %v num的地址 = %v
    ", num, num, &num)
    
    	num2 := new(int)
    	*num2 = 100
    	fmt.Printf("num2的类型 = %T num2的值 = %v num2的地址 = %v
     num2这个指针指向的值 = %v", num2, num2, &num2, *num2)
    }
    

    二、包

    包的本质实际上就是创建不同的文件夹,来存放程序文件。

    go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构。

    1、包的三大作用:
    区分相同名字的函数、变量等标识符
    当程序文件很多时,可以很好的管理项目 控
    制函数、变量等访问范围,即作用域
    2、打包基本语法
    package 包名
    3、引入包的基本语法
    import "包的路径"

    4、包使用的注意事项
    (1)、在给一个文件打包时,该包对应一个文件夹,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
    (2)、当一个文件要使用其它包函数或变量时,需要先引入对应的包
    引入方式1:import "包名"
    引入方式2:
    import (
    "包名"
    "包名"
    )
    (3)、package指令在文件第一行,然后是import指令。
    (4)、在import包时,路径从$GOPATH的src下开始,不用带src , 编译器会自动从src下开始引入
    (5)、为了让其它包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其它语言的public,这样才能跨包访问。
    (6)、在访问其它包函数,变量时,其语法是 包名.函数名
    (7)、如果包名较长,go支持给包取别名, 注意:取别名后,原来的包名就不能使用了,需要使用别名来访问该包的函数和变量。
    (8)、在同一包下,不能有相同的函数名(也不能有相同的全局变量名),否则报重复定义
    (9)、如果要编译成一个可执行程序文件,就需要将这个包声明为main, 即package main .这个就是一个语法规范,如果你是写一个库 ,包名可以自定义

    三、错误处理

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

    go语言不支持传统的try...catch...finally这种处理。

    go中引入的错误处理方式为:defer,panic,recover

    go中抛出panic异常,然后在defer中通过recover捕获这个异常,然后正常处理

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func test() {
    	//使用defer+recover来捕获和处理异常
    	defer func() {
    		err := recover() //recover()内置函数,可以捕获到异常
    		if err != nil { //说明捕获到错误
    			fmt.Println("err = ", err)
    			fmt.Println("发送邮件") //可以将错误信息发送给管理员
    		}
    	}()
    	num1 := 10
    	num2 := 0
    	res := num1 / num2
    	fmt.Println("res = ", res)
    }
    
    func main() {
    	test()
    	for {
    		fmt.Println("main()下面的代码...")
    		time.Sleep(time.Second)
    	}
    }
    

    自定义错误:

    Go程序中,也支持自定义错误,使用 errors.New和panic内置函数。
    (1)、errors.New("错误说明") , 会返回一个error类型的值,表示一个错误
    (2)、panic内置函数 ,接收一个interface{}类型的值(也就是任何值了)作为参数。可以接收error类型的变量,输出错误信息,并退出程序

    package main
    
    import (
    	"errors"
    	"fmt"
    )
    
    //函数读取配置文件init.conf的信息,如果文件名传入不正确,返回一个自定义错误
    func readConf(name string) (err error) {
    	if name == "config.ini" {
    		//读取......
    		return nil
    	} else {
    		//返回自定义错误
    		return errors.New("读取文件错误...")
    	}
    }
    
    func test() {
    	err := readConf("config.ini")
    	if err != nil {
    		//如果读取文件发生错误,就输出这个错误,并终止程序
    		panic(err)
    	}
    	fmt.Println("test()继续执行...")
    }
    
    func main() {
    	test()
    	fmt.Println("main()下面的代码...")
    }
    
  • 相关阅读:
    地铁项目结对编程
    地铁项目初步计划及简单设计
    构建之法浅读感想
    集美大学1511,1512软件工程课程作业总结
    第二次作业小结
    第二次作业评分可能要 delay 一些
    第一次作业小结
    关于我
    面向对象设计与构造第四单元总结及期终总结
    面向对象设计与构造第三单元作业总结
  • 原文地址:https://www.cnblogs.com/xidian2014/p/10586299.html
Copyright © 2020-2023  润新知