一、 基本类型:
- 数值
- 整型:
int
;int8
;int16
;int32
(rune
,Unicode
);int64
;uint
;uint8
(byte
);uint16
;uint32
;uint64
;uintptr
int
,uint
,uintptr
在 32 位系统上是 32 位,在 64位 系统上是 64位 - 浮点型:
float32
;float64
- 复数型:
complex64
;complex128
- 整型:
- 字符串:使用双引号
"a"
- 布尔:
true
;false
- 常量:三种基本类型
const(
kb = 1024
e = 2.71828182845904523536028747135266249775724709369995957496696763
F = false
)
在
if
语句中,若检验条件为i >= 0
,则i
的类型不宜为uint
型(uint
数据始终 >= 0)。尽管内置函数len()
返回值是非负整数,但它际返回int
型,
medals := []string{"gold", "silver", "bronze"}
for i := len(medals)-1; i >= 0; i--{
fmt.Println(medals[i]) // "bronze", "silver", "gold"
}
基本类型与操作符构成表达式
- 取余
%
只用于整型;余数与被除数符号相同,5%3=2
;-5%3=-2
-
5.0/4=1.25
;5/4.0=1.25
;5/4=1
-
&&
若左边的表达式结果为false
,不检验右边的表达式;&
始终检验两边的表达式 - 与或
^
; 一元前缀^
二、 聚集类型 (aggregate types):
1.数组
var a [3]int
r := [...]int{99: 1} // 索引为99的元素,r[99], 等于 1,其他默认 0
p := new([10]int) // 将生成的数组的指针赋给 p, 为 *[10]int 类型
p[0]=1 // 给 p 指向的数组的索引为0的元素赋值 1
数组的长度也是数组类型的一部分,因此 [3]int
和 [4]int
是不同类型的数组,不能进行比较或赋值。
2.结构
(1)结构的字段(field)
type person struct{ // 定义 person 类型
gender string
age int
}
func main(){
student := person{}
student.age = 16
student.gender = "male"
// or
student := person{
gender : "male",
age : 16, //逗号不能省
}
// or
student := person{"male", 16}
teacher := &person{ //取指针
gender : "female",
age : 30,
}
teacher.age = 36 //指针 teacher 仍然可以进行点操作
}
指针也可以进行点操作。
匿名结构,字段匿名
student := struct{ // 匿名结构
gender : string
age : int
}{
gender : "male",
age : "17",
}
type person struct{
string
int
}
// 按照顺序初始化
student := person{"male", 10}
结构嵌套,
type person struct{
gender string
age int
parents struct{ // 嵌套一个匿名结构
dad, mom : string
}
}
type address struct{
state, city string
}
type person2 struct{
gender string
age int
address // 嵌套你一个结构address
}
}
func main(){
student := person{gender : "female", age : 10}
student.parents.dad = "Tom"
student.parents.mom = "Lily"
student2 := person2{gender:"female", age:10, address : address{county:"LA" state:"California"} }
student2.address.state = "Massachusetts" // or
student2.state = "Massachusetts"
}
(2)结构的方法(method)
函数与方法
package main
import (
"fmt"
"math"
)
type Point struct{ X, Y float64 }
func Distance(p, q Point) float64 { //函数
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
func (p Point) Distance(q Point) float64 { // 方法,在函数名前增加一个形参(receiver) 类似于Java的this 和python的 self,
return math.Hypot(q.X-p.X, q.Y-p.Y) // 接收者的名称通常取它的类型名称的第一个字母
}
func main() {
p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q)) // 打印 5, 调用函数
fmt.Println(p.Distance(q)) // 调用方法
}
当需要使用方法对值(value of type T,相对于方法来讲就是实参,argument) 的字段进行修改时,使用接收者为指针的方法或者叫指针方法
func (p *Point) ScaleBy(factor float64) { // 接收者参数p的类型是指针类型
p.X *= factor // p在这里是指针,等价于 (*p).X
p.Y *= factor
}
同一个 struct 的方法和字段占据相同的命名空间(name space),因此两者的名称不能重复;
指针方法看作高权限方法。
对方法的调用, 值(实参) 和 接收者(形参)类型要相同
Point{1, 2}.Distance(q) // Point Point
pptr.ScaleBy(2) // *Point *Point
pptr.Distance(q) // 隐含 (*pptr)
p.ScaleBy(2) // 隐含 (&p)
Point{1, 2}.ScaleBy(2) //错误!!!
(&Point{1, 2}).ScaleBy(2)
不仅仅是 struct
package main
import (
"fmt"
)
type INT int
func main() {
var a INT
a = 1
a.Print() // 打印 2
}
func (a *INT) Print() {
*a = 2
fmt.Println(*a)
}
方法是与命名类型(named type)相关联的函数。
三、引用类型
1.指针
var p *int
i := 20
p = &i
*p = 10 // i 的值为 10
2.切片(slice)
var s []int // 声明切片 s
a := [5]int{1, 2, 3, 4, 5}
s = a[:2] // [1, 2], len(s)等于2,cap(s)等于5
s = a[0:1] // [1],len(s)等于1,cap(s)等于5
s = a[3:] // [4, 5],len(s)等于2,cap(s)等于2
s := make(int[], 2, 4) //make([]type, len, cap) ,切片长度为2,底层数组的长度为4
s = append(s, 1) // s 的地址不变
s = append(s, 2, 3) // 生成新的数组,地址改变,容量翻倍,也就是cap(s)等于4*2=8
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
copy(s1, s2) //把 s2 复制到 s1,s1 为 [4, 5, 3]
s1 = []int{1, 2, 3}
copy(s2, s1) // s2 为 [1, 2]
切片的本质是对底层数组的引用;切片的容量(cap)是切片的始索引到底层数组的末索引的长度。
3.映射(map)
var m map[int]string // key int 型;value string 型
m = make(map[int]string)
m2 := make(map[int]string)
m[0] = "OK"
delete(m, 0) // 删除 m 中键为0的键值对
嵌套,
m := make(map[int]map[int]string) // value map型
m[1] = make(map[int]string)
m[1][2] = "YES"
4.函数
func main(int, []string) int
means, function main takes an int and a slice of strings and returns an int
函数作为类型,
var f func(func(int,int) int, int) func(int, int) int
不定长变参,闭包
package main
import (
"fmt"
)
func main(){
var_args(1)
var_args(1, 2, 3)
f := closure(10)
fmt.Println(f(1))
fmt.Println(f(2))
}
func var_args(args ...int){
fmt.Println(args)
}
func closure(x int) func(int) int{ // 返回匿名函数
return func(y int) int{
return x + y
}
}
输出:
[1]
[1 2 3]
11
12
4.channel
channel 是 goroutine 沟通的桥梁,通过 make 创建,close 关闭
package main
import (
"fmt"
)
func main() {
c := make(chan bool)
go func() {
fmt.Println("I from goroutine !")
c <- true
}()
<-c
}
channel 作为函数形参
package main
import (
"fmt"
)
func main() {
c := make(chan bool)
go Hello(c)
<-c
}
func Hello(c chan bool) {
fmt.Println("Hello, I from goroutine!")
c <- true
}
多个 goroutine,多个channel
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // 开启多核
c := make(chan bool)
for i := 0; i < 5; i++ { // 启动多个 goroutin
go Decomposition(c, i, 100000007)
}
for i := 0; i < 5; i++ { // 多个 channel 阻塞
<-c
}
}
func Decomposition(c chan bool, index int, n int) { // 质数分解
for i := 2; i <= n; i++ {
for n != i {
if n%i == 0 {
fmt.Printf("%d*", i)
n = n / i
} else {
break
}
}
}
fmt.Printf("%d: %d\n", index, n)
c <- true
}
输出:
0: 100000007
2: 100000007
1: 100000007
4: 100000007
3: 100000007
从输出结果的顺序可以看出 goroutine 并非先启动先执行
使用同步包来代替 channel
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
wg := sync.WaitGroup{}
wg.Add(5) // 添加 5 个 任务(goroutine)
for i := 0; i < 5; i++ {
go Decomposition(&wg, i, 100000007)
}
wg.Wait() // 等到任务数减到 0
}
func Decomposition(wg *sync.WaitGroup, m int, n int) {
for i := 2; i <= n; i++ {
for n != i {
if n%i == 0 {
fmt.Printf("%d*", i)
n = n / i
} else {
break
}
}
}
fmt.Printf("%d: %d\n", m, n)
wg.Done() // 任务数减 1
}
输出:
0: 100000007
2: 100000007
4: 100000007
1: 100000007
3: 100000007
selec{}
语句,
如果有多个case 读取数据,select会随机选择一个case执行,其他不执行;
如果没有case读取数据,就执行default;
如果没有case读取数据,且没有default,select将阻塞,直到某个case可以执行。
package main
import (
"fmt"
)
func main() {
c1, c2, block := make(chan int), make(chan string), make(chan bool)
go func() {
for {
select { // 按随机顺序处理多个 case
case message, open := <-c1:
if !open { // 如果通道 c1 关闭,则跳出无限循环
block <- true
break
}
fmt.Println("A message from main by c1:", message)
case message, open := <-c2:
if !open { // 如果通道 c2 关闭,则跳出无限循环
block <- true
break
}
fmt.Println("A message from main by c2:", message)
}
}
}()
c1 <- 10
c2 <- "hello"
c1 <- 20
c2 <- "world"
close(c1) // 关闭通道 c1
<-block
}
输出:
A message from main by c1: 10
A message from main by c2: hello
A message from main by c1: 20
A message from main by c2: world
package main
import (
"fmt"
"time"
)
func main() {
select {
case <-time.After(2000000 * time.Microsecond):
fmt.Println("2 seconds")
case <-time.After(1999999 * time.Microsecond):
fmt.Println("1.999999 seconds")
}
}
输出:
1.999999 seconds
四、接口类型(interface):
(1)接口代表某些方法的集合
package main
import (
"fmt"
)
type game interface {
Strike_of_Kings() int
Battle_Grounds() int
}
type contact interface {
Wechat()
QQ()
}
type smartphone interface{ // 接口嵌套,
game
contact
}
type iphone struct {
version string
price float32
user string
}
func (iph iphone) Wechat() {
fmt.Println("I installed wechat on my iphone", iph.version)
}
func (iph *iphone) QQ() {
fmt.Println("I installed wechat on my iphone", iph.version)
}
// iphone 不满足 contact 接口
func (iph iphone) Battle_Grounds() int {
fmt.Println("There are 4 teammates at most in the Battle Grounds.")
return 4
}
func (iph iphone) Strike_of_Kings() int {
fmt.Println("There are 5 teammates at most in the Strike of Kings.")
return 5
}
// iphone 满足 game 接口
func (iph *iphone) New_Version(version string) {
iph.version = version
}
func all_round_game(game) { // 接口作为形参
fmt.Println("Both Strike of_Kings and Battle Grounds have installed.")
}
func main() {
my_phone := iphone{"X", 8316, "Xiaohe"}
my_phone.Wechat()
fmt.Println(my_phone.Battle_Grounds())
all_round_game(my_phone) // my_phone 符合 game 接口,可作为该函数的实参
输出:
I installed wechat on my iphone X
There are 4 teammates at most in the Battle Grounds.
4
Both Strike of_Kings and Battle Grounds have installed.
}
(2)任何类型都满足空接口;空接口interface{}
作为形参可以接受任何类型的实参
package main
import (
"fmt"
)
func main() {
m := make(map[int]interface{})
m[1] = "a"
m[2] = 2
m[3] = false
print_map(m)
}
func print_map(m map[int]interface{}) {
for k, v := range m {
fmt.Println(k, ":", v)
}
}
输出:
1 : a
2 : 2
3 : false
[]string
和[]interface{}
是不同的类型;
接口是一种抽象类型,可理解为是将所有具体类型按照方法集进行再分类;
指针方法集包含非指针方法集。
(3)接口值(interface value)包含 类型 (接口的动态类型)和 类型值 (接口的动态值) 两个部分,仅当两者均为nil
时,接口才为nil
(4)反射 (reflection)
package main
import (
"fmt"
"reflect"
)
type iphone struct {
version string
price int
user string
}
func (iph iphone) Wechat() {
fmt.Println("I installed wechat on my iphone", iph.version)
}
func main() {
my_phone := iphone{"X", 8316, "Xiaohe"}
Info(my_phone)
}
func Info(itf interface{}) {
t := reflect.TypeOf(itf)
fmt.Println("What type:", t.Name())
v := reflect.ValueOf(itf)
for i := 0; i < v.NumField(); i++ {
fmt.Println(t.Field(i).Type)
fmt.Println(v.Field(i))
}
}
输出:
What type: iphone
string
X
int
8316
string
Xiaohe
五、控制流 (for if switch defer goto):
(1)for
:
for (inti statement);condition;(post statement) {
}
for i := 0; i < 10; i++ {
}
for ; i < 10; { // 去掉分号
}
for i < 10{ // while 语句
}
for{ // 无限循环
}
(2)if
:
if (init statement);condition {
}
if (init statement);condition {
}else {
}
(3)switch
:
switch (init statement);some value {
case 0:
case f():
...
default:
}
switch {
case 布尔表达式1:
case 布尔表达式2:
...
default:
}
一旦符合条件自动终止,若希望继续检验下面的case,使用 fallthrough
语句。
(4)defer
:
- defer 后必须跟函数引用
- defer 语句被检验后,延迟函数获得变量的拷贝
func a() {
i := 0
defer fmt.Println(i)
i++
return
} // defer 语句打印0
- defer 语句被检验后,延迟匿名函数获得变量的地址
func b() (i int) {
defer func() { i++ }()
return 1 // 将1 赋给 i
} // 返回 2。利用 defer 语句修改外围函数的命名返回值
func c() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Print(i)
}()
}
return
} // 打印 333
- defer 语句被检验后,延迟函数的引用被推入堆栈,当外围函数返回后,按照后进先出的顺序被调用(即使外围函数发生错误,如 panic,延迟函数仍然会被调用)
func d() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
} // 打印 3210
更多细节如,panic
, recover
(只能用在延迟函数中) 参考 defer blog。
(5)goto
:
LABEL:
for {
for i := 0; i < 10; i++ {
if i > 3 {
break LABEL // 跳出与LABEL同级的循环,即跳出无限循环
}
}
}
LABEL:
for i := 0; i < 10; i++ {
for {
continue LABEL
}
}
LABEL:
for {
for i := 0; i < 10; i++ {
if i > 3 {
goto LABEL // 将再次进入无限循环
}
}
}
通常
标签
放到goto
的后面。
创建工程目录:
Go工程中共有三个部分:
- src:存放go源码文件
- pkg:存放编译后的包文件
- bin:存放编译后的可执行文件
注意:src目录需要自己创建,pkg和bin编译时会自动创建。
步骤:
- 新建工程目录,my_project,并在该目录下创建 src目录;
- 把my_project 添加到 GOPATH,GOPATH=/home/user/...;my_project(可以同时添加多个路径目录,Linux下用冒号:隔开,window下分号;隔开);
- 在 src 下创建my_pkg 和 my_cmd;
- 包文件放入到 my_pkg 中,比如 test.go
package my_pkg
import "fmt"
func Test(){
fmt.Println("Hello,world!")
fmt.Println("You used a function defined in my_package!")
}
在命令行src目录,执行 go install my_pkg
将创建 pkg 目录并声成 my_pkg.a 文件。
- my_cmd 中放入 package main,比如 hello_world.go
package main
import(
"my_pkg"
)
func main(){
my_pkg.Test()
}
在命令行src目录,执行 go install my_cmd
将创建 bin 目录并生成可执行文件成 hello_world.exe 文件。
目录结构:
src/
my_pkg/
test.go
my_cmd/
hello_world.go
其他:
-
fmt.printf
verbs:
%x %b
:16进制,2进制显示;%t
:显示 bool 结果;%T
:显示值的类型;%v
:显示值;%p
:显示地址;\n
:换行 - Sublime text 3
上一个编辑处: alt+-
下一个编辑处: alt+shift+- - GoSublime:
GoSublime快捷键列表:ctrl+.+. (连击 .)
查看声明:ctrl+.+h
代码跳转:ctrl+shift+左键
package control:ctrl+shift+p
参考资料:
[1] 无闻;Go编程基础系列视频.
[2] Alan A.A. Donovan; Brain W. Kernighan; The Go Programming Language; 2015.
网友评论