原文链接:https://www.ahaoaha.top/2019/04/28/Golang基础语法总结/
本文基于golang1.12.5。
前提知识
- golang以包(文件夹)作为管理单位
- 每一个
xxx.go
文件必须先声明所属包 - 一个程序中必须有一个main包
- 导入的包必须进行使用,否则会编译报错,在VsCode编辑器下,保存
xxx.go
文件时,未使用的包会自动进行删除 - golang中的init函数会在main函数开始之前执行,当init函数执行完成之后,main函数才会开始执行
代码规范
golang对代码规范要求十分严格,不规范代码会产生各种问题。
- 函数的第一个括号必须和函数名位于同一行
- if语句的第一个括号必须与if位于同一行
- else语句必须与其对应的if的上一个右括号以及自身左括号位于同一行
- switch语句的右括号必须与switch在同一行
常用的包
fmt
:包含IO相关的函数
golang关键字
int default break func interface select case defer go map
struct chan else goto package switch const fallthrough if
range type continue for import return var
内建常量
true false
iota
nil
内建类型
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内建函数
make len cap new append copy close delete
complex real imag
panic recover
函数名 | 说明 |
---|---|
len、cap | len用于返回某个类型的长度或数量(字符串、数组、切片、map、管道);cap用于返回某个类型的最大容量(只可用于切片和map) |
new、make | new和make都用于内存分配:new用于值类型和用户定义的类型,make用于内置类型(切片、map、管道) |
copy、append | copy用于复制切片;append用于连接切片 |
panic、recover | 两者均用于错误处理机制;panic类似于C语言中的perror |
print、println | 底层打印函数 |
complex、read、imag | complex用于创建复数;read用于提出复数的实部;imag用于提取复数的虚部 |
变量
定义变量
-
使用var关键字:
var 变量名 变量类型
package main import "fmt" func main() { var a int //声明的变量必须使用,否则会产生错误 a = 1 //变量赋值 fmt.Println(a) var b, c int b, c = 3, 4 fmt.Println(b, c) } //使用var关键字声明变量的初始化方式: //1.声明之后采用赋值 //2.var 变量名 变量类型 = 变量初始值
-
var自动推导类型:var 变量名 = 变量初始值
package main import "fmt" func main() { a := 1 //定义变量+初始化 b, c := 3, 3.1415 fmt.Println(a) fmt.Println(b, c) }
-
:=
自动推导类型:变量名 := 变量初始化的值
package main import "fmt" func main() { var a = 1 //定义变量+初始化 var ( b = 3 c = 3.1415 ) fmt.Println(a) fmt.Println(b, c) }
- 声明一个变量未进行初始化时,该变量的值默认为零值。
- 对于同一个变量,
:=
在其生命周期中只能使用一次。
交换两个变量的值:
package main
import "fmt"
func main() {
a := 3
b := 5
a, b = b, a
fmt.Println(a, b)
}
匿名变量
-
_
:表示匿名变量,通常使用匿名变量接收的值会被丢弃使用场景:当一个函数返回两个数据时,只想使用第一个不使用第二个,此时就可以用匿名变量接收第二个返回值。
常量
声明常量
-
使用const关键字:
const 常量名 常量类型 = 常量值
package main import "fmt" func main() { const a int = 1 //声明常量必须进行初始化 fmt.Println(a) }
-
**自动推导类型:
const 常量名 = 常量值
**package main import "fmt" func main() { const a = 1 //常量推导类型使用=,与变量不同 fmt.Println(a) }
数据类型
内建类型
自定义类型
数组
-
声明格式:
var 数组名 [元素个数]元素类型
var arr1 [5]int //具有5个int元素的数组,未进行初始化,元素默认为零值 var arr2 [5]int{1, 2, 3, 4, 5} //定义并直接初始化
切片
-
声明格式:
var 切片名 []切片类型
var sic1 []int //声明含有多个未知元素的切片 var sic2 []int{} //声明空切片
-
使用
append
函数可以为切片追加内容 -
使用
[]
可以对数组进行切片操作:[开始位置:结束位置]
所切区域为前闭后开 -
切片内含扩容机制,以2倍方式增容
结构体声明
-
type 结构体名 struct {}
:右括号必须和type在同一行package main import "fmt" type student struct { var id int32 var name string } //声明一个结构体 func main() { var ahaoo student //创建结构体对象 ahaoo.id = 1 ahaoo.name = ahao fmt.Println(ahaoo.id, ahaoo.name) var nar student = {2, "nar"} fmt.Println(nar.id, nar.name) }
枚举声明
-
使用iota+const关键字
- iota为常量自动生成器, 每隔一行自动累加1
- iota给常量赋值使用
- iota遇到const就会重置为0
- 同一个const中可以只写一个iota
- 如果iota位于同一行,则值相同
package main import "fmt" const ( r = iota //0 s = iota //1 t = iota //2 ) const ( u = iota //0 v //1 w //2 ) const ( u1 = iota //0 v1, w1, x1 = iota, iota, iota //1, 1, 1 ) func main() { fmt.Println(r) }
指针
golang中的取地址符号是&
-
普通指针:对变量/常量取地址的方式与C语言相同
//普通指针的声明 var 指针名 *指针类型
-
函数指针:函数指针依然受限于参数列表
//函数指针的声明方式 //1.推导类型 func test() { //TODO } funcptr := test //var声明 var 函数指针名 func(参数列表)
-
空指针:当声明指针未进行初始化时,指针的默认值为空指针
类型转换
要转换的数据类型(变量名)
:类似C的类型转换
- bool类型不能转换为整型,整型也不能转换为bool类型,这种不能转换的类型叫做不兼容类型。
类型别名
type 类型别名 类型原名
逻辑语句
if语句
if
- 左括号必须和if在同一行
if/else
- else必须和if的右括号以及自己的左括号在同一行
if/else if/…/else
- else if必须和上一个右括号以及自己的左括号在同一行
for循环
基本格式
for 初始条件; 判断条件; 条件变换 {
//TODO
}
range关键字的使用
- 作用:迭代遍历切片/数组中的每一个元素,默认有两个返回值,第一个为元素下标,第二个为元素数据
for k, v := range array {
//k为array中元素的下标
//v为array中元素的数据
}
for k := range array {
//此时第二个返回值默认丢弃
}
for _, i := range array {
//此时第一个参数默认丢弃
}
switch语句
- golang保留break关键字,在switch语句中如果不写则默认添加
- break可用于for/switch/select
- continue只可以用于for
- case后面可以跟多个条件
//首先声明变量
var num1 int = 1
switch num1 {
case 1:
//...
case 2:
//...
case 3, 4, 5:
//...
...
default:
//...
}
//直接使用初始化语句
switch num2 := 1; num1 {
case 1:
//...
case 2:
//...
...
default:
//...
}
//switch后面可以不添加参数
var num int = 0
switch {
case num > 90:
//...
case num < 90:
//...
default:
//...
}
函数
函数定义的格式
func 函数名(参数列表)(返回值变量名 返回值类型, 返回值变量名, 返回值类型...) {
//函数体
}
- 函数的声明由
func
关键字开始 - FuncName:根据约定函数名首字母小写即为private,函数名首字母大写即为public
- 参数列表:函数可以有0/多个参数
- 返回值列表:函数可以返回0/多个返回值
- 如果一个函数的返回值只有一个且不声明返回值变量,则返回值的括号可以省略
- 如果没有返回值就直接忽略最后的返回值信息
- 如果有返回值,那么必须在函数内部添加return语句
变长参数列表
-
函数的最后一个参数是用
...类型名
命名就说明该函数的参数列表是一个不定长的参数列表func Func(...int) { //TODO } //变长参数列表传递的参数类似于该类型的slice,可使用for循环迭代
defer关键字
- 被
derfer
修饰的语句或函数会直到返回之后才会被执行
golang并发
- 使用
go 函数名
可以创建出一个协程执行对应函数
golang面向对象
golang中没有类,但是它支持struct,struct是用户自定义类型(含方法),可以像其他语言中的类一样使用。
定义struct成员函数
(形式对象 结构体名)
被称为接收器条款,也就是只有通过该种类型的对象才能调用该函数
//模板
func (形式对象 结构体名) 函数名(参数列表)(返回值列表) {
//
}
嵌入
可以将一个匿名的类型嵌入彼此,如果将一个匿名的struct A嵌入另一个struct B,则A的接口也可以通过B对象来进行调用,但是A对象只能调用A自己的函数
//例子
package main
import ."fmt"
type student struct {
name string
id int32
class
}
type class struct {
}
//student的成员函数
func (st student) GetName()string {
return st.name
}
//class的成员函数
func (cl class) FuncTest()string {
return "hello this is class"
}
func main() {
var st student
var cl class
st.name = "ahao"
st.id = 1
Println(st.GetName()) //打印"ahao"
Println(st.FuncTest()) //打印"hello this is class"
Println(cl.GetName()) //编译报错
Println(cl.FuncTest()) //打印"hello this is class"
}
接口
接口就是定义出一个类似于函数的集合,该集合中存在很多未进行定义的函数,当实现一个结构体时并且将该接口中的函数全部实现时,就成为结构体实现了该接口,体现了golang的多态性
-
声明接口
interface
-
接口可以被任意的struct实现
一个接口struct实现接口中的所有方法才能算作实现接口,如果只实现部分接口,代码编译会出现panic
package main import "fmt" type Itfe interface { Func1() Func2() Func3() } type student struct { } func (st student) Func1() string { return "实现了Func1()" } func (st student) Func2() string { return "实现Func2()" } func (st student) Func3() string { return "实现Func3()" } func main() { var st student st.name = "ahao" st.id = 1 st.Func1() //打印"实现了Func1()" st.Func2() //打印"实现了Func2()" st.Func3() //打印"实现了Func3()" }
接口和结构体的关系
- 一个接口类型的对象可以作为函数的参数来接收实现了该接口的结构体对象
- 如果一个接口实现了某一个接口中的所有的方法,那么就可以认为这个接口实现了该接口,如果一个结构体实现了某一个接口,就说明这个结构体对象可以被参数为该接口类型的对象接收,当在函数内部通过该接口使用接口中的某一个方法时,会自动匹配对应结构体所实现的方法
- //接口与指针TODO
接口的类型判断
func IntFunc(INTERFACE_OBJ INTERFACE_TYPE) {
switch INTERFACE_OBJ.(type) {
case STRUCT_TYPE1:
//DOSOMETHING
case STRUCT_TYPE2:
//DOSOMETHING
...
default:
//DOSOMETHING
}
}
传入的STRUCT_TYPE不需要使用 " " 标注
- 接口可以通过上述方式判断传入的结构体类型
空接口
- 每一种类型都能匹配到空接口,也就是说可以使用空接口的作为函数的参数来接收任何类型的对象
//将空接口转换为INTERFACE_TYPE类型接口并进行INTERFACE_MOTHOD方法调用
func IntFunc(INTERFACE_OBJ interface{}) RETURN_TYPE {
INTERFACE_OBJ.(INTERFACE_TYPE).INTERFACE_MOTHOD
}
封装
- golang在包级别进行封装,以小写字母开头的函数仅在当前包中有效
特殊语法
-
import ."PackageName"
:在该文件中可以直接使用包中的函数,不需要PackageName.Func()
package main import ."fmt" func main() { Println("hello world") }
-
import _"PackageName"
:当导入一个包时,该包文件下的所有init()函数都会被执行,然而有时候仅仅需要执行该包中的init()函数而已,这个时候就使用import _"PackageName"完成。