目录
6.4 defer
1 变量
变量是计算机语言中储存数据的基本单元,变量指定了某存储单元(Memory Location)的名称,该存储单元会存储特定类型的值。
变量又分为局部变量和全局变量。
- 局部变量,是定义在大括号({})内部的变量,大括号的内部也是局部变量的作用域。
- 全局变量,是定义在函数和大括号({})外部的变量。
1.1 声明变量
变量声明以关键字var
开头,变量类型放在变量的后面,行尾无需分号。
var name type
var 变量名 变量类型
声明多个变量:
var (
a string
b int
c bool
)
var (
name = "golang"
age = 20
height int
)
var width, height int = 100, 50
1.2 初始化
如果变量有初始值,那么 Go 能够自动推断具有初始值的变量的类型。因此,如果变量有初始值,就可以在变量声明中省略 type
。
var a int = 10 //标准
var b = 10 //省略 type,自动推断类型
name, age := "golang", 29 //简短声明, := 操作符的左边至少有一个变量是尚未声明的
1.3 匿名变量
在使用多重赋值时,尤其是当Go语言的函数返回多个值,而想要忽略某个值时,那么就可以使用匿名变量,即下画线“_”替换即可,也称作空白符。
x, _ := foo()
_, y := foo()
匿名变量既不占用命名空间,也不会分配内存。
注意:
GOlang的全局变量只声明不使用会报错。
处理方法:关闭golang 的 variable declared but not used
2 数据类型
在Go语言中,有以下几种数据类型。
基本数据类型(原生数据类型):整型、浮点型、复数型、布尔型、字符串、字符(byte、rune)。
复合数据类型(派生数据类型):数组(array)、切片(slice)、映射(map)、函数(function)、结构体(struct)、通道(channel)、接口(interface)、指针(pointer)。
2.1 常用类型
1)整型
2)浮点型
3)字符
byte 是 uint8 的别名。
rune 是 int32 的别名。
4)字符串
字符串是字节的集合。双引号书写字符串被称为字符串字面量(string literal),这种字面量不能跨行。Go 中的字符串是兼容 Unicode 编码的,并且使用 UTF-8 进行编码。
s1 := "hello"
多行字符串需要使用反引号“`”,多用于内嵌源码和内嵌数据。
s1 := `第一行
第二行
第三行
`
转义字符
常用方法
5)复数类型
complex64:实部和虚部都是 float32 类型的的复数。
complex128:实部和虚部都是 float64 类型的的复数。
内建函数complex用于创建一个包含实部和虚部的复数。complex 函数的定义如下:
func complex(r, i FloatType) ComplexType
也可以使用简短语法:
c := 6 + 7i
2.2 数据类型转换
Go 有着非常严格的强类型特征。Go 没有自动类型转换。
必须采用数据类型前置加括号的方式进行强制类型转换。
3 常量
常量中的数据类型只可以是布尔型、数字型(整型、浮点型和复数型)和字符串。常量的定义格式如下:
const 标识符 [类型] = 值
const a int = 10
a = 20 // 不允许对常量重新赋值
const b = math.Sqrt(4) // 常量的值会在编译的时候确定,所以不允许将函数的返回值赋值给常量
Go语言现阶段没有提供枚举,可以使用常量组模拟枚举。
import "fmt"
const(
status = 0
female = 2
)
fun main(){
fmt.println(status,female)
}
iota
iota,特殊常量值,是一个系统定义的可以被编译器修改的常量值。
iota只能被用在常量的赋值中,在每一个const关键字出现时,被重置为0,然后每出现一个常量,iota所代表的数值会自动增加1。iota可以理解成常量组中常量的计数器。
const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
4 fmt
4.1 输出
fmt包实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。
1) Print
Print系列函数会将内容输出到系统的标准输出,区别在于Print函数直接输出内容,Printf函数支持格式化输出字符串,Println函数会在输出内容的结尾添加一个换行符。
func main() {
fmt.Print("在终端打印该信息。")
name := "枯藤"
fmt.Printf("我是:%s\n", name)
fmt.Println("在终端打印单独一行显示")
}
2) Fprint
Fprint系列函数会将内容输出到一个io.Writer接口类型的变量w中,我们通常用这个函数往文件中写入内容。
// 向标准输出写入内容
fmt.Fprintln(os.Stdout, "向标准输出写入内容")
fileObj, err := os.OpenFile("./xx.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("打开文件出错,err:", err)
return
}
name := "枯藤"
// 向打开的文件句柄中写入内容
fmt.Fprintf(fileObj, "往文件中写如信息:%s", name)
3)Sprint
Sprint系列函数会把传入的数据生成并返回一个字符串。
s1 := fmt.Sprint("枯藤")
s2 := fmt.Sprintf("name:%s,age:%d", name, age)
s3 := fmt.Sprintln("枯藤")
4) Errorf函数根据format参数生成格式化字符串并返回一个包含该字符串的错误。
err := fmt.Errorf("这是一个错误")
4.2 打印格式
通用打印格式
BOOL打印格式
整型打印格式
浮点型打印格式
字符串和字符数组打印格式
指针
4.3 输入
1) fmt
func Scan(a ...interface{}) (n int, err error)
func Scanf(format string, a ...interface{}) (n int, err error)
func Scanln(a ...interface{}) (n int, err error)
2) bufio.NewReader
func bufioDemo() {
reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
fmt.Print("请输入内容:")
text, _ := reader.ReadString('\n') // 读到换行
text = strings.TrimSpace(text)
fmt.Printf("%#v\n", text)
}
5 流程控制
5.1 IF
if 是条件语句,如果 condition
为真,则执行 {
和 }
之间的代码。
if condition {
//...
}
if condition {
//...
} else if condition {
//...
} else {
//...
}
if
还有另外一种形式,它包含一个 statement
可选语句部分,该组件在条件判断之前运行。
if statement; condition {
//...
}
if num := 10; num % 2 == 0 { //checks if number is even
fmt.Println(num,"is even")
} else {
fmt.Println(num,"is odd")
}
注意:else
语句应该在 if
语句的大括号 }
之后的同一行中。如果不是,编译器会不通过。
5.2 Switch
switch 是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行相应的代码块。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
定义多个表达式:
letter := "i"
switch letter {
case "a", "e", "i", "o", "u": // 一个选项多个表达式
fmt.Println("vowel")
default:
fmt.Println("not a vowel")
}
switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
switch x.(type){
case type:
statement(s)
case type:
statement(s)
default: /* 可选 */
statement(s)
}
Fallthrough 语句:
在 Go 中,每执行完一个 case 后,会从 switch 语句中跳出来,不再做后续 case 的判断和执行。使用 fallthrough
语句可以在已经执行完成的 case 之后,把控制权转移到下一个 case 的执行代码中。
func main() {
num := 75
//表达式是可选的,可以被省略
switch {
case num < 50:
fmt.Printf("%d is lesser than 50\n", num)
fallthrough
case num < 100:
fmt.Printf("%d is lesser than 100\n", num)
fallthrough
case num < 200:
fmt.Printf("%d is lesser than 200", num)
}
}
5.3 FOR
for
是 Go 语言唯一的循环语句。Go 语言中并没有其他语言比如 C 语言中的 while
和 do while
循环。
形式1 :for init; condition; post { }
形式2 :for condition { }
形式3 :for { }
init:初始化部门;
condition: 条件;
post: 控制变量增量或减量。
案例:
for i := 1; i <= 10; i++ {
fmt.Printf(" %d",i)
}
break 语句用于在完成正常执行之前突然终止 for 循环
for i := 1; i <= 10; i++ {
if i > 5 {
break //loop is terminated if i > 5
}
fmt.Printf("%d ", i)
}
continue 语句用来跳出 for 循环中当前循环
for i := 1; i <= 10; i++ {
if i%2 == 0 {
continue
}
fmt.Printf("%d ", i)
}
6 函数
6.1 普通函数
函数声明通用语法如下:
func functionname(parametername type) returntype {
// 函数体(具体实现的功能)
}
func functionname() {
// 注: 表示这个函数不需要输入参数,且没有返回值
}
函数使用示例:
func calculatePrice(price int, num int) int {
var totalPrice = price * num // 商品总价 = 商品单价 * 数量
return totalPrice // 返回总价
}
//如果有连续若干个参数,它们的类型一致,那么我们无须一一罗列,只需在最后一个参数后添加该类型。
func calculatePrice2(price , num int) int {
var totalPrice = price * num // 商品总价 = 商品单价 * 数量
return totalPrice // 返回总价
}
func main() {
price, num := 90, 6 // 定义 price 和 num,默认类型为 int
totalPrice := calculatePrice(price, num)
fmt.Println("Total price is", totalPrice) // 打印到控制台上
}
多返回值:
Go 语言支持一个函数可以有多个返回值。我们来写个以矩形的长和宽为输入参数,计算并返回矩形面积和周长的函数 rectProps。
func rectProps(length, width float64)(float64, float64) {
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter //同时返回两个值,面积和周长
}
func main() {
area, perimeter := rectProps(10.8, 5.6)
fmt.Printf("Area %f Perimeter %f", area, perimeter)
}
命名返回值:
//从函数中可以返回一个命名值,函数中的 return 语句没有显式返回任何值。由于 area 和 perimeter 在函数声明中指定为返回值, 因此当遇到 return 语句时, 它们将自动从函数返回。
func rectProps(length, width float64)(area, perimeter float64) {
area = length * width
perimeter = (length + width) * 2
return
6.2 匿名函数
匿名函数没有函数名,只有函数体,函数可以作为一种类型被赋值给变量,匿名函数也往往以变量方式被传递。
1)定义匿名函数的时候就可以直接使用(仅使用一次)
func main() {
res := func(length, width float64)float64{
return length*width
}(10.6,20.8)
}
2)可以将匿名函数赋值给变量,再通过该变量来调用匿名函数
func main() {
res := func(length, width float64)float64{
return length*width
}
res1 := res(2.4,6.2)
fmt.Println(res1)
}
3)匿名函数作为返回值
func caculate() func(float64,float64){
area := func(length,width float64 ){
println(length*width)
}
return area
}
6.3 可变参数
如果函数最后一个参数被记作 ...T
,这时函数可以接受任意个 T
类型参数作为最后一个参数。
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)
}
6.4 defer
含有 defer 语句的函数,会在该函数将要返回之前,调用另一个函数。
func finished() {
fmt.Println("Finished finding largest")
}
func largest(nums []int) {
defer finished()
fmt.Println("Started finding largest")
max := nums[0]
for _, v := range nums {
if v > max {
max = v
}
}
fmt.Println("Largest number in", nums, "is", max)
}
func main() {
nums := []int{78, 109, 2, 563, 300}
largest(nums)
}
在 largest() 函数将要返回之前,会调用 finished() 函数。
defer也可以用来延迟方法:
func printA(a int) {
fmt.Println("value of a in deferred function", a)
}
func main() {
a := 5
defer printA(a)
a = 10
fmt.Println("value of a before deferred function call", a)
}
当一个函数内多次调用 defer 时,Go 会把 defer 调用放入到一个栈中,随后按照后进先出(Last In First Out, LIFO)的顺序执行。
7 MAP
Go 语言提供了内置类型 map,它将值(value)与键(key)关联起来,可以使用相应的键检索值。
1)创建MAP
var 变量名 map[KeyType]ValueType //使用var关键字定义map
变量名 := make(map[string]int) //通过向
make
函数传入键和值的类型
var person map[string]int
if person == nil {
fmt.Println("map is nil. Going to make one.")
person = make(map[string]int)
}
2)添加元素
var country = map[string]string{
"China": "Beijing",
"Italy": "Rome",
}
country["Japan"] = "XRIBen"
3)检索map值
value, ok := map[key]
func main() {
var country = map[string]string{
"China": "Beijing",
"Italy": "Rome",
}
country["Japan"] = "XRIBen"
newCon := "Italy"
value, ok := country[newCon]
if ok == true {
fmt.Println("Capatoal of", newCon, "is", value)
} else {
fmt.Println(newCon,"not found")
}
}
for range循环遍历
(1)、key 、value都遍历
for k, v := range countryMap {
fmt.Println("国家", k, "首都", v)
}
(2)、只展示value
for _, v := range countryMap {
fmt.Println("国家", "首都", v)
}
(3)、只展示key
for k := range countryMap {
fmt.Println("国家", k , "首都", countryMap[k])
}
4)删除元素
删除 map 中 key 的语法是 delete(map, key)。这个函数没有返回值。
delete(country, "Japan")
5)其他
len(personSalary) 函数获取map 的长度。
map 是引用类型。当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构。因此,改变其中一个变量,就会影响到另一变量。
map 之间不能使用 == 操作符判断,== 只能用来检查 map 是否为 nil。
8 数组和切片
8.1 数组
数组是同一类型元素的集合。一个数组的表示形式为 [n]T。n 表示数组中元素的数量,T 代表每个元素的类型。元素的数量 n 也是该类型的一部分。
1)声明数组
var a [3]int //int array with length 3
a[0] = 12 // array index starts at 0
a := [3]int{12, 78, 50} // short hand declaration to create array
a := [...]int{12, 78, 50} // ... makes the compiler determine the length
Go 中的数组是值类型而不是引用类型。这意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,则不会影响原始数组。
2)遍历数组
a := [...]float64{67.7, 89.8, 21, 78}
for i := 0; i < len(a); i++ {
fmt.Printf("%d is %.2f\n", i, a[i])
}
for i, v := range a {
fmt.Printf("%d is %.2f\n", i, v)
}
3)多维数组
func printarray(a [3][2]string) {
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s ", v2)
}
fmt.Printf("\n")
}
}
func main() {
a := [3][2]string{
{"lion", "tiger"},
{"cat", "dog"},
{"pigeon", "peacock"},
}
printarray(a)
}
8.2 切片
1)声明
切片由 []T 表示,没有元素数量。
a := [5]int{76, 77, 78, 79, 80}
var b []int = a[1:4] // creates a slice from a[1] to a[3]
var b []int = arr[start:end]
var b []int = arr[:end]
var b []int = arr[start:]
var b []int = arr[:]
var b = arr[:len(arr)-1] //去掉切片的最后一个元素
c := []int{6, 7, 8} // creates and array and returns a slice reference
2)修改切片
切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。
func main() {
darr := [...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
dslice := darr[2:5]
fmt.Println("array before", darr)
for i := range dslice {
dslice[i]++
}
fmt.Println("array after", darr)
}
3)make创建切片
func make([]T,len,cap)[]T 函数通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片。
i := make([]int, 5, 5)
4)其他
切片可以通过len()方法获取长度,可以通过cap()方法获取容量。
函数append()用于往切片中追加新元素,可以向切片里面追加一个或者多个元素,也可以追加一个切片。
函数copy()会复制切片元素,将源切片中的元素复制到目标切片中,返回复制的元素的个数。