Golang基础知多少
代码结构概览
首先,GO语言中的代码结构做一个概览
// 所属包
package main
// 导入依赖包
import "fmt"
// 声明常量
const filename = "abc.txt"
// 声明全局变量
var a string = "hello"
// 类型声明
type myint int
// 声明结构体
type Student struct {
Name string
Age int
}
// 声明接口
type learn interface {}
// 函数定义
func learnGo() {
fmt.Println("something good")
}
// main函数
func main() {
...
}
Package包
package是工程管理中依赖关系的体现:
- 每个Go语言源代码开头,都拥有一个package声明,表示源码文件所属代码包
- 如果要生成可执行文件,则必须要有名为main的package包,并且该包下必须有main函数
- 相同路径下只能存在一个package,一个package可以分多个源文件
Import导入
import语句用来导入所依赖的package包:
- 仅需要导入源文件中需要用到的package,存在不用的包导入,则会编译报错
- 导入语句的import可以一行行写,也可以通过括号方式一起写
- 如果一个main导入了其他的包,则将先顺序导入依赖的包
- 导入过程包括初始化依赖包中的常量和变量等,如果有init()函数,则会自动执行
- 所有依赖的包导入完成后,才会对main中的常量和变量进行初始化,然后执行main中的init()函数,最后执行main函数
此外,import语句还有以下几种特殊写法:
- 别名
//给fmt起别名为f
import f "fmt"
//引用时则直接使用f
f.Println("Hello")
.
点标示
import (
. "fmt"
)
//点标示的包导入后,调用该包中的函数时,可以省略前缀包名
Println("Hello")
_
下划线标示
//说明:使用下划线标示后,表示只执行该包中的init函数,但并不通过包名来调用包中其他函数。此下划线用法,往往是为了注册包里面的引擎
import (
"log"
"net/http"
_ "net/http/pprof"
"os"
)
数据类型
数据类型是为了把不同的数据分成不同的类型,以便在使用时仅分配类型对应所需内存大小。
基础数据类型种类
与其他编程语言不一样的是,golang原生支持复数类型:
- 整型
- uint8
- uint16
- uint32
- uint64
- int8
- int16
- int32
- int64
- int和uint: 32位或64位,取决于计算机类型
- 浮点型
- float32
- float64
- 复数
- complex64: 32位实数和虚数
- complex128: 64位实数和虚数
- 字符串
- string
- 布尔型
- bool
- 其他
- byte: 类似uint8
- rune: 类似int32
- uintptr: 无符号整型,用于存放一个指针
注意:
- go语言中没有char,只有rune
- go语言中的类型转换必须是显式的,并且只能发生在两种兼容类型之间
数据类型存储大小
下面是在网上看到的一张描述各数据类型存储大小的示例图,画的比较好,看了让人觉得一目了然:
扩展数据类型
除了上面的基础数据类型外,go语言中还有些适合复杂场景需求的特殊数据类型
- 指针类型
- 数组类型
- 切片类型
- 结构体类型
- Map类型
- 接口类型
- Channel类型
- …
变量
变量声明和赋值可放在包内,或放在函数内。与其他编程语言最大的不同就是:变量类型写在变量名后面
- 使用var关键字
var a string
a = "golang"
var b bool
var c, d int
var s1, s2 string = "hello", "world"
var (
x int
y float32
)
- 使用
:=
// 此种方式,没有明确的写出变量的类型,但是会根据右边对应的赋值来识别类型
a, b, x, y, s1, s2 := true, false, 3, 3.14, "hello", "world"
注意:
- go语言中,声明后的变量必须要使用,否则编译报错
- 全局变量的声明必须采用var关键字方式
- 对于仅用作占位而不后续使用的变量,可以使用
_
下划线(常用在数组、map循环遍历的场景) - 仅声明但没有赋值的变量,将使用类型零值来作为默认值(如:整型默认0,布尔默认false,string默认空字符串)
- 大写字母开头的变量是可导出的公有变量;小写字母开头的变量是不可导出的私有变量
常量
常量的声明
只有布尔类型,整、浮、复数类型,和字符串类型支持声明为常量。常量的声明形式也分为两种:
- 显式
声明时指定了变量类型
const 变量名 变量类型 = 值
//例
const filename string = "hello.txt"
- 隐式
声明时不指定变量类型,这种无类型常量会根据右边赋的值来识别类型
const 变量名 变量类型 = 值
//例
const filename = "hello.txt"
const a, b = 3, 4
特殊常量iota
iota
在const关键字声明出现时候的默认值为0,之后是以步长1来按行递增
注意:
- 出现在同一行的多个
iota
的值相等 iota
由0起始、按行递增的作用范围仅限于当前const关键字声明的变量iota
可以做运算后再赋值,如:iota * 2
- 例1
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
)
//上面示例的结果:b为2的0次方,kb为2的10次方,mb为2的20次方,依次类推
- 例2
const (
a = iota
b = iota * 3
_ // 下划线占位符也用了一行,也会让iota的值递增
c = iota
)
const (
// 此const声明中的iota仍然从0开始,不会受到上一个const声明中的iota递增的影响
d = iota * 2
e = iota
f = iota
)
const (
aa, bb = iota, iota * 3 // 出现在同一行的多个`iota`的值相等
cc, dd
_, _ // 占位符的格式要与紧挨着的上一行的变量个数保持一致
ee = iota
_
ff
)
func main() {
fmt.Println("a", a)
fmt.Println("b", b)
fmt.Println("c", c)
fmt.Println("d", d)
fmt.Println("e", e)
fmt.Println("f", f)
fmt.Println("aa", aa)
fmt.Println("bb", bb)
fmt.Println("cc", cc)
fmt.Println("dd", dd)
fmt.Println("ee", ee)
fmt.Println("ff", ff)
}
// 执行结果:
// a 0
// b 3
// c 3
// d 0
// e 1
// f 2
// aa 0
// bb 0
// cc 1
// dd 3
// ee 3
// ff 5
运算符
算术运算符
+ - * / % ++ --
关系运算符
== != > < >= <=
逻辑运算符
&& || !
按位运算符
& | ^ << >>
赋值运算符
除了=
等号以外,算术运算符或按位运算符写在等号左边就可以变成一个新的赋值运算符,如:+= %= &= >>=
控制语句
if条件语句
语法格式
if [赋值表达式;] 条件语句1 {
...
} else if 条件语句2 {
...
} else {
...
}
注意:
- if条件里不需要括号
- if条件里可以赋值
- if条件里赋值的变量的作用域仅限在这个if语句
例1: if
// 条件语句不需要括号
if num > 100 {
return 100
} else if num < 0 {
return 0
} else {
return num
}
// 带赋值的条件语句
if contents, err := ioutil, ReadFile(filename); err == nil {
fmt.Println(string(contents))
} else {
fmt.Println("read file error: ", err)
}
switch选择语句
语法格式
switch [表达式] {
case sth1:
...
case sth2:
...
default:
...
}
注意:
- switch关键字后面可以没有表达式
- 在匹配到case后会自动break,除非使用fallthrough
- go语言中还有个select语句与switch非常相似,但是select只用于chan
例2: switch
// 判断op为哪种运算符
switch op {
case "+":
result = 1 + 1
case "-":
result = 1 - 1
case "*":
result = 1 * 1
case "/":
result = 1 / 1
default:
panic("wrong operator:" + op)
}
// switch关键字后没有表达式的情况
var score int = 81
level := ""
switch {
case score < 60:
level = "F"
case score < 70:
level = "C"
case score < 80:
level = "B"
case score <= 100:
level = "A"
default:
panic(fmt.Sprintf("wrong socre: %d", score))
}
// 用type-switch的方式来判断interface变量中实际存储的变量类型
var a interface{}
a = 32
switch a.(type) {
case int:
fmt.Println("int")
case string:
fmt.Println("string")
default:
fmt.Println("something else")
}
for循环语句
语法格式
for 初始条件; 结束条件; 递增表达式 {
…
}
注意:
- for的条件里不需要括号
- for关键字后面的初始条件、结束条件、 递增表达式都可以省略,此时相当于死循环
- go语言中没有while;当省略初始条件时,就相当于其他语言中的while
例3: for
// 带条件的常规for循环
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
// 死循环
for {
fmt.Println("hello world")
}
// 配合range进行遍历
str := []string{"I", "am", "a", "boy"}
for key, value := range str {
fmt.Println("key: ", key , "value: ", value)
}
控制语句关键字
- goto
- break
- continue