顺序编程
变量
var v10 int
v10 = 123
v11 := 234
// 匿名变量示例
func GetName() (firstName, lastName nickName string) {
return "May", "Chan", "Chibi"
}
_, _, nickName := GetName()
常量
iota是一个特殊的常量,它会在每一个const关键字出现时重置为0,然后在下一个const出现前,每出现一次iota,它代表的值加1。如果两个const的赋值语句表达式一样,后一个赋值可省略。
const (
c0 = iota
c1 = iota
c2
)
枚举
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays
)
类型
- 布尔:bool
- 整型:int8、byte、int16、int、uint、unitptr等
- 浮点:float32、float64
- 复数:complex64、complex128
- 字符串:string:在初始化后不能被修改
- 字符:rune
- 错误:error
- 指针(pointer)
- 数组(array)
- 切片(slice):可动态增减元素
- 字典(map)
- 通道(chan)
- 接口(interface)
- 结构体(struct)
// 切片例子
mySlice := make([]int, 5, 10)
fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))
mySlice = append(mySlice, 1, 2, 3)
mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2)
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) //将slice1中的前3个元素复制到slice2
copy(slice1, slice2) //将slice2中的前3个元素复制到slice1
// Map例子
package main
import (
"fmt"
)
type PersonInfo struct{
ID string
Name string
Address string
}
func main(){
var personDB map[string] PersonInfo
personDB = make(map[string] PersonInfo)
personDB["12345"] = PersonInfo{"12345", "Tom", "Room 12345"}
personDB["546"] = PersonInfo{"546", "Tom", "Room 546"}
person, ok := personDB["1234"]
if ok {
fmt.Println("Found person", person.Name)
}else{
fmt.Println("Did not find person")
}
delete(personDB, "546")
fmt.Println(personDB["12345"].Name)
}
流程控制
// if else
func if_else(x int) int {
if x == 0 {
return 5
}else{
return x
}
}
// switch case
func switch_case(i int){ //无需break
switch i{
case 0:
fmt.Println("0")
case 1:
fmt.Println("1")
default:
fmt.Println("Default")
}
}
// for
sum := 0
for {
sum ++
if sum > 10 {
break
}
}
for j := 0; j < 5; j++ {
ftm.Println(j)
}
// goto
func myfunc(){
i := 0
HERE:
fmt.Println(i)
i++
if i < 10 {
goto HERE
}
}
函数
import "mymath" //假设Add被放在一个叫mymath的包中
c := mymath.Add(1, 2)
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
}
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
myfunc2(args...)
}
任意类型的不定参数
func Printf(format string, args ...interface{}){
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value")
case string:
fmt.Println(arg, "is a string value")
case int64:
fmt.Println(arg, "is an int64 value")
default:
fmt.Println(arg, "is an unknown type")
}
}
}
多返回值
func (file *File) Read(b []byte) (n int, err Error)
匿名函数与闭包
package main
import(
"fmt"
)
func main(){
var j int = 5
a := func()(func()){
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}
// 结果
// i, j: 10, 5
// i, j: 10, 10
异常处理
type error interface {
Error() string
}
大多数函数,如果要返回错误,可以定义如下模式。
func Foo(param int)(n int, err error) {
// ...
}
n, err := Foo(0)
if err != nil {
// 错误处理
} else {
// 使用返回值n
}
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
defer
func CopyFile(dst, src string) (w int64, err error){
srcFile, err := os.Open(src)
if err != nil {
return
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
panic/recover
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime error caught: %v", r)
}
}()
面向对象编程
结构体
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
type Header map[string] []string
func (h Header) Add(key, value string){
textproto.MIMEHeader(h).Add(key, value)
}
func (h Header) Set(key, value string){
textproto.MIMEHeader(h).Set(key, value)
}
引用类型:数组切片、map、channel、接口(interface)
值类型:byte、int、bool、float32、float64、string、array、struct、pointer等
type Animal struct {
Name string //名称
Color string //颜色
Height float32 //身高
Weight float32 //体重
Age int //年龄
}
//奔跑
func (a Animal)Run() {
fmt.Println(a.Name + "is running")
}
type Lion struct {
Animal //匿名字段
}
func main(){
var lion = Lion{
Animal{
Name: "小狮子",
Color: "灰色",
},
}
lion.Run()
fmt.Println(lion.Name)
}
匿名组合相当于以其类型名称(去掉包名部分)作为成员变量的名字。
Go语言中,要使某个符号对其他包可见,需要将该符号定义为以大写字母开头。Go语言中符号的可访问性是包一级别,而不是类型级别。
接口
Go语言中,一个类只需要实现了接口要求的所有函数,就说这个类实现了该接口。
type File struct {
// ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Close() error
type IFile interface{
IReader
IWrite
IClose
}
type IReader interface{
Read(buf []byte) (n int, err error)
}
type IWriter interface{
Write(buf []byte) (n int, err error)
}
type IClose interface{
Close() error
}
var file1 IFile = new(File)
var file2 IReader = new(File)
Go语言中任何对象实例都满足空接口interface{}。
并发编程
go channel
package main
import "fmt"
func Count(ch chan int){
fmt.Println("Counting")
ch <- 1
}
func main(){
chs := make([]chan int, 10)
for i :=0; i < 10; i++ {
chs[i] = make(chan int)
go Count(chs[i])
}
for _, ch := range(chs) {
a := <- ch
fmt.Println(a)
}
}
超时机制
timeout := make(chan bool, 1)
go func(){
time.Sleep(1e9)
timeout <- true
}()
select {
case <-ch:
//从ch中读取到数据
case <-timeout
//一直没有从ch中读取到数据,但从timeout中读取到了数据
}
channel的传递
type PipeData struct {
value int
handler func(int) int
next chan int
}
func handle(queue chan *PipeData){
for data := range queue{
data.next <- data.handler(data.value)
}
}
关闭channel
close(ch)
同步锁
var l sync.Mutex
func foo(){
l.Lock()
defer l.Unlock()
//...
}
全局唯一操作
var a string
var once sync.once
func setup(){
a = "hello"
}
func doprint() {
once.Do(setup)
print(a)
}
func twoprint(){
go doprint()
go doprint()
}
以上代码setup只会运行一次。
网络编程
Socket编程
Dial():连接,支持如下协议:tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6
conn, err := net.Dial("tcp", "192.168.0.2:2100")
conn2, err := net.Dial("udp", "192.168.0.2:53")
conn3, err := net.Dial("ip4:icmp", "www.baidu.com")
Write()与Read()方法来发送数据与接收数据。
HTTP编程
net/http包,包含HTTP客户端与服务端的具体实现。
resp, err := http.Get("http://example.com/")
if err != nil{
return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil{
return
}
if resp.StatusCode != http.StatusOK{
return
}
resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"artical body"}})
if err != nil{
return
}
resp, err := http.Head("http://example.com/")
req, err := http.NewRequest("GET", "http://example.com/posts", nil)
req.Header.Add("User-Agent", "Gobook User-Agent")
client := &http.Client{...}
resp, err := client.Do(req)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "hello")
})
RPC
net/rpc,包实现RPC协议的细节
func (t *T) MethodName(argType T1, replyType *T2) error
T1与T2默认会使用encoding/gob包进行编码与解码。
argType表示由RPC客户端传入的参数
replyType表示要返回给RPC客户端的结果
服务端示例
package main
import (
"log"
"net"
"net/http"
"net/rpc"
)
type Args struct{
A, B int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error{
*reply = args.A * args.B
return nil
}
func main(){
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil{
log.Fatal("listen error", e)
}
http.Serve(l, nil)
}
客户端示例
package main
import (
"net/rpc"
"fmt"
"log"
)
type Args struct{
A, B int
}
func main(){
// 顺序执行RPC
client, _ := rpc.DialHTTP("tcp", "127.0.0.1:1234")
args := &Args{7,8}
var reply int
err := client.Call("Arith.Multiply", args, &reply)
if err != nil{
log.Fatal(err)
}
fmt.Println(reply)
//异步执行RPC
var reply2 int
divCall := client.Go("Arith.Multiply", &Args{6,8}, &reply2, nil)
divCall = <-divCall.Done
fmt.Println(reply2)
}
JSON
encoding/json标准库
json.Marshal(),将一组数据进行JSON格式化编码
json.Unmarshal(),将JSON格式的文档解码为Go里边预期的数据结构。
data := make(map[string] string)
data["name"] = "Michael"
data2, err := json.Marshal(data)
if err != nil{
fmt.Println("err")
return
}
fmt.Println(string(data2[:]))
data := make(map[string] string)
data_json := []byte("{\"name\":\"Michael\"}")
json.Unmarshal(data_json, &data)
fmt.Println(data)
反射
type T struct{
A int
B string
}
t := T{203, "mh203"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}
Go工具
包文档查看(网站形式)
$ godoc -http=:8080
$ godoc -http=:8080 -path="."
包文档查看(命令行形式)
$ go doc foo
代码格式化
$ go fmt hello.go
$ gofmt -l -w .
项目构建
go build:用于测试编译包,在项目目录下生成可执行文件(有main包);不能生成包文件
go install:主要用来生成库和工具。一是编译包文件(无main包),将编译后的包文件放到 pkg 目录下($GOPATH/pkg)。二是编译生成可执行文件(有main包),将可执行文件放到 bin 目录($GOPATH/bin);生成可执行文件在当前目录下, go install 生成可执行文件在bin目录下($GOPATH/bin)
$ GOOS=linux GOARCH=amd64 go build calc
$ go install calc
GOOS:系统,linux/darwin/windows
GOARCH:386/amd64
单元测试
$ go test calc
模块管理go mod
$ go mod download # 下载指定模块
$ go mod init test.com/go #初始化当前模块
$ go mod vendor # 将依赖复制到当前vendor目录下
使用go module后,项目代码不需要放在GOPATH目录下。import项目的package时使用module路径。
以下是beego应用使用module来管理依赖的例子
$ cat <<EOF > go.mod
module beegoapp
go 1.12
replace (
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20191227163750-53104e6ec876
golang.org/x/mod => github.com/golang/mod v0.1.0
golang.org/x/net => github.com/golang/net v0.0.0-20191209160850-c0dbc17a3553
golang.org/x/sync => github.com/golang/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys => github.com/golang/sys v0.0.0-20191228213918-04cbcbbfeed8
golang.org/x/text => github.com/golang/text v0.3.2
golang.org/x/tools => github.com/golang/tools v0.0.0-20191230220329-2aa90c603ae3
golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20191204190536-9bdfabe68543
)
require (
github.com/astaxie/beego v1.12.0
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
github.com/smartystreets/goconvey v1.6.4
)
EOF