1. 变量与数据类型
1.1 变量的声明
1.1.1 单变量声明
var 变量名 数据类型
var 变量名 数据类型 = 初始值
var 变量名 = 初始值
变量名:=初始值
- 后面两种方式会自动推导数据类型
1.1.2 多变量声明
var 变量1, 变量2, 变量3 数据类型
var 变量1, 变量2, 变量3 = 值1, 值2, 值3
变量1, 变量2, 变量3 := 值1, 值2, 值3
- 不同类型的变量进行一起声明
var (
n1=100
n2=100
name="mary"
)
1.2 数据类型
- 常见的数据类型
- 整型
- 浮点型
- 字符串
- 布尔类型
- 数值类型之间转换都需要显示转换
-类型(要转换的变量)
1.2.1 字符串与数值类型转换
fmt.Sprintf("%参数",表达式)
- 使用strconv包的函数
1.2.2 指针类型
var ptr *int = &num
1.2.3 值类型与引用类型
- 值类型:通常在栈上分配
- 基本数据类型
- string
- 数组
- 结构体
- 引用类型:通常在堆区分配
- 指针
- slice切片
- map
- 管道
- interface
1.2.4 自定义数据类型
type myInt int
type myFunType func(int,int) int
- 定义了一个func(int,int) int 的函数类型
2. 运算符与流程控制
2.1 运算符
取消了++i,但保留i++
而且i++是语句,不是表达式,j=i++是非法的
2.2 流程控制
2.2.1 if语句
// go支持在if中定义变量
if age:=10; age >18{
执行代码块1
}else if age <10{
执行代码块2
}else{
执行代码块3
}
2.2.2 switch语句
// 语句块后面不需要写break
switch 表达式{
case 表达式1,表达式2,...:
语句块1
case 表达式3,表达式4,...:
语句块2
default:
语句块
}
- switch后面不带表达式,则根据case中的表达式来判断
- switch后面要是带表达式,case后面接字面量
- 同理,switch后面也可以声明变量
- 在语句块后面加
fallthrough
可以穿透case
2.2.3 for循环语句
- 方式1
for 循环变量初始化;循环条件;循环变量迭代{
循环语句
}
- 方式2
// 等价与 java中的while(条件)
for 循环条件{
循环语句
}
- 方式3
// 等价于java中的while(true)
for{
循环语句
}
3. 函数
3.1 函数的基本语法
func 函数名(形参列表) (返回值类型列表){
执行语句
return 返回值列表
}
- 函数首字母大写类似于
public
- 函数首字母小写类似于
private
函数也是一种数据类型
3.1.1 函数类型作为函数参数
type myFunType func(int,int) int
func myFun2(funvar myFunType,num1 int,num2 int) int{
return funvar(num1,num2)
}
3.1.2 支持对函数返回值命名
func getSumAndSub(n1 int,n2 int) (sum int,sub int){
sub=n1-n2
sum=n1+n2
return
}
// _ 忽略返回值
a1,_ :=getSumAndSub(1,2)
3.1.3 支持可变参数
func sum(n1 int,args... int) (sum int){
语句
}
3.2 常见的函数
3.2.1 init函数
- 每个源文件都可以包含一个init函数(没有返回值),主要用于一些初始化动作
- 该函数会在main函数执行前被Go运行框架调用。
- 如果一个文件同时包含全局变量定义,init函数和main函数,则执行顺序为
- 全局变量定义
- init函数
- main函数
3.2.2 定义匿名函数
- 定义时直接使用
res1:=func(n1 int,n2 int) int{
return n1+n2
}(10,20)
fmt.Println("res1=",res1)
- 定义时用变量接收
res1:=func(n1 int,n2 int) int{
return n1+n2
}
fmt.Println("res1=",res1(90,30))
- 全局匿名函数
var(
Fun1=func(n1 int,n2 int)int{
return n1*n2
}
)
3.3 闭包
- 闭包就是
一个函数
和与其相关的引用环境
组合的一个整体 - 非常像面向对象
func AddUpper() func(int) int{
var n int =10
return func(x int) int{
n=n+x
return n
}
}
func main(){
f:=AddUpper()
fmt.Println(f(1))
fmt.Println(f(3))
}
3.4 defer
- 在函数中,当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈
- 当函数执行完毕后,再从defer栈,按照先入后出的方式出站执行
- 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈
func sum(n1 int,n2 int) int{
defer fmt.Println("ok1 n1=",n1)
defer fmt.Println("ok2 n2=",n2)
n1++
n2++
res:=n1+n2
fmt.Println("ok1 res=",res)
return res
}
func main(){
res:=sum(10,20)
fmt.Println("res=",res)
}
/*
ok3 res=32
ok2 n2=20
ok1 n1=10
res=32
*/
3.5 函数的参数传递方式
- 无论是值传递还是引用传递,都是传递的是变量的副本
- 在函数外声明的变量是全局变量,作用域在整个包都有效;如果其首字母大写,则作用域在整个程序有效
- 如果变量声明在代码块,或者for / if 中,则作用域则为该代码块
3.6 内置函数
- len(变量):求长度
- new(值类型):分配内存,主要用来分配值类型,返回的是指针
- make(引用类型):分配内存,主要用来分配引用类型,返回的是指针
4 包的概念
- 每一个go文件要求属于一个包
- 包可以区分相同名字的函数、变量等标识符
- 包可以控制函数、变量等访问范围,即作用域
- 当程序文件很多时,便于管理项目
4.1 打包语法
package 包名
4.2 引包语法
import “包的路径”
import 别名 "包的路径"
5. 错误处理
5.1 利用recover()和defer可以实现捕获错误
func test(){
defer func(){
err:=recover()
if err!=nil{
fmt.Println("err=",err)
错误处理逻辑
}
}()
num1:=10
num2:=0
res:=num1/num2
fmt.Println("res=",res)
}
5.2 自定义错误
- 通过errors.New()自定义错误
- 通过panic()向外抛出错误
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()继续执行")
}
6. 数组与切片
6.1 数组的定义
var 数组名 [数组大小]数据类型
var a [5]int
6.2 数组的初始化
var arr1 [3]int =[3]int{1,2,3}
var arr2=[2]int{1,2}
var arr3=[...]int{1,2,3}
var arr4=[...]int{1:800,0:900,2:999}
arr5:=[2]int{1,2}
6.3 数组的遍历
for index,value:=range arr1{
执行语句
}
6.4 切片(类似java中的ArrayList)
- 切片是数组的一个引用,因此切片是引用类型。
- 切片进行传递时,遵循引用传递的机制
- 切片的使用与数组类型
- 切片长度是可以变化的,因此切片是一个可以动态变化的数组
6.5 切片的定义
var 切片名 []类型
var a []int
6.7 切片的使用
- 方式一
var arr [5]int=[...]int{1,22,33,44,99}
// 用切片引用一个创建好的数组
slice:=arr[1:3]
- 方式二
var 切片名 []type=make([]type,len,[cap])
var slice []float64 =make([]float64,5,10)
- 方式三
var slice []string=[]string{"hello","jack"}
6.8 关于切片的内置函数
6.8.1 append
var slice []int=[]int{1,2,3}
slice=append(slice,4,5,6)
slice=append(slice,slice...)
- 追加操作本质是对数组扩容。会创建一个新的更大的数组,然后将之前的元素拷贝过来。
6.8.2 copy
var slice1 []int=[]int{1,2,3,4,5}
var slice2 = make([]int,10)
copy(slice1,slice2)
6.9 string与slice的关系
- string底层是一个byte数组,因此string也可以进行切片处理
- string底层的byte数组里面的数据是不可变的,所以即使切片了我们也不能修改
- []rune 可以按照字符处理,兼容汉字
str:="hello 北京"
arr1:=[]byte(string)
arr1[0]='z'
str="北京"
arr1=[]rune(str)
arr1[0]='南'
str=string(arr1)
6.10 二维数组
var 数组名 [大小][大小]类型
var arr [2][3]int=[2][3]int{{1,2,3},{4,5,6}}
- 定义和使用跟一维数组差不多
7. map
7.1 map的定义
var 变量名 map[keytype]valuetype
var a map[string]string
var a map[string]int
var a map[string]map[string]string
- map的key不能重复,重复则覆盖
- map的key-value是无序的
- map是引用类型
- map会自动扩容
7.2 map的使用
var a map[string]string
a=make(map[string]string,10)
a["no1"]="张三"
cities:=make(map[string]string)
cities["no1"]="北京"
heroes :=map[string]string{
"no1":"张三",
"no2":"李四"
}
7.3 map的元素删除和查找
7.3.1 删除
- 通过内置函数delete
delete(map1,"no1")
7.3.2 查找
val,ok:=map1["no1"]
if ok {
执行语句
}else{
没找到
}
7.3.3 map遍历
for key,value:=range map1{
执行语句
}
7.4 map切片
- 类似Java中的
ArrayList<Map>
var monsters []map[string]string
monsters=make([]map[string]string,2)
monsters[0]=make(map[string]string,2)
monsters[0]["name"]="张三"
monsters[0]["age"]="23"
7.5 map排序
- go中没有针对map排序的方法
- 可以将key收集,然后对key排序
map1:=make(map[int]int,10)
map1[10]=100
map1[2]=12
map1[3]=90
var keys []int
for k,_:= range map1{
keys=append(keys,k)
}
sort.Ints(keys)
for _,k:=range keys{
fmt.Println(map1[k])
}