脚本化的语法,容易上手。
静态类型+编译性,开发、运行效率都有保证
函数式 & 面向对象 两种编程范式,
原生支持并发编程支持,降低开发成本,维护成本,以及更好的兼容性,效率。
劣势:语法糖没有 Python 和 Ruby 多。运行效率不及C,但已赶超C++,Java。第三方库不多,就是轮子少(喜欢造轮子的可以加入golang轮子大军)。
官方: https://golang.org/
国内官方站点: https://go-zh.org/
http://golang.org/dl/ 下载最新Go语言二进制包
wget https://dl.google.com/go/go1.13.15.linux-amd64.tar.gz tar -C /usr/local -xzf go1.13.15.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin go version
GOROOT, GOPATH, GOBIN, PATH, 现在安装的最新golang runtiem都不用配置了环境变量了。
break //退出循环 default //选择结构默认项(switch、select) func //定义函数 interface //定义接口 select //channel case //选择结构标签 chan //定义channel const //常量 continue //跳过本次循环 defer //延迟执行内容(收尾工作) go //并发执行 map //map类型 struct //定义结构体 else //选择结构 goto //跳转语句 package //包 switch //选择结构 fallthrough //?? if //选择结构 range //从slice、map等结构中取元素 type //定义类型 for //循环 import //导入包 return //返回 var //定义变量
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
不要求缩进,不要求末尾加分号——;,同一行代码中有多个表达式,需要用 分号 分割。没有使用的变量,包,会导致报错。
每个go源文件开头必须是package开头,定义自己的包
一个目录下,只能有一个包名
一个可执行的文件必须要有 main()
函数
两种引入风格
import "package1" import "package2"
import ( "package1" pa2 "package2" // 包别名,别名为 pa2 . "fmt" _ "mysql" )
. "fmt"
方式引入包的化,使用fmt里面的函数就可直接使用,不用带 fmt 前缀了
如果引入的包不使用,会报错, 或者加个前缀 _ 即可,这样的下划线会把引入的包的init函数执行一下。定义的变量不用,也会报错。
定义 包内 初始化函数
func init() { }
只导入这个包部分,并运行init函数,由于导入不全,所以在代码中就不能使用这个包了。
import _ "MyPackage"
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括: (a) 指针类型(Pointer) (b) 数组类型 (c) 结构化类型(struct) (d) Channel 类型 (e) 函数类型 (f) 切片类型 (g) 接口类型(interface) (h) Map 类型 |
使用 int 时,根据当前操作系统来的,64位系统对应 int64, 32位操作系统,对应int32.
分组声明 var ( i int foo float32 name string ) 分组批量声明、赋值 var a,b,c,d int = 1,2,3,4 a,b := 1,2
特殊变量 _
var
声明,局部变量可省略作用域可以分为以下四个类型:
语句块是由花括弧({})所包含的一系列语句。
在 Go 中还有很多的隐式语句块:
断言,顾名思义就是果断的去猜测一个未知的事物。在 go 语言中,interface{} 就是这个神秘的未知类型,其断言操作就是用来判断 interface{} 的类型。
var foo interface{} = 22 f, ok := foo.(int) if !ok { t.Log("Guess wrong ...") } t.Logf("The type is : %T", f)
变量类型支持: bool, int, float, string
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | A + B 输出结果 30 |
- | 相减 | A - B 输出结果 -10 |
* | 相乘 | A * B 输出结果 200 |
/ | 相除 | B / A 输出结果 2 |
% | 求余 | B % A 输出结果 0 |
++ | 自增 | A++ 输出结果 11 |
-- | 自减 | A-- 输出结果 9 |
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 | (A == B) 为 False |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 | (A != B) 为 True |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 | (A > B) 为 False |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 | (A < B) 为 True |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 | (A >= B) 为 False |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 | (A <= B) 为 True |
运算符 | 描述 | 实例 | ||||
---|---|---|---|---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False | ||||
\ | \ | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A \ | \ | B) 为 True | |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
运算符 | 描述 | 实例 | |||
---|---|---|---|---|---|
& | 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 | |||
\ | 按位或运算符"\ | "是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A \ | B) 结果为 61, 二进制为 0011 1101 | |
^ | 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 | |||
<< | 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 | |||
>> | 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
运算符 | 描述 | 实例 | |||
---|---|---|---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C | |||
+= | 相加后再赋值 | C += A 等于 C = C + A | |||
-= | 相减后再赋值 | C -= A 等于 C = C - A | |||
*= | 相乘后再赋值 | C = A 等于 C = C A | |||
/= | 相除后再赋值 | C /= A 等于 C = C / A | |||
%= | 求余后再赋值 | C %= A 等于 C = C % A | |||
<<= | 左移后赋值 | C <<= 2 等于 C = C << 2 | |||
>>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 | |||
&= | 按位与后赋值 | C &= 2 等于 C = C & 2 | |||
^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 | |||
\ | = | 按位或后赋值 | C \ | = 2 等于 C = C \ | 2 |
优先级 | 运算符 | 功能 | ||
---|---|---|---|---|
9 | () [] -> . | 后缀运算 | ||
8 | ! *(指针) & ++ -- +(正号) -(负号) | 单目运算 | ||
7 | * / % + - | 算术运算,加减乘除 | ||
6 | << >> | 位运算 | ||
5 | == != < <= > >= | 逻辑运算、不等、等 | ||
4 | & \ | ^ | 按位 逻辑与、或 | |
3 | \ | \ | && | 逻辑或、与 |
2 | = += -= *= 等等 | 赋值运算 | ||
1 | , | 逗号 |
一元
、赋值
这两大运算符是 从右到左
关联,其他都是 从左到右
关联。
注意:优先级 值越大则优先级越高。为了方便理解、记忆,我对没有严格按照优先级制表,只是做了个大概!!
更详细的
var number int = 37 if number += 4; 10 > number { fmt.Print("less than 10:", number) } else if 10 < number { number -= 2 fmt.Print("greater 10:", number) } else { }
package main import ( "fmt" "math/rand" ) func main() { ia := []interface{}{byte(6), 'a', uint(10), int32(-4), "CC"} v := ia[rand.Intn(4)] // 值 switch switch v { case 'a' : fmt.Println("char: ", v) case 10 : fmt.Println("uint: ", v) case -4 : fmt.Println("int: ", v) case 0.1 : fallthrough caes "0.1" fmt.Println("float: ", v) default : fmt.Println("byte: ", v) } // 变量类型 switch switch interface{}(v).(type) { case string : fmt.Printf("Case A.") case byte : fmt.Printf("Case B.") case int : fmt.Printf("Case B.") default: fmt.Println("Unknown!") } }
注意,go语言和其他语言不同的时,每个case代码末尾会自动加上break
操作, 如果你需要使用 fallthrough
来抵消默认的 break
select 用于管道
是的 golang
的 for
集 for
,foreach
,for in
,while
于一体。
do while
表示:golang你这么绕,不优雅
package main import ( "fmt" "time" ) func main() { map1 := map[int]string{1: "Golang", 2: "Java", 3: "Python", 4: "C"} n := 1 for { // 省略则默认是true if n > 3 { break; } fmt.Println("for true map item: ", map1[n]) time.Sleep(1) n++ } for i := 1; i < 4; i++ { fmt.Println("for i map item: ", map1[i]) } for k,v := range map1 { fmt.Print(k, ":", v) } }
goto 是跳过代码块
package main import ( "fmt" "time" ) func main() { code: fmt.Println("do some thing~") time.Sleep(1) goto code }
break 跳出并结束循环
continue 跳过当前循环
虽然不能和PHP
那样break 2
跳出多层, 单只要有goto就能干很多事了。golang给 循环 就分配了一个 for,语句跳转语句却整了那么多花样
内建方法就是不需要引入包就能用的
make 可以创建 slice、map、chan,返回指针类型
一股c编程风格扑面而来, char ptr = (char )malloc(sizeof(char) * 5);
内存置0,返回传入类型的指针地址
package main import fmt import reflect func main() { mSlice := make([]string, 3) mSlice[0] = "dog" mSlice[1] = "cat" mSlice[2] = "pig" fmt.Println("animals: ", mSlice) mMap := make(map[int]string) mMap[10] = "dog" mMap['2'] = "cat" fmt.Println(reflect.TypeOf(mMap)) fmt.Println("animals :: ", mMap) nMap := new(map[int]string) fmt.Println(reflect.TypeOf(nMap)) }
slice可以使用copy,append 函数
delete 是专门用来删除 map
package main import "fmt" func main() { mSlice := make([]string, 3) mSlice[0] = "dog" mSlice[1] = "cat" mSlice[2] = "pig" fmt.Println("animals: ", mSlice) // append(mSlice, "id-3") // 这样写会导致报错: append(mSlice, "id-3") evaluated but not used mSlice = append(mSlice, "id-3") fmt.Println("animals update:", mSlice) fmt.Println("animals len :", len(mSlice)) fmt.Println("animals cap:", cap(mSlice)) // newSlice := make([]string) // 这样写导致报错:missing len argument to make([]string) // newSlice := make([]string, 2) // 这样写会导致数据丢失2个,不会自动扩容 newSlice := make([]string, 3) // 不要多次定义初始化:no new variables on left side of := copy(mSlice, newSlice) // 这样反向copy,会导致前面的几个数组元素被置为空 // copy(newSlice, mSlice) fmt.Println("animals dst:", mSlice) fmt.Println("animals copy:", newSlice) delete(mMap, 50) fmt.Println(mMap) }
异常处理
panic() 抛出异常
recover() 获取异常
报错会导致程序代码中断,不会再执行后续操作
package main import "fmt" import "errors" func panicFunc() { defer func() { // recover() message := recover() // 声明了message 变量就需要使用哦,不然报错 fmt.Println("panice msg: ", message) switch message.(type) { case string: case error: fmt.Println("panice error msg: ", message) default: } }() // panic("报错啦") panic(errors.New("I am error.")) } func main() { panicFunc() }
len可以计算 string, array, slice, map, chan
cap 可以计算 slice, map, chan
len()
获取数组长度cap()
获取占用空间分配close()
用于关闭管道——chan当声明一个数组时,go会预先分配一部分空间给当前数组,获取实际空间占用大小,使用cap()
不用像PHP那样,strlen(), count(), length 傻傻分不清楚了。
package main import "fmt" func main() { mSlice := make([]string, 3) mSlice[0] = "dog" mSlice[1] = "cat" mSlice[2] = "pig" fmt.Println("animals: ", mSlice) fmt.Println("animals update:", mSlice) fmt.Println("animals len :", len(mSlice)) fmt.Println("animals cap:", cap(mSlice)) mChan := make(chan int, 1) close(mChan) mChan <- 1 // 会导致报错: panic: send on closed channel }
定一个当前方法关闭时,运行的代码, 压栈设计,先声明的后执行。
package main import "fmt" type Dog struct { ID int Name string Age int32 } func main() { var dog Dog dog.ID = 1 dog.Name = "haha" dog.Age = 3 fmt.Println("print Dog Struct", dog) dog2 := Dog{ID:2, Name:"san", Age:4} fmt.Println("print Dog 2 Struct", dog2) dog3 := new(Dog) dog3.ID = 3 dog3.Name = "Tom" dog3.Age = 5 fmt.Println("print Dog 3 Struct", dog) }
输出
print Dog Struct {1 haha 3} print Dog 2 Struct {2 san 4} print Dog 3 Struct &{3 Tom 5}
/* define an interface */ type interface_name interface { method_name1 [return_type] method_name2 [return_type] ... method_namen [return_type] } /* define a struct */ type struct_name struct { /* variables */ } /* implement interface methods*/ func (struct_name_variable struct_name) method_name1() [return_type] { /* method implementation */ } ... func (struct_name_variable struct_name) method_namen() [return_type] { /* method implementation */ }
需要引入包 encoding/json
, 两个函数分别是 json.Marshal()
, json.Unmarshal()
.
注意,最后一个是英文字母小写的L
,不是1
package main import "fmt" import "encoding/json" type ServerInfo struct { SerName string SerIp string SerPort uint16 } func main() { server := new(ServerInfo) server.SerName = "http-nginx" server.SerIp = "127.0.0.1" server.SerPort = 8080 re,err := json.Marshal(server) if nil != err { fmt.Println("error: ", err.Error()) } else { fmt.Println("struct json bytes: ", re) fmt.Println("struct json string: ", string(re)) } mServer := make(map[string]interface{}) mServer["serverName"] = "apache2-http" mServer["serIp"] = "192.168.30.133" mServer["serPort"] = "3033" mRe,err := json.Marshal(mServer) if nil != err { fmt.Println("error: ", err.Error()) } else { fmt.Println("map json string: ", string(mRe)) } }
输出
struct json bytes: [123 34 83 101 114 78 97 109 101 34 58 34 104 116 116 112 45 110 103 105 110 120 34 44 34 83 101 114 73 112 34 58 34 49 48 46 49 48 48 46 49 55 46 50 55 58 51 48 48 48 49 34 44 34 83 101 114 80 111 114 116 34 58 56 48 56 48 125] struct json string: {"SerName":"http-nginx","SerIp":"10.100.17.27:30001","SerPort":8080} map json string: {"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}
ps: 我也不知道 10.100.17.27:30001
是怎么回事
可以使用 tag 来做 mapping,
package main import "fmt" import "encoding/json" type ServerInfo struct { SerName string `json:"name"` SerIp string `json:"ip"` SerPort uint16 `json:"port"` } func main() { // jsonStr := "{\"SerName\":\"http-nginx\",\"SerIp\":\"10.100.17.27:30001\",\"SerPort\":8080}" \\ 双引号注意转义 jsonStr := "{\"name\":\"http-nginx\",\"ip\":\"10.100.17.27:30001\",\"port\":8080}" sServer := new(ServerInfo) jsonBytes := []byte(jsonStr) uerr := json.Unmarshal(jsonBytes, &sServer) if nil != uerr { fmt.Println("error: ", err.Error()) } else { fmt.Println("uns struct: ", sServer) } jsonStr3 := `{"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}` \\ 使用键盘1旁边的 ` 符号包裹双引号就不用转义了 uSer := make(map[string]interface{}) uErr := json.Unmarshal([]byte(jsonStr3), &uSer) if nil != uErr { fmt.Println("error: ", uErr.Error()) } else { fmt.Println("unmar map: ", uSer) } }
输出
uns struct: &{http-nginx 10.100.17.27:30001 8080} unmar map: map[serIp:192.168.30.133 serPort:3033 serverName:apache2-http]
tag
这个东东把,就是json的别名,感觉这个功能是go的特色,与encoding/json
包紧密结合。
为什么会有这个东西,我估计是这个和 go命名规则 有关,go命名规则,要求public的变量开头要大写,小写开头的变量是private的,所以,json中的变量就会影响一个接口体变量的访问权限,为了不像java那样复杂,提供了方便的tag功能。
package main import "fmt" import "encoding/json" type ServerInfo struct { SerName string `json:"name"` SerIp string `json:"ip"` SerPort uint16 `json:"port"` } func main() { server := new(ServerInfo) server.SerName = "http-nginx" server.SerIp = "127.0.0.1" server.SerPort = 8080 re,err := json.Marshal(server) if nil != err { fmt.Println("error: ", err.Error()) } else { fmt.Println("struct json string: ", string(re)) } }
输出
struct json string: {"name":"http-nginx","ip":"10.100.17.27:30001","port":8080} map json strin
这就好比是Linux 里的 /dev/null
, 由于go语言要求声明的变量必须被使用,返回的变量必须被接收,那么真有个变量没用但必须要接受怎么办呢,就把返回的参数给他。例如:
package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for _, v := range pow { fmt.Printf("value is %d\n", v) } }
这里我们只要值,不要key的信息,返回的key不能不收不是,但我也不像把它输出出来,就让 _
来接收好了。
引入包, 并不直接使用这个包,运行时执行一次它的 init()
函数,
import ( _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/gorm" )