Go语言精修(尚硅谷笔记)第六章
六、函数、包和错误处理
6.1 函数概念
不用函数的弊端
1)写法可以完成功能, 但是代码冗余
2 ) 同时不利于代码维护
概念:为完成某一功能的程序指令(语句)的集合,称为函数。
在Go中,函数分为: 自定义函数、系统函数
基本语法
//函数的基本语法
func 函数名(形参列表)(返回值列表){ // 形参名在前 形参类型在后执行语句..return 返回值列表
}
- 形参列表:表示函数的输入
- 函数中的语句:表示为了实现某一功能代码块
- 函数可以有返回值,也可以没有
案例
package main
import ("fmt"
)func cal(n1 float64, n2 float64, operator byte) float64 {var res float64switch operator {case '+':res = n1 + n2case '-':res = n1 - n2case '*':res = n1 * n2case '/':res = n1 / n2default:fmt.Println("操作符号错误...")}return res
}func main() {//请大家完成这样一个需求://输入两个数,再输入一个运算符(+,-,*,/),得到结果.。//分析思路....var n1 float64 = 1.2var n2 float64 = 2.3var operator byte = '+'result := cal(n1, n2 , operator) fmt.Println("result~=", result)
}
6.2 包的概念
包的本质实际上就是创建不同的文件夹,来存放程序文件。
说明:go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的
包的三大作用
- 区分相同名字的函数、变量等标识符
- 当程序文件很多时,可以很好的管理项目
- 控制函数、变量等访问范围,即作用域
打包基本语法
package 包名
引入包的基本语法
import"包的路径"
注意事项
1 ) 在给一个文件打包时,该包对应一个文件夹,比如这里的 utils 文件夹对应的包名就是utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。
2 ) 当一个文件要使用其它包函数或变量时,需要先引入对应的包
-
引入方式 1 :import “包名”
-
引入方式 2 :
import ("包名""包名" )
-
package 指令在 文件第一行,然后是 import 指令。
-
在import 包时,路径从 $GOPATH的 src 下开始,不用带src, 编译器会自动从src下开始引入
3 ) 为了让其它包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其它语言的public,这样才能跨包访问。比如 utils.go 的
//为了让其它包的文件使用Cal函数,需要将C大小类似其它语言的public
func Cal(n1 float64, n2 float64, operator byte) float64 {....
4 ) 在访问其它包函数,变量时,其语法是 包名.函数名, 比如这里的 main.go文件中
utils.Cal(n1, n2 , operator)
5 ) 如果包名较长,Go支持给包取别名, 注意细节:取别名后,原来的包名就不能使用了
package main
import ("fmt"util "go_code/chapter06/fundemo01/utils"
)
说明: 如果给包取了别名,则需要使用别名来访问该包的函数和变量。
6.) 在同一包下,不能有相同的函数名(也不能有相同的全局变量名),否则报重复定义
7 ) 如果你要编译成一个可执行程序文件,就需要将这个包声明为 main, 即 packagemain.这个就 是一个语法规范,如果你是写一个库 ,包名可以自定义
6.3 函数的调用机制
( 1 ) 在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其它的栈的空间区分开来
( 2 ) 在每个函数对应的栈中,数据空间是独立的,不会混淆
( 3 ) 当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间。
6.4 return
基本语法和说明
// Go函数支持返回多个值,这一点是其他编程语言没有的
func 函数名(形参列表)(返回值类型列表){语句return 返回值列表
}
1.如果返回多个值,在接收时,希望忽略某个返回值,则使用_符号表示占位忽略
2.如果返回值只有一个,返回值类型列表可以不写()
6.5 函数递归调用
一个函数在函数体内又调用了本身,我们称为递归调用
函数递归需要遵守的重要原则:
1 ) 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
2 ) 函数的局部变量是独立的,不会相互影响
3 ) 递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)
4 ) 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁
题 1 :斐波那契数
请使用递归的方式,求出斐波那契数 1 , 1 , 2 , 3 , 5 , 8 , 13
给你一个整数n,求出它的斐波那契数是多少?
package main
import ("fmt"
)/*
请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13...
给你一个整数n,求出它的斐波那契数是多少?
*/
func fbn(n int) int {if (n == 1 || n == 2) {return 1} else {return fbn(n - 1) + fbn(n - 2)}
}func main() {res := fbn(3)//测试fmt.Println("res=", res)fmt.Println("res=", fbn(4)) // 3fmt.Println("res=", fbn(5)) // 5 fmt.Println("res=", fbn(6)) // 8
}
6.6 函数注意事项
1 ) 函数的形参列表可以是多个,返回值列表也可以是多个。
2 ) 形参列表和返回值列表的数据类型可以是值类型和引用类型。
3 ) 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似public, 首字母小写,只能被本包文件使用,其它包文件不能使用,类似privat
4 ) 函数中的变量是局部的,函数外不生效
package main
import ("fmt"
)
//函数中的变量是局部的,函数外不生效
func test(){//n1 是 test函数的局部变量,只能在test中使用var n1 int = 10
}func main() { fmt.Println("n1=", n1) //报错,这里不能使用n1
}
5 ) 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
package main
import ("fmt"
)func test02(n1 int){n1 = n1 + 10fmt.Println("test02() n1=", n1)
}func main() { num := 20test02(num)fmt.Println("main() num=", num)
}
6.) 如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用 。
package main
import ("fmt"
)// n1 就是 *int 类型
func test03(n1 *int) {fmt.Printf("n1的地址 %v\n",&n1)*n1 = *n1 + 10fmt.Println("test03() n1= ", *n1) // 30
}func main() {num := 20fmt.Printf("num的地址=%v\n", &num)test03(&num)fmt.Println("main() num= ", num) // 30
}
7 ) Go函数不支持函数重载
package main
import ("fmt"
)
//有两个test02不支持重载
func test02(n1 int) { n1 = n1 + 10fmt.Println("test02() n1= ", n1)
}
//有两个test02不支持重载
func test02(n1 int , n2 int) {}func main() {}
8 ) 在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用
package main
import ("fmt"
)//在Go中,函数也是一种数据类型,
//可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用
func getSum(n1 int, n2 int) int {return n1 + n2
}func main() {a := getSumfmt.Printf("a的类型%T, getSum类型是%T\n", a, getSum)res := a(10, 40) // 等价 res := getSum(10, 40)fmt.Println("res=", res)
}
9 ) 函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
package main
import ("fmt"
)//在Go中,函数也是一种数据类型,
//可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用
func getSum(n1 int, n2 int) int {return n1 + n2
}//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func myFun(funvar func(int, int) int, num1 int, num2 int ) int {return funvar(num1, num2)
}func main() {//看案例res2 := myFun(getSum, 50, 60)fmt.Println("res2=", res2)
}
10 ) 为了简化数据类型定义,Go支持自定义数据类型
基本语法:type 自定义数据类型名 数据类型 // 理解: 相当于一个别名
package main
import ("fmt"
)//在Go中,函数也是一种数据类型,
//可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用func getSum(n1 int, n2 int) int {return n1 + n2
}//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func myFun(funvar func(int, int) int, num1 int, num2 int ) int {return funvar(num1, num2)
}//再加一个案例
//这时 myFun 就是 func(int, int) int类型
type myFunType func(int, int) int//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用
func myFun2(funvar myFunType, num1 int, num2 int ) int {return funvar(num1, num2)
}func main() {// 给int取了别名 , 在go中 myInt 和 int 虽然都是int类型,但是go认为myInt和int两个类型type myInt int var num1 myInt // var num2 intnum1 = 40num2 = int(num1) //各位,注意这里依然需要显示转换,go认为myInt和int两个类型fmt.Println("num1=", num1, "num2=",num2)//看案例res3 := myFun2(getSum, 500, 600)fmt.Println("res3=", res3)
}
11 )支持对函数返回值命名
package main
import ("fmt"
)//支持对函数返回值命名
func getSumAndSub(n1 int, n2 int) (sum int, sub int){sub = n1 - n2sum = n1 + n2return
}func main() {//看案例a1, b1 := getSumAndSub(1, 2)fmt.Printf("a=%v b=%v\n", a1, b1)
}
12 ) 使用 _ 标识符,忽略返回值
13 ) Go支持可变参数
//支持0到多个参数
func sum(args...int){
}
//支持1到多个参数
func sum(n1 int,args... int) sum int{
}
说明:
- args是slice切片,通过args[index]可以访问到各个值
- 案例演示:编写一个函数sum,可以求出1到多个int的和
- 如果一个函数的形参列表中有可变的参数,则可变参数需要放到形参列表的最后
6.7 init()函数
每一个源文件都可以包含一个 init 函数,该函数会在main函数执行前,被Go运行框架调用,也 就是说init会在main函数前被调用。
package main
import ("fmt"
)func init() {fmt.Println("init()")
}func main() { fmt.Println("main()")
}
输出的结果是:
init()
main()
注意事项
1 ) 如果一个文件同时包含全局变量定义, init 函数和 main 函数,则执行的流程全局变量定义 - >init函数 - >main 函数
package main
import ("fmt"
)var age = test()
//为了看到全局变量是先被初始化的,我们这里先写函数
func test() int {fmt.Println("test()")//1return 90
}
// init函数,通常可以在init函数中完成初始化工作
func init() {fmt.Println("init()")//2
}func main() { fmt.Println("main()...age=",age)//3
}
2 ) init函数最主要的作用,就是完成一些初始化的工作
package utils
import "fmt"
var Age int
var Name string//Age 和 Name 全局变量,我们需要在main.go 使用
//但是我们需要初始化Age 和 Name//init 函数完成初始化工作
func init() {fmt.Println("utils 包的 init()...")Age = 100Name = "tom~"
}
3 ) 细节说明: 面试题:案例如果main.go 和 utils.go 都含有 变量定义,init函数时,执行的流程又是怎么样的呢?
6.8 匿名函数
Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考 虑使用匿名函数,匿名函数也可以实现多次调用。
匿名函数使用方式 1
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。 【案例演示】
package main
import ("fmt"
)
func main() {//在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次//案例演示,求两个数的和, 使用匿名函数的方式完成res1 := func (n1 int, n2 int) int {return n1 + n2}(10, 20)fmt.Println("res1=", res1)
}
匿名函数使用方式 2
将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
package main
import ("fmt"
)func main() {//将匿名函数func (n1 int, n2 int) int赋给 a变量//则a 的数据类型就是函数类型 ,此时,我们可以通过a完成调用a := func (n1 int, n2 int) int {return n1 - n2}res2 := a(10, 30)fmt.Println("res2=", res2)res3 := a(90, 30)fmt.Println("res3=", res3)
}
全局匿名函数
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效。
package main
import ("fmt"
)var (//fun1就是一个全局匿名函数Fun1 = func (n1 int, n2 int) int {return n1 * n2}
)func main() {//全局匿名函数的使用res4 := Fun1(4, 9)fmt.Println("res4=", res4)
}
6.9 **闭包
基本介绍:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
闭包让你可以在一个内层函数中访问到其外层函数的作用域。
可简单理解为:有权访问另一个函数作用域内变量的函数都是闭包。
package main
import ("fmt"
)//累加器
func AddUpper() func (int) int {var n int = 10 return func (x int) int {n = n + xreturn n}
}func main() {//使用前面的代码f := AddUpper()fmt.Println(f(1))// 11 fmt.Println(f(2))// 13fmt.Println(f(3))// 16}
对上面代码的说明和总结
1 ) AddUpper 是一个函数,返回的数据类型是 fun(int)int
2 ) 闭包的说明
var n int = 10 return func (x int) int {n = n + xreturn n}
返回的是一个匿名函数, 但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包。
6.9.1使用案例
1 ) 编写一个函数 makeSuffix(suffixstring) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包
2 ) 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回 文件名.jpg, 如果已经有.jpg后缀,则返回原文件名。
3 ) 要求使用闭包的方式完成
4 ) strings.HasSuffix, 该函数可以判断某个字符串是否有指定的后缀。
package main
import ("fmt""strings"
)//
// 1)编写一个函数 makeSuffix(suffix string) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包
// 2)调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。
// 3)要求使用闭包的方式完成
// 4)strings.HasSuffix , 该函数可以判断某个字符串是否有指定的后缀。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.jgpfmt.Println("文件名处理后=", f2("bird.jpg")) // bird.jpgfmt.Println("文件名处理后=", makeSuffix2("jpg", "winter")) // winter.jgpfmt.Println("文件名处理后=", makeSuffix2("jpg", "bird.jpg")) // bird.jpg
}
上面代码的总结和说明:
1 ) 返回的匿名函数和 makeSuffix(suffixstring) 的 suffix 变量 组合成一个闭包,因为 返回的函数引用到suffix这个变量
2 ) 我们体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入 后缀名,比如 .jpg,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复使用。(以面向对象思想理解闭包----------外部整体 像一个类,先传入的.jpg 像设置类里的一个public属性,再向返回函数传参 像调用类的成员函数,此时成员函数可以调用类里已设置的属性。)
6.9.2 闭包经典使用场景
1、return一个内部函数,读取内部函数的变量;
2、函数作为参数
3、IIFE(自执行函数)
5、使用回调函数就是在使用闭包
6、将外部函数创建的变量值始终保持在内存中;(会出现内存泄漏)
6.9.3 使用闭包注意点
因为使用闭包会包含其他函数的作用域,会比其他函数占据更多的内存空间,不会在调用结束之后被垃圾回收机制(简称GC机制)回收,多度使用闭包会过度占用内存,造成内存泄漏。
6.9.4 闭包相关面试题
1、简述什么是闭包,闭包的作用是什么?写出一个简单的闭包例子。
2、闭包会造成内存泄漏吗?
会,因为使用闭包会包含其他函数的作用域,会比其他函数占据更多的内存空间,不会在调用结束之后被垃圾回收机制回收,多度使用闭包会过度占用内存,造成内存泄漏。
3、for循环和闭包(必刷题)
6.10 defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。看下模拟代码。
package main
import ("fmt"
)func sum(n1 int, n2 int) int {//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)//当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行defer fmt.Println("ok1 n1=", n1) //defer 3. ok1 n1 = 10defer fmt.Println("ok2 n2=", n2) //defer 2. ok2 n2= 20res := n1 + n2 // res = 30fmt.Println("ok3 res=", res) // 1. ok3 res= 30return res}func main() {res := sum(10, 20)fmt.Println("res=", res) // 4. res= 30
}
执行后,输出的结果:
ok3 res= 30
ok2 n2= 20
ok1 n1 = 10
res= 30
注意事项
1 ) 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer 后的语句压入到一个栈中[我为了讲课方便,暂时称该栈为defer栈],然后继续执行函数下一个语句。
2 ) 当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制),所以同学们看到前面案例输出的顺序。
3 ) 在defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。
defer使用
1 ) 在golang编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是锁资源), 可以执行 defer file.Close() defer connect.Close()
2 ) 在defer后,可以继续使用创建资源.
3 ) 当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源.
4 ) 这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心。
6.11 函数传递方式
传递方式分类
1 ) 值传递
2 ) 引用传递
其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。
值类型和引用类型
1 ) 值类型:基本数据类型 int 系列,float 系列,bool,string 、数组和结构体struct
2 ) 引用类型:指针、slice切片、map、管道chan、interface 等都是引用类型
各自的特点
1 ) 值类型默认是值传递:变量直接存储值,内存通常在栈中分配。
2 ) 引用类型默认是引用传递:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,改地址对应的数据空间就成为一个垃圾,由GC来回收。
3 ) 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用 。
6.12 变量作用域
1 ) 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
2 ) 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
3 ) 如果变量是在一个代码块,比如 for/if中,那么这个变量的的作用域就在该代码块
6.13 字符串常用系统函数
1 ) 统计字符串的长度,按字节 len(str)
2 ) 字符串遍历,同时处理有中文的问题 r:=[]rune(str)
3 ) 字符串转整数: n,err:=strconv.Atoi(" 12 ")
4 ) 整数转字符串 str=strconv.Itoa( 12345 )
5 ) 字符串 转 []byte: varbytes=[]byte(“hello go”)
6.) []byte 转 字符串:str=string([]byte{ 97 , 98 , 99 })
7 ) 10 进制转 2 , 8 , 16.进制: str=strconv.FormatInt( 123 , 2 )// 2 - > 8 , 16
8 ) 查找子串是否在指定的字符串中:strings.Contains(“seafood”,“foo”)//true
9 ) 统计一个字符串有几个指定的子串 : strings.Count(“ceheese”,“e”)// 4
10 ) 不区分大小写的字符串比较(==是区分字母大小写的):fmt.Println(strings.EqualFold(“abc”,“Abc”))//true
11 )返回子串在字符串第一次出现的index值,如果没有返回- 1 :strings.Index(“NLT_abc”,“abc”)// 4
12 ) 返回子串在字符串最后一次出现的index,如没有返回- 1 :strings.LastIndex(“gogolang”,“go”)
13 ) 将指定的子串替换成 另外一个子串:strings.Replace(“gogohello”,“go”,“go语言”,n)n可以指定你希望替换几个,如果n=- 1 表示全部替换
14 ) 按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:strings.Split(“hello,wrold,ok”,“,”)
15 ) 将字符串的字母进行大小写的转换:strings.ToLower(“Go”)//gostrings.ToUpper(“Go”)//GO
16.) 将字符串左右两边的空格去掉: strings.TrimSpace("tnalonegopherntrn ")
17 ) 将字符串左右两边指定的字符去掉 : strings.Trim(“!hello!”,“!”) //[“hello”]//将左右两边! 和 ""去掉
18 ) 将字符串左边指定的字符去掉 : strings.TrimLeft(“!hello!”,“!”) //[“hello”]//将左边! 和 " "去掉
19 ) 将字符串右边指定的字符去掉 :strings.TrimRight(“!hello!”,“!”) //[“hello”]//将右边! 和 " "去掉
20 ) 判断字符串是否以指定的字符串开头:strings.HasPrefix("ftp:// 192. 168. 10. 1 ",“ftp”)//true
21 ) 判断字符串是否以指定的字符串结束:strings.HasSuffix(“NLT_abc.jpg”,“abc”)//false
6.14 时间和日期相关函数
1 ) 时间和日期相关函数,需要导入 time包
2 ) time.Time 类型,用于表示时间
package mainimport ("fmt""time"
)func main() {//看看日期和时间相关函数和方法使用//1. 获取当前时间now := time.Now()fmt.Printf("now=%v now type=%T\n", now, now)
}
3 ) 如何获取到部分的日期信息
package main
import ("fmt""time"
)
func main() {//看看日期和时间相关函数和方法使用//1. 获取当前时间now := time.Now()fmt.Printf("now=%v now type=%T\n", now, now)//2.通过now可以获取到年月日,时分秒fmt.Printf("年=%v\n", now.Year())fmt.Printf("月=%v\n", now.Month())fmt.Printf("月=%v\n", int(now.Month()))fmt.Printf("日=%v\n", now.Day())fmt.Printf("时=%v\n", now.Hour())fmt.Printf("分=%v\n", now.Minute())fmt.Printf("秒=%v\n", now.Second())
}
4 ) 格式化日期时间
方式 1 : 就是使用Printf 或者 SPrintf
package main
import ("fmt""time"
)func main() {//格式化日期时间fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d \n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())dateStr := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d \n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())fmt.Printf("dateStr=%v\n", dateStr)
}
方式二: 使用 time.Format() 方法完成:
package main
import ("fmt""time"
)func main() {//格式化日期时间的第二种方式fmt.Printf(now.Format("2006-01-02 15:04:05"))fmt.Println()fmt.Printf(now.Format("2006-01-02"))fmt.Println()fmt.Printf(now.Format("15:04:05"))fmt.Println()fmt.Printf(now.Format("2006"))fmt.Println()
}
5 ) 时间的常量
const(Nanosecond Duration= 1 //纳秒Microsecond = 1000 *Nanosecond //微秒Millisecond = 1000 *Microsecond//毫秒Second = 1000 *Millisecond//秒Minute = 60 *Second//分钟Hour = 60 *Minute//小时
)
常量的作用:在程序中可用于获取指定时间单位的时间,比如想得到 100 毫秒
100 *time.Millisecond
7 ) time的Unix和UnixNano的方法
编写一段代码来统计 函数test 03 执行的时间
package main
import ("fmt""time""strconv"
)func test03() {str := ""for i := 0; i < 100000; i++ {str += "hello" + strconv.Itoa(i)}
}func main() {//在执行test03前,先获取到当前的unix时间戳start := time.Now().Unix()test03()end := time.Now().Unix()fmt.Printf("执行test03()耗费时间为%v秒\n", end-start)
}
6.15 系统函数
1 ) len:用来求长度,比如string、array、slice、map、channel
2 ) new:用来分配内存,主要用来分配值类型,比如int、float 32 ,struct返回的是指针
package main
import ("fmt"
)func main() {num1 := 100fmt.Printf("num1的类型%T , num1的值=%v , num1的地址%v\n", num1, num1, &num1)num2 := new(int) // *int//num2的类型%T => *int//num2的值 = 地址 0xc04204c098 (这个地址是系统分配)//num2的地址%v = 地址 0xc04206a020 (这个地址是系统分配)//num2指向的值 = 100*num2 = 100fmt.Printf("num2的类型%T , num2的值=%v , num2的地址%v\n num2这个指针,指向的值=%v", num2, num2, &num2, *num2)
}
上面代码对应的内存分析图:
3 ) make:用来分配内存,主要用来分配引用类型,比如channel、map、slice。
6.16 错误处理
1 ) 在默认情况下,当发生错误后(panic),程序就会退出(崩溃.)
2 ) 如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行。还可以在捕获到错误后,给管理员一个提示(邮件,短信。。。)
基本说明
1 ) Go语言追求简洁优雅,所以,Go语言不支持传统的 trycatchfinally 这种处理。
2 ) Go中引入的处理方式为: defer , panic , recover
3 ) 这几个异常的使用场景可以这么简单描述: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)}}()num1 := 10num2 := 0res := num1 / num2fmt.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 ("fmt"_ "time""errors"
)
//函数去读取以配置文件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() {//测试自定义错误的使用test02()fmt.Println("main()下面的代码...")
}
相关文章:

Go语言精修(尚硅谷笔记)第六章
六、函数、包和错误处理 6.1 函数概念 不用函数的弊端 1)写法可以完成功能, 但是代码冗余 2 ) 同时不利于代码维护 概念:为完成某一功能的程序指令(语句)的集合,称为函数。 在Go中,函数分为: 自定义函数、系统函数 基本语法 //函数的基本语法 fu…...

Photoshop的功能
Photoshop是一款功能强大的图片编辑软件,它提供了数百种不同的工具和特效,让您可以编辑图片、创建图形和设计网页等。 以下是Photoshop的一些主要功能: 1.图层:Photoshop允许您创建多个图层,让您可以在每一个图层上进…...

C++初阶——内存管理
目录 1. C/C内存分布 2. C语言中动态内存管理方式:malloc/calloc/realloc/free 3. C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 4. operator new与operator delete函数 重要 4.1 operator new与operator delete函数(…...

uds服务汇总
还有一些服务列举在下面: RequestDownload(服务ID为0x34)和RequestUpload(服务ID为0x35):这两个服务用于在ECU和诊断器之间进行数据传输。通过 RequestDownload服务,诊断器可以请求ECU接收一些数…...

【深度学习】2023李宏毅homework1作业一代码详解
研一刚入门深度学习的小白一枚,想记录自己学习代码的经过,理解每行代码的意思,这样整理方便日后复习也方便理清自己的思路。感觉每天时间都不够用了!!加油啦。 第一部分:导入模块 导入各个模块࿰…...

【软件测试】基础知识第二篇
文章目录一. 开发模型1. 瀑布模型2. 螺旋模型3. 增量和迭代模型3.1 增量模型3.2 迭代模型3.3 增量和迭代模型的区别4. 敏捷模型4.1 敏捷宣言4.2 scrum模型二. 开发模型V 模型W 模型一. 开发模型 1. 瀑布模型 瀑布模型在软件工程中占有重要地位,是所有其他模型的基…...

Java中File类以及初步认识流
1、File类操作文件或目录属性 (1)在Java程序中通过使用java.io包提供的一些接口和类,对计算机中的文件进行基本的操作,包括对文件和目录属性的操作、对文件读写的操作; (2)File对象既可以表示…...

【C语言】文件操作详细讲解
本章要分享的内容是C语言中文件操作的内容,为了方便大家学习,目录如下 目录 1.为什么要使用文件 2.什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3.文件的打开和关闭 3.1文件指针 3.2打开和关闭 4.文件的顺序读写 4.1顺序读写函数介绍…...

爱奇艺万能联播使用教程
众所周知,爱奇艺是百度旗下的一款产品,所以今天用爱奇艺万能联播的方法实现下载百度网盘,并没有破解百度网盘,是官方正版下载渠道。软件是官方版本,大家双击安装即可。 安装完成以后,在软件中就有了“访问网…...

真题讲解-软件设计(三十七)
数据流图DFD(真题讲解)-软件设计(三十六)https://blog.csdn.net/ke1ying/article/details/129803164 在网络安全管理中,加强内防内控可采取的策略是? 终端访问权限,防止合法终端越权访问。加强…...

Android 上的协程(第一部分):了解背景
本系列文章 Android 上的协程(第一部分):了解背景 Android 上的协程(第二部分):入门 Android上的协程 (第三部分): 实际应用 Android 上的协程(第一部分):了解背景 这篇…...

【H3C】VRRP2 及Vrrp3基本原理 华为同用
文章目录VRRP2基本概念报文格式主备选举规则(优先级)0和255双Master原因VRRP认证VRRP状态机抢占模式VRRP主备切换状态项目场景VRRP3H3C参考致谢VRRP2 基本概念 VRRP路由器(VRRP Router):运行VRRP的设备,它…...

【数据库】SQL语法
目录 1. 常用数据类型 2. 约束 3. 数据库操作 4. 数据表操作 查看表 创建表格 添加数据 删除数据 修改数据 单表查询数据 多表查询数据 模糊查询 关联查询 连接查询 数据查询的执行顺序 4. 内置函数 1. 常用数据类型 整型:int浮点型:flo…...

JavaEE简单示例——文件的上传和下载
文件的上传和下载的实现原理的简单介绍 表单的构成 首先,我们先来介绍我们的需要用到的表单,在这个表单中,首先值得我们注意的就是,在type为file的input标签中.这个控件是我们主要用来选择上传的文件的, 除此之外,我们要想实现文件的上传,还需要将method的属性的值设置为post…...

【C语言督学训练营 第五天】数组字符串相关知识
文章目录前言一、数组的定义1.一维数组①.如何定义②.声明规则③.内存分布④.初始化方法2.二维数组3.高维数组二、访问数组元素相关问题1.访问越界2.数组的传递三、Scanf与字符数组1.字符数组初始化2.scanf读取字符四、字符数组相关函数前言 今天的C语言训练营没有安排高维数组…...

GPT-4 免费体验方法
POE 在Quora上非常受欢迎的手机聊天机器人Poe App已经集成ChatGPT助手!除了最初集成的三个聊天机器人Sage、Claude和Dragonfly外,Poe现在还加入了第四位ChatGPT。由于使用了ChatGPT API,因此Poe拥有真正的ChatGPT。 现在更是第一批集成了GP…...

中断-屏蔽位
1.中断控制器(PIC:适用于单处理器、APIC) 1.定义 中断控制器可以看作是中断服务的代理,外设五花八门,如果没有一个中断的代理,外设想要给cpu发送中断信号来处理中断。那么只能是外设连接在cpu引脚上,由于cpu引脚很宝贵,所以不可能拿出那么多引脚来供外设连接,所以就有…...

【洛谷P1636】 Einstein学画画
题目描述:Einstein 学起了画画。此人比较懒~~,他希望用最少的笔画画出一张画……给定一个无向图,包含 n 个顶点(编号 1∼n),m 条边,求最少用多少笔可以画出图中所有的边。输入格式第一行两个整数…...

户外LED显示屏钢结构制作原则
户外LED显示屏在施工安装时是必须要制作固定钢结构的,因为户外LED显示屏工作环境相对比较恶劣,制作钢结构一是为了安全,二是为了提高防护等级。那么户外LED显示屏钢结构制作原则是什么呢?迈普光彩小编总结了一些分享个大家。 户外…...

【内网穿透】使用Haproxy反向代理搭建企业私有云:神卓互联教程
神卓互联是一款强大的内网穿透工具,可以帮助企业搭建私有云,实现对内部资源的远程访问。在搭建私有云的过程中,使用HAProxy反向代理可以提高系统的性能和可靠性。本文将介绍如何使用神卓互联和HAProxy反向代理搭建私有云。 步骤如下…...

spring boot项目:实现与数据库的连接
步骤【写在前面】定义数据库连接信息:引入数据库驱动:创建数据源:创建JdbcTemplate:编写DAO层:使用Service注解标注Service层:使用RestController注解标注Controller层:示例代码:app…...

【gitlab部署】centos8安装gitlab(搭建属于自己的代码服务器)
这里写目录标题部署篇序言要求检查系统是否安装OpenSSH防火墙问题准备gitlab.rb 配置坑点一忘记root密码重置使用篇gitlab转换成中文git关闭注册入口创建用户部署篇 序言 在团队开发过程中,想要拥有高效的开发效率,选择一个好的代码开发工具是必不可少的…...

2021年全国职业院校技能大赛(中职组)网络安全竞赛第三套试题A模块解析(超级详细)
2021年全国职业院校技能大赛(中职组) 网络安全竞赛试题 (3) (总分100分) 赛题说明 一、竞赛项目简介 “网络安全”竞赛共分A. 基础设施设置与安全加固;B. 网络安全事件响应、数字取证调查和应用安全;C. CTF夺旗-攻击;D. CTF夺旗-防御等四个模块。根据比赛实际情况…...

Hbase异步复制和同步复制解析
背景 Hbase是一个KV数据库,自然和Mysql以及Redis等会涉及到复制的问题,也有主从集群的概念,那么本文就来看下Hbase的复制逻辑 Hbase复制实现 首先我们先在回顾下,在Hbase实现中,每个RegionServer上面会包含多个Regi…...

TIKTOK海外直播公会如何申
在“清朗行动”的规范化整治下,国内秀场直播俨然成为了“夕阳行业”,早已度过了野蛮生长的阶段。随着直播公会内卷竞争加剧,公会的生存也愈发艰难,有的娱乐主播甚至纷纷转行做起了电商,可见国内娱乐直播行业的惨淡。 …...

6.springcloud微服务架构搭建 之 《springboot集成Gateway》
5.springcloud微服务架构搭建 之 《springboot集成Hystrix》 目录 1.gateway介绍 2.项目引入gateway 3.yml配置gateway参数 5.自定义全局Filter 6.测试 1.gateway介绍 服务网关(Spring Cloud Gateway)是Spring Cloud官方推出的 第二代网关框架&#…...

[N1CTF 2018]eating_cms_
目录 信息收集 代码审计 parse_url解析漏洞 信息收集 进入即是登录页面,抓包一看应该是SQL注入,但是空格、%、|等等啥的都被waf了,不太好注入,先信息收集一波 花一分钟扫下目录,发现一个viminfo和register.php Viminfo文件…...

《Spring系列》第13章 Aop切面(二) 代理创建
前言 本篇文章主要介绍AOP的源码,要想看懂AOP,那么就看AOP给容器中注入了什么组件,这个组件什么时候工作,这个组件的功能是什么? EnableAspectJAutoProxy会向IOC容器中注入一个后置处理器,它会在Bean的创…...

算法-贪心
贪心算法1信息学竞赛课堂贪心算法2贪心法实际生活中,经常需要求一些问题的“可行解”和“最优解”,这就是所谓的“最优化”问题。一般来说,每个最优化问题都包含一组“限制条件”和一个“目标函数”,符合限制条件的问题求解方案称…...

【数据结构与算法】树(Tree)【详解】
文章目录前言树一、树的基本概念1、树的定义2、基本术语3、树的性质二、树的存储结构1、双亲表示法2、孩子表示法3、孩子兄弟表示法二叉树一、二叉树的概念1、二叉树的定义2、几个特殊的二叉树3、二叉树的性质4、二叉树的存储结构二、遍历二叉树1、先序遍历2、中序遍历3、后序遍…...