Golang中内置了一些函数,在使用这些函数时,不必以包名为前缀来调用,而是直接写函数名即可调用,这些函数都是一些基础的函数,在程序设计中应用比较普遍,所以一定要牢记这些最基本的函数用法。下边来介绍一下Golang内置函数。
append
append作用是在切片变量后边追加新的数据,然后返回新的切片变量。函数声明是:
func append(slice []Type, elems ...Type) []Type
append是一个可变参数函数,第一个参数是切片类型,后边的参数是可变参数,可变参数的类型必须与切片中数据类型一致。应用示例:
package main
import (
"fmt"
)
func main() {
var arr1 []string
arr2 := append(arr1, "a")
fmt.Println(arr2)
arr3 := append(arr1, arr2...)
fmt.Println(arr3)
}
copy
func copy(dst, src []Type) int
copy的作用是将一个切片内容拷贝到另一个切片,被拷贝的切片称为源切片,接收内容的切片称为目标切片,源切片与目标切片数据类型一致。copy函数第一个参数是目标切片,第二个参数是源切片。copy在进行切片内容拷贝时,并不会为目标切片扩展长度,所以,想要使用copy来复制切片内容,最好是给目标切片设置足够的长度来装载源切片中的内容。示例如下:
package main
import (
"fmt"
)
func main() {
var arr1 []string = []string{"a", "b", "c", "d"}
var arr2 []string
copy(arr2, arr1)
fmt.Println(arr2)
arr2 = make([]string, 4)
copy(arr2, arr1)
fmt.Println(arr2)
}
输出信息是:
[]
[a b c d]
delete
func delete(m map[Type]Type1, key Type)
delete只能用来删除字典中的内容。delete函数接收两个参数,第一个参数是字典变量,第二个是需要删除内容的key值。delete是通过key来删除map中的内容。请看下边示例代码:
package main
import (
"fmt"
)
func main() {
var mp = map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}
fmt.Println("字典内容是:", mp)
//删除字典中key为a的内容
delete(mp, "a")
fmt.Println("删除操作后,字典内容是:", mp)
}
输出信息是:
字典内容是: map[a:1 b:2 c:3 d:4]
删除操作后,字典内容是: map[b:2 c:3 d:4]
len
func len(v Type) int
len用来获取字符串,切片,数组,通道,字典类型变量的内容长度,不同的数据类型,长度计算规则不一样。如对于切片,字典,数组,通道类型的变量,它们中每一个元素就是一个长度。对于string类型变量,它们每一个字节是一个长度。对于rune类型切片变量,它们每一个字符是一个长度,rune类型变量中的内容采用utf-8编码,一个字符可能对应4个字节。下边来看一段示例:
package main
import (
"fmt"
)
func main() {
// 数组
var arr [4]string = [4]string{"a", "b", "c", "d"}
fmt.Println("数组长度是:", len(arr))
// 切片
var slice []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("slice长度是:", len(slice))
// 通道
var ch = make(chan int, 10)
ch <- 1
ch <- 2
ch <- 3
ch <- 4
fmt.Println("chan长度是:", len(ch))
// 字典
var mp map[string]bool = map[string]bool{
"0": false,
"1": true,
}
fmt.Println("map长度是:", len(mp))
// 字符串
var str string = "道德经"
fmt.Println("string类型长度是:", len(str))
fmt.Println("rune类型长度是:", len([]rune(str)))
}
输出信息是:
数组长度是: 4
slice长度是: 10
chan长度是: 4
map长度是: 2
string类型长度是: 9
rune类型长度是: 3
len计算的不是这个变量的容量,而是计算这个变量已有数据的内容长度。上边的chan类型变量分配了10个buffer,但是len计算的长度只有4,刚好等于通道中现有数据元素个数。计算变量容量的函数是cap,将在后边进行讲解。
cap
cap用来计算切片,通道,数组类型变量的容量,也就是这个变量最多能装多少个元素。示例代码如下:
package main
import (
"fmt"
)
func main() {
// 数组
var arr [4]string = [4]string{"a", "b", "c", "d"}
fmt.Println("数组长度是:", cap(arr))
// 切片
var slice []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("slice长度是:", cap(slice))
// 通道
var ch = make(chan int, 10)
ch <- 1
ch <- 2
ch <- 3
ch <- 4
fmt.Println("chan长度是:", cap(ch))
}
输出信息是:
数组长度是: 4
slice长度是: 10
chan长度是: 10
cap只能用于计算数组,切片,通道类型的变量。
new
func new(Type) *Type
new函数用来创建某一个类型的指针型对象。理论上,一个数据类型,只要能够被访问,就可以使用new函数来创建这个类型的指针型对象。下边来看一段示例,使用new函数创建基本类型的指针型对象。
package main
import (
"fmt"
)
type demo struct{}
func main() {
var str = new(string)
fmt.Println("string pointer", str)
var num = new(int)
fmt.Println("int pointer", num)
var de = new(demo)
fmt.Println("struct pointer", de)
}
在调用new函数时,将类型名作为参数即可创建出这个类型的指针型对象。
在golang中存在几种特殊的情况。如使用new创建chan类型指针型对象,但是仍然需要调用make来给chan类型变量分配容量。
package main
import (
"fmt"
"time"
)
func main() {
// 创建通道类型指针型对象
var ch = new(chan int)
// 给对象分配容量,容量为1
*ch = make(chan int)
// 开启协程,从通道中读取数据
go func() {
fmt.Println(<-(*ch))
}()
// 向通道中写入数据
*ch <- 8
time.Sleep(time.Second * 1)
}
如果使用new创建通道类型变量后,不使用make来分配容量,当向通道中写入信息或从通道中读取信息时,将会出现下边错误信息:
... [chan send (nil chan)]:
... [chan receive (nil chan)]
虽然new关键字可以创建任何类型的指针型对象,但是由于某些特殊类型的对象被创建后,需要进行更多的初始化工作,所以golang中引入了make函数,通过make来创建这些特殊类型的对象。这些特殊类型就是:切片类型,通道类型,字典类型。
make
func make(t Type, size ...IntegerType) Type
make用于给切片,通道,字典类型变量分配容量空间。给不同的类型变量分配空间时,make参数不一样。下边来介绍一下不同类型使用make时的不同用法:
使用make给切片类型变量分配容量空间
var arr = make([]string,4)
给字符串类型切片分配4个容量空间。
var arr = make([]string,5,10)
给字符串类型切片分配10个容量空间,且设置切片长度为5,也就是设置切片前5个元素已经被使用,如果使用append向切片中追加元素,新追加的元素将会从第6个位置算起(下标为5)。
使用make给通道类型变量分配容量空间
var ch = make(chan int)
给切片类型变量分配一个容量的空间,创建了一个不带缓冲的通道。
var ch = make(chan int, 10)
给切片类型变量分配10个容量的空间,创建了一个带10个缓冲的通道。
使用make给字典类型变量分配容量空间
var mp = make(map[string]int)
给字典类型变量分配空间,不需要指定容量,如果在第二个参数中指定容量,这个容量值也会被忽略。如下边写法:
// 容量10被忽略
var mp = make(map[string]int,10)
虽然在给map类型变量分配容量时,指定了长度10,但由于map类型变量能够装载的数据量与系统内存有关,所以给map类型变量设置容量的做法将会被忽略。
请牢记一点:make函数只能初始化字典(map),切片(slice),通道(chan)类型。
complex
func complex(r, i FloatType) ComplexType
complex函数用来创建复数对象。这个函数需要两个参数,第一个参数是复数的实部,第二个参数是复数的虚部。复数的表型形式是:
1+2i
使用complex函数创建复数的示例如下:
package main
import (
"fmt"
)
type demo struct{}
func main() {
var de = complex(1, 2)
fmt.Println(de)
}
输出信息是:
(1+2i)
real
func real(c ComplexType) FloatType
用来获取复数的实部值
imag
func imag(c ComplexType) FloatType
用来获取复数的虚部值
close
func close(c chan<- Type)
内置的close函数,只能用于chan类型变量。使用close函数关闭通道后,这个通道不允许被写入新的信息,但是关闭操作不会清除通道中已有的内容,不影响通道被读取。示例代码如下:
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i * 10
time.Sleep(time.Second * 1)
}
close(ch)
}
func read(ch chan int) {
for {
if val, ok := <-ch; ok {
fmt.Println("从通道中读取值:", val)
} else {
// 通道被关闭
fmt.Println("通道已关闭,退出读取程序")
break
}
}
}
func main() {
var ch = make(chan int, 10)
go write(ch)
read(ch)
}
上边的通道读取操作是:
val,ok := <-ch
当通道被关闭后,如果从通道中读取到信息,则ok值为true,val是一个有效值;如果从通道中没有读取到信息,则ok值为false,此时的val是脏数据,切勿将ok为false时的val值拿去使用,此时的val值是chan指定数据类型的默认值。
如果通道没有被关闭,当从通道中没有读取到信息时,读取操作将会产生程序阻塞
。
所以使用close函数的目的是关闭不会再写入数据的通道,告诉通道读取方,所有数据发送完毕。
panic
func panic(v interface{})
用来抛出异常,使用panic抛出异常后,函数执行将从调用panic的地方停止,如果函数内有defer调用,则执行defer后边的函数调用,如果defer调用的函数中没有捕获异常信息,这个异常会沿着函数调用栈往上传递,直到main函数仍然没有捕获异常,将会导致程序异常退出。示例代码:
package main
func demo() {
panic("抛出异常")
}
func main() {
demo()
}
输出信息是:
panic: 抛出异常
goroutine 1 [running]:
main.demo()
...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:4 +0x40
main.main()
...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:7 +0x27
请谨慎使用panic函数抛出异常,如果没有捕获异常,将会导致程序异常退出。
recover
func recover() interface{}
recover用来捕获panic函数抛出的异常信息。示例代码如下:
package main
import "fmt"
func demo() {
panic("抛出异常")
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
demo()
}
输出信息是:
抛出异常
当没有异常信息抛出时,recover函数返回值是nil。recover只有在defer调用的函数内部时,才能阻止panic抛出的异常信息继续向上传递,如果不是在defer调用的函数内部,将会是什么样子呢?
package main
func main() {
defer recover()
panic("hello world")
}
输出信息是:
panic: hello world
goroutine 1 [running]:
main.main()
...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:5 +0x66
所以必须将recover函数调用放在defer调用的函数内部,如:
package main
func main() {
defer func() {
recover()
}()
panic("hello world")
}
这样才能阻止panic抛出的异常信息继续向上传递。
打印信息到标准输出,结尾没有默认换行符。使用方法是:
package main
func main() {
print("hello world")
}
println
打印信息到标准输出,结尾自动换行。使用方法是:
package main
func main() {
println("hello world")
}
error
error是golang语言中用来表示错误信息的接口,error接口定义是:
type error interface {
Error() string
}
golang标准库中errors包内的errorString结构体实现了error接口,实现代码是:
// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
error接口并不是系统异常接口,不会导致程序进入异常状态,error接口与其他普通接口实质上一样,没有什么系统级的特殊功能,error接口只是定义了一种错误信息处理的规范,规范就是:在程序设计中,把代表错误的信息使用error接口表示。在golang标准库中,大部分错误信息都使用error接口表示。所以error接口是程序中约定俗成的代表错误的符号。
在程序设计中,我们也可以自定义错误信息规范,
如你的代码中使用hello来表示错误信息
type hello interface{
SayError()
}
如果你的代码给别的组织使用,则你必须告诉对方,你的错误信息是用hello接口来表示的,而对方的代码又是以error接口来表示错误信息,对方在使用你的代码时,还需要做额外的思想准备,甚至做接口转换。所以虽然golang可以使用自定义接口来表示错误信息,但是使用自定义错误信息接口会增加不同组织之间的沟通成本。error作为标准库提供的错误信息接口,有利于形成规范,提高代码可读性和可重用性。