函数参数传递方式
- 基本介绍
函数传递方式有两类:值类型和引用类型。值类型参数默认就是值传递,引用类型参数默认为引用传递。 - 两种传递方式
1)值传递
2)引用传递
实际上不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递是值的拷贝,引用传递的是地址的拷贝。一般情况下,地址拷贝效率高,因为其数据量小,而值拷贝取决于拷贝的数据大小,数据越大,效率越低。 - 值类型和引用类型
1)值类型:基本数据类型(int float系列,bool string complex64/128等),数组和结构体
2)引用类型:指针,slice切片,map,管道chan,interface
变量作用域
- 函数内部声明/定义的变量叫局部变量(不论变量首字母是否大写),作用域仅限于函数内部
- 函数外部声明/定义的变量叫全局变量,作用域整个包都有效,如果变量名首字母大写,则作用域整个程序都有效
- 如果变量一个代码块中,比如for/if中,那么这个变量的作用域就在该代码块中。(与其他语言的一大区别)
全局变量使用细节
4. 当在函数内定义了一个与已存在的全局变量相同名字的变量时,在该函数内,编译器遵循就近原则
package main
import "fmt"
var name = "tom" // 全局变量
func test01() {
fmt.Println(name)
}
func test02() {
name := "jack" //1 编译器采用就近原则
//name = jack // 2
fmt.Println(name)
}
func main() {
fmt.Println(name) //tom 2 tom
test01() //tom 2 tom
test02() //jack 2 jack
test01() //tom 2 jack
//全局变量可以在代码块中直接改变值,但局部变量要在代码块中改变的值影响到自身需要使用引用变量
}
- 全局变量不能使用类型推导形式。例如:Age:=20//实际上包含了申明/定义语句 var Age int 和赋值语句Age = 20 。在函数体外可以声明/定义、初始化变量,但不能进行语句操作(赋值语句)
package main
import "fmt"
var Name = "tom" // 全局变量
Age := 20 //报错
func main() {
fmt.Println(Name)
}
字符串常用的系统函数
- 统计字符串的长度,按字节len(str)
golang的编码统一编码为utf-8,ASCII的字符(字母和数字)占一个字节汉子占3个字节。 - 当含义中文的字符串遍历时,需先对字符串转为rune切片,防止乱码现象。(str1 := []rune(str) )
- 字符串转其他类型,其他类型转字符串。使用strconv包,func Parse…方法:字符串转其他类型;func Format…方法:其他类型转字符串。基本类型转string推荐使用fmt.Sprintf()方法。
package main
import (
"fmt"
"strconv"
// _ "unsafe" //如果我们没有使用一个包,但是又不想去掉,前面加入_表示忽略此包
)
//演示golang中基本数据类型转换成string使用
// 使用到函数:fmt.Sprintf()
func main() {
var num1 int = 99
var num2 float64 = 23.456
var b bool = true
var myChar byte = 'h'
var str string //空的str
//使用第一种方式转换来转换 fmt.Sprintf(format string, a...interface{})
//根据format参数生成格式化的字符串并返回该字符串。
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type %T, str=%v,%s\n", str, str, str)
fmt.Println(str)
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type %T, str=%v,%s\n", str, str, str)
str = fmt.Sprintf("%t", b)
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
str = fmt.Sprintf("%c", myChar)
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
//第二种方式 strconv包中函数
var num3 int = 99
var num4 float64 = 23456.154852
var b2 bool = true
var num5 int64 = 4562
str = strconv.FormatInt(int64(num3), 10) //10:十进制 还可是 2 8 16
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
//strconv.FormatFloat(num4, 'f', 6, 64)
//'f'转换成的格式, 6:精度, 64:来源num4的格式
str = strconv.FormatFloat(num4, 'e', 6, 64) // e:科学计数法
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
str = strconv.FormatBool(b2)
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
//strconv.Itoa():strconv包中的,将int类转换为string类
str = strconv.Itoa(int(num5))
fmt.Printf("str type %T, str=%v,%q\n", str, str, str)
- 字符串转[]byte: var bytes = []byte("hello go),为了将字符串写入二进制文件,使用byte切片[]byte(string)。
- []byte转字符串 str := string([]byte(97,98,99) ,输出str结果为对应的ASCII码值“abc”
- 查找子字符串是否在指定的字符串中,使用strings包中的Contains方法。
func Contains(s, substr string) bool
Example
fmt.Println(strings.Contains(“seafood”, “foo”))
fmt.Println(strings.Contains(“seafood”, “bar”))
fmt.Println(strings.Contains(“seafood”, “”))
fmt.Println(strings.Contains("", “”))
Output:
true
false
true
true
- 查找一个字符串有几个指定的子字符串。strings包中func Count(s, sep string) int,返回字符串有几个不重复的sep字符串。
Example
fmt.Println(strings.Count(“cheese”, “e”))
fmt.Println(strings.Count(“five”, “”)) // before & after each rune
Output:
3
5
- 字符串的比较,若不区分字母大小写则使用==,需要区分大小写,则使用strings包中的func EqualFold(s, t string) bool(判断两个utf-8编码字符串(将unicode大写、小写、标题三种格式字符视为相同)是否相同)
fmt.Println("结果", "abc" == "ABC") //结果 flase
fmt.Println(strings.EqualFold("abc", "ABC")) //true
- 返回子串在字符串第一次出现时的index值,如果没有则返回-1:strings.Index(s,t string)
fmt.Println(strings.Index("NLT_abcabcabc", "abc")) //4
fmt.Println(strings.Index("NLT_abcabcabc", "hi")) //-1
- 返回子串在字符串最后一次出现时的index值,如果没有则返回-1:strings.LastIndex(s,t string)
- 将指定的子串替换成另外一个子串:strings包func Replace(s, old, new string, n int) string(返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串。)
Example
fmt.Println(strings.Replace(“oink oink oink”, “k”, “ky”, 2))
fmt.Println(strings.Replace(“oink oink oink”, “oink”, “moo”, -1))
Output:
oinky oinky oink
moo moo moo
注意:s并不会被影响,替换后是得到一个新的字符串
str2 := "go go hello"
fmt.Println(strings.Replace(str2, "go", "golang", 1)) //不会影响str2的值,替换后得到一个新字符串
fmt.Println(strings.Replace("go go hello", "go", "golang", -1))
fmt.Println(str2) //go go hello
11.按照指定的某个字符,为分隔标识,将一个字符串拆分成字符串数组。strings.Split(s, sep string) []string。
用去掉s中出现的sep的方式进行分割,会分割到结尾,并返回生成的所有片段组成的切片(每一个sep都会进行一次切割,即使两个sep相邻,也会进行两次切割)。如果sep为空字符,Split会将s切分成每一个unicode码值一个字符串。
Example
fmt.Printf("%q\n", strings.Split(“a,b,c”, “,”))
fmt.Printf("%q\n", strings.Split(“a man a plan a canal panama”, “a “))
fmt.Printf(”%q\n”, strings.Split(" xyz “, “”))
fmt.Printf(”%q\n", strings.Split("", “Bernardo O’Higgins”))
Output:
[“a” “b” “c”]
["" “man " “plan " “canal panama”]
[” " “x” “y” “z” " “]
[””]
- 将字符串的字母大小写转换:strings.ToLower(s)/strings.ToUpper(s)
- 将字符串左右两边的空格去掉。使用strings.TrimSpace(s string) string。返回将s前后端所有空白(unicode.IsSpace指定)都去掉的字符串。
Example
fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
Output:
a lone gopher
- 将字符串两边指定的字符去掉。使用strings.Trim(s string, cutset string) string。返回将s前后端所有cutset包含的utf-8码值都去掉的字符串。
Example
fmt.Printf("[%q]", strings.Trim(" !!! Achtung! Achtung! !!! ", "! "))//把左右两边的!和空格去掉
Output:
[“Achtung! Achtung”]
- 将字符串前段指定字符去掉:strings.TrimLeft(s string, cutset string) string;将字符串后端段指定字符去掉:strings.TrimRight(s string, cutset string) string。
- 判断字符串是否以指定的字符串开头:strings.HasPrefix(s, prefix string) bool;判断字符串是否以指定的字符串结尾:strings.HasSuffix(s, suffix string) bool。
时间和日期相关的函数 time包
- time.Time类型用于表示时间
- 获取当前时间的方法:now:=time.Now()
- 获取更详细的日期信息
//日期和时间相关函数和方法的使用
//1.获取当前时间
now := time.Now() // 返回的是time.Time类型
fmt.Printf("now=%v now type=%T\n", now, now)
//time.Time 是一种表示时间的类型
//2.通过now可以获取到年月日,时分秒,使用 func (Time) Year/Month ...等方法
fmt.Printf("年=%v\n", now.Year())
fmt.Printf("月=%v\n", now.Month())
fmt.Printf("日=%v\n", int(now.Day())) // 将November 表示成 11
fmt.Printf("时=%v\n", now.Hour())
fmt.Printf("分=%v\n", now.Minute())
fmt.Printf("秒=%v\n", now.Second())
fmt.Printf("秒=%v\n", time.Now().Second())
- 格式化日期时间:
1)采用格式化输出
2)使用func(Time)Format(“2006/01/02 15:04:05”),需注意Format内的标准时间是固定的,不可改动
//格式化日期时间
//方式一:格式化输出
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("dataStr=%s\n", dateStr)
//方式二采用func (Time) Format方法
fmt.Println(now.Format("2006/01/02 15:04:05")) //判断时间2006-01-02 15:04:05是固定的
fmt.Println(now.Format("2006-01-02 ")) //年月日
fmt.Println(now.Format("15:04:05")) //时分秒
fmt.Println(now.Format("15")) //输出时
fmt.Println(now.Format("04")) //输出分
fmt.Println(now.Format("05")) //输出秒
}
- 时间常量,type Duration int。Duration类型代表两个时间点之间经过的时间,以纳秒为单位。可表示的最长时间段大约为290光年。
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
常用的时间段。没有定义一天或超过一天的单元,以避免夏时制的时区切换的混乱。
常量的作用:在程序中可以用于获取指定时间单位的时间,比如想得到100毫秒 100 * time.Millisecond
- 结合Sleep,使用常量。
//结合Sleep 使用时间常量/Duration
//需求:每隔0.1秒打印一个数字,打印到100退出
for i := 1; i <= 100; i++ {
fmt.Println(i)
time.Sleep(time.Millisecond * 100)
- 获取当前Unix(秒)时间戳和UnixNano(纳秒)时间戳。(作用:用于获取随机数字)
func (Time) Unix
func (t Time) Unix() int64
Unix将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒)。
func (Time) UnixNano
func (t Time) UnixNano() int64
UnixNano将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位纳秒)。如果纳秒为单位的unix时间超出了int64能表示的范围,结果是未定义的。注意这就意味着Time零值调用UnixNano方法的话,结果是未定义的。
内置函数
Golang设计者为编程方便,提供了一些函数,这些函数可以直接使用,称之为内置函数。在buildin包中。
- len:用来求长度,例如string、array、slice、map、channel
- new🆕用来分配内存,主要用来分配值类型,比如int、float系列,struct…返回的指针。
//一般的赋值方法
num1 := 10
fmt.Printf("num1的类型%T,值为%d,地址为%v\n", num1, num1, &num1)
//使用new()
//new:用来分配内存,主要用来分配值类型,比如int、float系列,struct...返回的指针
num2 := new(int) //类型 *int
//num2的值为一个指向新的int类型(值为默认值)的空间的地址
*num2 = 100 //num2指向的值 = 100
fmt.Printf("num2的类型%T,值(指向的空间地址)为%v,地址为%v,指向的值为%v\n", num2, num2, &num2, *num2)
- make:用来分配内存,主要用来分配引用类型,比如channel、map、slice。
func make(Type, size IntegerType) Type
内建函数make分配并初始化一个类型为切片、映射、或通道的对象。其第一个实参为类型,而非值。make的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型:
切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;
它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。
映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个
小的起始大小。
通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。
错误处理机制
案例:
package main
import "fmt"
func test(){
n1 := 10
n2 := 0
res := n1 / n2
fmt.Println("res",res)
}
func main(){
//测试
test()
fmt.Println("main()下面的代码")
}
1)在默认情况下,当发生错误后(panic),程序就会退出(崩溃)
2)如果希望当错误发生后,可以捕获到错误,并进行处理,保证程序可以继续执行。还可以在捕获到错误后,给管理员一个提示(邮件,短信。。。)
错误处理机制基本说明:
1)Go语言为追求简洁,所以GO语言不支持传统的try…catch…finally这种处理
2)Go中引入的处理方式:defer,panic,recover
3)使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理,使得代码更加健壮。
func recover() interface{}
内建函数recover允许程序管理恐慌过程中的Go程。在defer的函数中,执行recover调用会取回传至panic调用的错误值,恢复正常执行,停止恐慌过程。若recover在defer的函数之外被调用,它将不会停止恐慌过程序列。在此情况下,或当该Go程不在恐慌过程中时,或提供给panic的实参为nil时,recover就会返回nil。
func test() {
//使用defer + recover 来捕获和处理异常
defer func() {
if err := recover(); err != nil { // recover()为内置函数,可以捕获到异常
fmt.Println(err)
// 这里可以将错误信息发送给管理员)
// 伪代码
fmt.Println("~发送邮件给admin@128.com")
}
}() //匿名函数的用法,直接应用 func(int int){}(int int)
n1 := 10
n2 := 0
res := n1 / n2
fmt.Println("res", res)
}
func main() {
//测试
test()
for i := 1; i <= 15; i++ {
fmt.Println("main()下面的代码")
time.Sleep(time.Second)
}
}
自定义错误
Go程序中,也支持自定义错误,使用errors.New和panic内置函数。
errors.New(“错误说明”),使用字符串创建一个错误,返回一个error类型,表示一个错误。
panic内置函数,接收一个interface{}类型的值(也就是任何值了)作为参数。可以接收error类型的变量,输出错误信息,并退出程序。
func panic(v interface{})
内建函数panic停止当前Go程的正常执行。当函数F调用panic时,F的正常执行就会立刻停止。F中defer的所有函数先入后出执行后,F返回给其调用者G。G如同F一样行动,层层返回,直到该Go程中所有函数都按相反的顺序停止执行。之后,程序被终止,而错误情况会被报告,包括引发该恐慌的实参值,此终止序列称为恐慌过程。
//函数读取配置文件init.conf的信息
//如果文件名传入不正确就返回一个自定义错误
func readConf(name string) (err error) {
if name == "config.ini" {
//读取
return nil
} else {
return errors.New("读取文件名错误")
}
}
func test01() {
// err := readConf("config1.ini")
if readConf("config1.ini") != nil { // err != nil
//读取文件发生错误,就输出自定义错误,并终止程序
fmt.Println(readConf("config1.ini"))
panic(readConf("config1.ini")) //panic(err)
}
fmt.Println("test()01继续执行")//不执行
}
func main() {
//测试
test01()
fmt.Println("main()下面的代码")//不执行
}