最近有需求做后端,技术选型选择主力开发语言,于是乎从Python,Go,Java,PHP中做选择,经过测试和调研
- Go,天生高并发支持,语法简洁,C系列有语言基础
- 占用内存和CPU资源小,创业公司机器紧张哈
- 效率超然
通过wrk测试,递归测试,字符串拼接测试
Go完美胜出,无论是内存占用,还是处理速度,都位列第一。编译型的语言就是强 233
IDE选择
强烈推荐IntelliJ IDEA,下载之后安装Go语言插件(直接在IDEA的插件里面在线安装)
让我们从0开始
先记住几个点
-
Go没有char,所有的字符都用rune(跟int是一个)代替,输出需要手动转string
-
Go语言的大括号必须放在一句话的后面
/*这是一个Hello World,不经意间你就学会了*/ package main import ( "fmt" ) func main() { fmt.Println("Hello World"); }
Go语言只有一个入口,main包下的main函数
-
Go语言函数内不能声明不用的变量和包名,编译器会报错,但是在函数外面是可以的
package main import ( "fmt" "net/http" //Error,因为下面没用到 ) const a = 3 //虽然没用但是可以 var b = 4 //同上 func main() { var str string //Error,因为下面没用到 fmt.Println("Hello World") }
-
Go语言不用写分号除非你想在一行里面写多条语句
-
Go不支持静态变量,并没有static
-
Go语言的变量和函数的公有私有靠首字母大小写区分,首字母大写是共有的,小写是私有的
fmt.Println(str); //这是一个公有的函数,因为P大写了,变量也是一样的
-
Go语言的定义变量,变量类型写变量后面
package main import ( "fmt" ) func main() { var str string = "Hello World" fmt.Println(str); }
-
Go没有try/catch,finally的功能靠defer关键字实现 (稍后解惑)
-
Go函数可以有多个返回值 (稍后解惑)
变量定义
const IP = "127.0.0.1" //常量用const
var str1 string = "small"
str2 := "solo" //这里go会自动解析出类型
var str3 = "smallSohoSolo" //同上
这些方式都是等价的
四种方式定义,其中第三种
str2 := "solo"
只能在函数内部定义,不能在函数外部
package main
import (
"fmt"
)
global := "small" //这个不行!!!!!!!
func main() {
fmt.Println("Hello World")
}
还有更爽的方式
str1,str2,str3 := 1,2,3
var str1,str2,str3 = "1","2","3"
var str1,str2,str3 string = "1","2","3"
以后很地方会用到,函数也可以一次返回多个返回值
PS:int在32位机器当前都是32位,未来可能在64位机器上变成64位,如果要指定准确的字长,那就用int8,int16等手动指定
另外不同类型的值是不能相加的(吐槽一下Java,Java会自己默默地转然后error = =)非常强的类型比对,非常适合大型企业项目
流程控制
if条件句
a := 1
if a == 1 {
//true
} else if a == 2{
} else {
}
另外if支持在条件句中申明一个变量
if a:= 1; a == 1 {
//true
}
for循环
for i := 0; i < 10; i++ {
//循环体
}
另外并没有while,用for解决
i := 0
for i != 10 {
i++;
}
for的遍历写法,下面是遍历一个map和array
for k,v := range map {
//循环体
}
list := []int{1,2,3,4,5}
for index,val := range list {
fmt.Println(index,val)
}
goto走一波
i := 1
Here:
if i == 1 {
i = 2
goto Here
}
switch不用break自己就中断了,想要接着执行就
var i = 3
switch i {
case 1,3:
fmt.Println("123")
fallthrough
case 2:
fmt.Println("789")
default:
fmt.Println("456")
}
函数
函数可以有多个返回值,支持闭包,func后面跟的第一个括号是入参,第二个括号是返回值,可以有多个哦
//第一种
c,d := func (a int,b int)(c int,d int) {
c = a + b
d = 4
return
}(1,2)
fmt.Println(c,d)
//第二种
c := func (a int,b int)(c int) {
return a +b
}(1,2)
fmt.Println(c)
//第三种
c := func (a int,b int)(int) {
return a + b
}(1,2)
fmt.Println(c)
//第四种
c,d := func (a int,b int)(int,int) {
return a,b
}(1,2)
fmt.Println(c,d)
任意搭配总有一款你喜欢的,当然不用闭包也是可以的,并不强制
函数也是可以作为值和类型传进来的
type study func(int) bool //type 给函数个类型名,类似宏 函数的样子
func getAd(fun study) {
//函数体
study(3)
}
同样函数也支持变长参数
func getAd(arr ...int) int {
return len(arr)
}
*和&
两者同c一样
- *是代表指针,也可以从地址中获取内容
- &获取一个对象的地址
i := 1
point := &i // point等于一个地址
data = *point //data == 1
另外函数中要分清楚值传递和指针传递,这是一个交换函数,很清晰
func change(a *int,b *int) {
c := *a
*a = *b
*b = c
}
a := 3
b := 4
func(&a,&b)
结构体
学习过C语言的同学肯定都知道结构体,没错,就是那个东西 0.0,结构体让Go有了面向对象的概念
type SmallSoho struct {
Name string //公有变量大写
Sex string
kg int //私有变量小写
}
如何申请一个结构体
meStruct := new(SmallSoho) //可以申请一个空的结构体,返回一个指针
meStruct := &SmallSoho{} //同上
meStruct := SmallSoho{} //返回一个结构体,是类型的
meStruct := &SmallSoho{"small","男",1} //可以初始化
meStruct := &SmallSoho{Name:"SmallSoho"} //也可以键值对这样来初始化
注意这里的传递问题,在任何地方*val表示传递指针,val表示值传递,什么是值传递,什么是指针传递请自行百度^_^
注意最后两种
meStruct := &SmallSoho{"small","男",1} //可以初始化
meStruct := &SmallSoho{Name:"SmallSoho"} //也可以键值对这样来初始化
上面的那种需要传递所有的参数才可以,不然编译不通过
下面的键值对形式可以传递部分参数
数据结构
Go语言有:数组,Slice(划分),map,channel(并发编程应用)
数组就是跟其他语言一样的数组,没有什么特别的,并且长度只能写死,不能通过变量来定义
list := [10]string{}
list := [10]int{1,2,3,4,5,6,7,8,9,10}
list := []int{1,2,3}
list := [3]int{1} //后面两个默认是0
slice是划分,也就是可以通过数组得到slice,其中每一个元素都指向数组的一个元素,也可以通过make来为划分申请内存
list := []int{1,2,3}
slice := list[0:3] //左闭右开,不支持负数索引
上述情况slice的每一个元素都指向了list,修改任意其一的元素,两者均变化
slice := make([]int,4)
也可以为slice申请一块内存,这样就可以做到动态数组
map的使用方法同上,是一个映射,具体更多的参数请参考文档
m := make(map[string]int)
m["small"] = 1
channel是并发编程中用到的,附上一篇文章来看,国外大神的译文
面向对象####
基于结构体,我们可以给结构体上设置很多东西
函数名称前面可以设置函数附属的结构体,也就是绑定方法
私有方法和共有方法同样使用大小写区分
type SmallSoho struct {
Name string //公有变量大写
Sex string
kg int //私有变量小写
}
func(*SmallSoho) GetName()(string) {
return "smallSohoSolo"
}
func(*SmallSoho) GetSex()(string) {
return "man"
}
func(SmallSoho) getkg()(int) { //注意这里的SmallSoho没加*,意味着是值传递,并且小写是私有方法
return SmallSoho.kg
}
构造函数如何设置?
因为Go不支持重载,所以类似Java的那种多种类型的构造函数需要使用工厂模式来进行实现(具体工场请自行百度)
但是Go支持简单的构造
meStruct := &SmallSoho{"small","男",1} //可以初始化
meStruct := &SmallSoho{Name:"SmallSoho"} //也可以键值对这样来初始化
上文提到的简单的初始化时可以的,不过里面不能存有逻辑,逻辑需要使用工厂模式来做
静态方法和属性呢?
Go不支持static所以也就没有这类东西,静态的方法请自定义一个函数直接调用,静态的属性请在包中定义一个变量使用。
私有方法公有方法?
老生常谈,大小写区分,这个大小是语言层面强制,小写的方法和属性就是外部拿不到的,会error
继承
每一个结构体中可以存在匿名域
package main
import "fmt"
type C struct {
Name string
sex string
}
type D struct {
C
}
func main() {
d := &D{C{}}
fmt.Println(d.Name)
}
这里C就是一个匿名域,匿名域可以存在没有交集的多个,匿名域可以直接点出属性和方法
但是匿名域只能存在没有交集的多个,如果有两个匿名域,并且这两个匿名域有交集,那么通过点语法会无法通过编译,除非你指定他的所属,这时候点语法会在调用交集的时候无法通过编译,举个例子:
package main
import "fmt"
type C struct {
Name string
sex string
}
type E struct {
Name string
}
type D struct {
C
E
}
func main() {
d := &D{}
d.E.Name = "da" //合法的
fmt.Println(d.E.Name)
//d.Name = "da"
//fmt.Println(d.Name) //不合法,因为她不知道你所要指定的是哪个
}
这里推荐使用单继承模式
接口
接口的含义跟Java一样,Go中不需要显示的去implement一下
我们定义和实现一个接口
package main
import "fmt"
type C struct {
Name string
sex string
}
type E interface {
say()
}
func (*C) say() {
fmt.Println("123")
}
func main() {
c := new(C)
c.say()
}
上面的可能显示的不是很到位,再举个栗子,我们模拟一个点击监听
package main
import "fmt"
type C struct {
Name string
sex string
}
type E interface {
click()
}
func (*C) click() {
fmt.Println("click~!~!~!")
}
func main() {
c := new(C)
Touch(c)
}
func Touch(e E) {
e.click()
}
是不是很清晰,因为实现了接口,所以他可以被传递进来,另外接口在go中其实也是一个指针,是可以=赋值的,他可以指向一个实现了接口的实例,举个例子
package main
import "fmt"
type C struct {
Name string
sex string
}
type E interface {
click()
}
func (*C) click() {
fmt.Println("click~!~!~!")
}
func main() {
c := new(C)
var inter E
inter = c
Touch(inter)
}
func Touch(e E) {
e.click()
}
包
package main
我们都看到了这行
包的概念类似Java,只不过包名是可以和文件名不一样的(虽然没人这么写)
一个go app中只能有一个main包,其中存在唯一一个main方法,没有返回值也没有入参
每一个包还可以有init方法,虽然init方法不限制个数,不过init方法的调用顺序不固定,所以推荐一个包中只写一个init方法,init方法会在包被import过来的时候自动执行,并且只会执行一次,具体调用顺序如图
package main
import "fmt"
func init() {
}
func main() {
fmt.Println("Hello world")
}
错误处理
go的并没有try catch finally
我们需要在return中返回err在外部获取判断
cli,err := mysql.connect()
if(err != nil) {
//处理错误
}
有些东西需要在finally中处理,比如关闭mysql连接,使用defer来进行操作,defer的语句会在函数结束的时候执行,并且一定会执行。
cli,err := mysql.connect()
defer mysql.close()
if(err != nil) {
//处理错误
}