1. 变量
变量的声明
Go语言中的变量需要声明后才能使用,并且变量声明后必须使用。变量声明格式为:
var 变量名称 变量类型
var a int
var b string
var d bool
变量的初始化
Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0.字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。
声明变量的时候为其指定初始值为初始化,声明标准格式如下:
var 变量名 类型 = 表达式
var a = "initial"
var b,c int = 1 , 2
var d = ture // 若省略变量类型,编译器会根据等号右边自动进行推导完成初始化
在函数内部,可以使用更简略的:=方式声明并初始化变量。
package main
import (
"fmt"
)
var m = 100 //全局变量m
func main() {
n := 10
m := 200 // 此处声明局部变量m
fmt.Println(m, n)
}
2. 常量
常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值,例:
const pi = 3.1415
const e = 2.7182
3.数组
数组定义
1.数组是同一种数据类型的固定长度的序列。 数组的定义:
var 数组名称 [数组长度(必须为常量)] 变量类型
2.长度是数组类型的一部分,var a[5] int和var a[10]int是不同的类型。
3.访问越界:如果下标在数组合法范围之外,则触发访问越界,会 panic
数组的初始化
package main
import (
"fmt"
)
//全局
var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
//如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
//如果设置了数组的长度,我们还可以通过指定下标来初始化元素
var str = [5]string{3: "hello world", 4: "tom"}
func main() {
//局部
a := [3]int{1, 2} // 未初始化元素值为 0。
b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
c := [5]int{2: 100, 4: 200} // 使用引号初始化元素。
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素类型。
{"user2", 20}, // 别忘了最后一行的逗号。
}
fmt.Println(arr0, arr1, arr2, str)
fmt.Println(a, b, c, d)
}
输出结果:
[1 2 3 0 0] [1 2 3 4 5] [1 2 3 4 5 6] [ hello world tom]
[1 2 0] [1 2 3 4] [0 0 100 0 200] [{user1 10} {user2 20}]
4. 条件语句
if else
Go语言中 if 的语法与 c 和 c++ 类似,不同点在于:
1.Go语言可省略条件表达式括号;
2.代码块左括号必须在条件表达式尾部。
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
switch
Go语言的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项。语法和 c 和 c++ 类似,不同点在于: c 和 c++中的switch 如果不加 break 会一直向下执行完所有case;而Go语言可省略 break,找到匹配项后默认自动终止。
Go语言里面的 switch 可以用来取代任意的if else语句。可以在switch后面不加任何的变量,然后在case里面写条件分支。这样代码相比你用多个if else代码逻辑会更为清晰。
package main
import (
"fmt"
"time"
)
func main(){
t:=time.Now()
switch { //不加任意变量,在case里面直接写条件分支
case t.Hour()<12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
5.循环语句
for 循环
go语言中的 for 语法和c和c++类似,在此省略。
在循环里面,可以用break或者continue来跳出或者继续循环。
6.切片(Slice)
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片的定义
可以声明一个未指定大小的数组来定义切片,切片不需要说明长度。 创建切片的方式:
package main
import "fmt"
func main() {
//1.声明切片
var s1 []int
// 2.:=
s2 := []int{}
/*
3.使用 make() 函数来创建切片(len 是数组的长度并且也是切片的初始长度)
格式为:
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
*/
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4.初始化赋值
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
}
切片的初始化
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] //可以简写为 var slice []int = arr[:end]
var slice2 []int = arr[5:10] //可以简写为 var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素
func main() {
fmt.Printf("arr %v\n", arr)
fmt.Printf("slice0 %v\n", slice0)
fmt.Printf("slice1 %v\n", slice1)
fmt.Printf("slice2 %v\n", slice2)
fmt.Printf("slice3 %v\n", slice3)
fmt.Printf("slice4 %v\n", slice4)
}
输出结果:
arr [0 1 2 3 4 5 6 7 8 9]
slice0 [2 3 4 5 6 7]
slice1 [0 1 2 3 4 5]
slice2 [5 6 7 8 9]
slice3 [0 1 2 3 4 5 6 7 8 9]
slice4 [0 1 2 3 4 5 6 7 8]
用append内置函数操作切片
package main
import (
"fmt"
)
func main() {
var a = []int{1, 2, 3}
fmt.Printf("slice a : %v\n", a) //slice a : [1 2 3]
var b = []int{4, 5, 6}
fmt.Printf("slice b : %v\n", b) //slice b : [4 5 6]
c := append(a, b...)
fmt.Printf("slice c : %v\n", c) //slice c : [1 2 3 4 5 6]
d := append(c, 7)
fmt.Printf("slice d : %v\n", d) //slice d : [1 2 3 4 5 6 7]
}
7.map
Map 是一种无序的键值对的集合,可以通过 key 来快速检索数据
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
//KeyType:表示键的类型 ValueType:表示键对应的值的类型 cap表示map的容量(非必须)
make(map[KeyType]ValueType, [cap])
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
判断某个键是否存在的写法:
value, ok := map[key]
使用delete()函数删除键值对的写法:
delete(map, key)
package main
import "fmt"
func main(){
m make(map:=[string]int)
m:=["one"]=1
m:=["two"]=2
fmt.Println(m) //map[one:1 two:2]
fmt.Println(len(m)) //2
fmt.Println(m:=["one"]) //1
fmt.Println(m:=["unknow"]) //0
r,ok :=["unknow"] // 如果键不存在,ok 的值为 false,r 的值为该类型的零值
fmt.Println(r,ok) //0 false
delete(m,"one")
m2 :=map[string]int{"one":1,"two":2}
var m3 =map[string]int{"one":1,"two":2}
fmt.Println(m2,m3)
Go语言的map是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。
8.range
range类似迭代器操作,返回 (索引, 值) 或 (键, 值),可忽略不想要的返回值,用 "_" 。
range 格式可以对 slice、map、数组、字符串等进行迭代循环,格式如下:
for key, value := range oldMap {
newMap[key] = value
}
//只读key
for key := range oldMap
//只读value
for _, value := range oldMap
package main
import "fmt"
func main(){
nums:=[]int{2,3,4}
sum :=0
for i,num :=range nums{
sum += num
if num ==2 {
fmt.Println("index:",i,"num:",num) //index:0 num:2
}
}
fmt.Println(sum) //9
m :=map[string]string{"a":"A","b":"B"}
for k,v := range m{
fmt.Println(k,v) //b B;a A
}
for k :=range m
fmt.Println("key",k) //key a;key b
9.函数
Go语言和很多其他语言不一样,函数类型是后置的。
• 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。
• 不支持 重载 (overload)
• 不支持 默认参数 (default parameter)。
Go语言里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。
-
%v 只输出所有的值
-
%+v 先输出字段名字,再输出该字段的值
-
%#v 先输出结构体名字值,再输出结构体(字段名字+字段的值)
package main
import "fmt"
type point struct {
x,y int
}
func main() {
s := "hello"
n :=123
p= point{s,n}
fmt.Printf(s,n) // hello 123
fmt.Printf(p) //{1 2}
fmt.Printf("s= %v\n", s) //s = hello
fmt.Printf("s= %v\n", n) //n = 123
fmt.Printf("s= %v\n", p) //p = {1 2}
fmt.Printf("s= %+v\n", p) //p = {x:1 y:2}
fmt.Printf("s= %#v\n", p) //p = main.point{x:1 , y:2)
}
%v的方式 = &{jiafu 123456}
%+v的方式 = &{name:jiafu id:123456}
%#v的方式 = &main.student{name:"jiafu", id:123456}
10.错误处理
过内置的错误接口提供了非常简单的错误处理机制,error 类型是一个接口类型,定义如下:
type error interface {
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
}