Go语言做Web编程非常方便,并且在开发效率和程序运行效率方面都非常优秀。相比于Java,其最大的优势就是简便易用,而相比于PHP,它最大的优势就是性能好。
(go做web)推荐Gorilla的库,里面的路由,csrf的包用起来都很方便。
如果你要使用Go语言做Web后端开发,我推荐你用Beego。如果你对性能有超高的要求(不想因为用了框架而降低一点点性能),我推荐你用Gin。
Go语言要求public的变量必须以大写字母开头,private变量则以小写字母开头
代码部分并未格式化!!
go get 获取远程包(需提前安装git)
go run 运行
go build 测试编译(package main 的文件)
go fmt 格式化代码
go install 编译包文件和整个程序
go test 运行测试文件(**_test.go是测试文件,命名自己文件的时候需要注意)
go doc 查看文档(本地官网 go -http=:8080 & 后台执行)
注释:
//
/* */
go结构
var 全局变量
type 基本类型 比如:type newtype struct/interface{(内容)}
packagename.funcname包中方法的调用
改包名:
import abc "fmt"/import . "fmt"
调用的时候
abc.Println/Println
不推荐使用.那种,容易混淆
首字母大小写决定 常量,变量,类型,接口,结构 是否可以被外部调用
函数名首字母小写=private
函数名首字母大写=public
问:声明多个全局变量,常量,结构体能不能像import那样一起弄呢?
答:可以,组。只能全局变量,函数体中不可以
type(
newtype int
type1 string
)
函数体中aaa, bbb = 1, 2 就可以了
a, b, c, d := 1, 2, 3, 4
(注意::=这种声明方式不能使用在函数外,函数外的每个语法块都必须以关键字开始。)
类型零值:某种类型的默认值,eg:int--0,bool--false,string--空字符串:“”,数组不给类型--空数组[] 数组指定int(var a []int)--[0]
类型别名type(byte int8)奇奇怪怪的东西,给类起个自己喜欢的名字 ┑( ̄Д  ̄)┍
var b (int可要可不要) = 1 声明的同时赋值
如果不加类型,系统会自己判断的,如果后面要用的类型和现在声明的时候不一样,声明的时候最好标出之后想使用的类型
d := 456
_下划线,空白标识符,可以忽略
常量枚举
const(
a=‘A‘
b
c=iota
d
)
output: 65 65 2 3
第四讲,最后讲了移位运算,感觉短时间用不到,马一下有空再看
用 . 来操作
&
默认值nil 而非NULL
正常运用:
a:=1
var p *int = &a
fmt.Println(p)
//output:addr
fmt.Println(*p)
//output:1即a
a++,a-- 不能给参数赋值,必须单独放一句
--,++也不能放在变量左边
没有括号,空格分隔
if a := 1 ; a > 1
//a为局部变量
if a, b :=1, 2; a > 0
//判断多个
注:条件语句中建议不使用函数;左大括号需要和语句在同一行
三种for循环
a:=1
for{
a++
if a > 3 {
break
}
fmt.Println(a)
}
fmt.Println("over")
a:=1
for a <= 3{
a++
fmt.Println(a)
}
fmt.Println("over")
a:=1
for i:= 0; i < 3; i++{
a++
fmt.Println(a)
}
fmt.Println("over")
switch 不用break,匹配自动停,如果匹配还想继续,要fallthrough
支持初始化表达式,右侧要分号(switch a := 1; {...)这个初始化 都是局部变量,出了switch就不能用了
配合标签名使用
break 和 continue 可跳出多层循环(结合label使用,label在哪级就可以跳到哪级)
问题是:continue换成goto会怎么样,--无限循环
数组长度也算是类型
var a [2]int
var b [1]int
a =b 不合法
a := [...]int{0:1,1:2,2:3}
//初始化可以不给定元素个数,索引也可以只指定某一个值
var p [100]int = &a 指向数组的指针
a := [...]int{&x,&y} 一个数组保存了两个变量的指针
两种传递方式:
数组之间可以 == 或者 !=(看上面,不同类型的数组不可以做任何直接比较)
指向数组的指针(new的返回值是指针,一般不new)
p := new([10]int)//输出也是指向数组的指针
多元数组(最外层最好定义好每个数组长度,不要用...)
a := [2][3]int {
{1,1,1}
{2,2,2}
}
new和make的区别:
func new(Type) *Type
# 返回一个指针
func make(Type,size,IntegerType) Type
# Type必须是引用类型(Slice,Map,Channal等)返回一个对象,而非指针
在slice上slice
越界会报错
在slice尾部追加元素,或者追加另一个slice(很像拼接两个数组)
如果超过cap,会拷贝原始数据
第一次追加后,输出的地址不变,因为没超过cap,第二次追加后地址变,超过了的话会拷贝到重新分配的内存处
key-value 比线性查找快,但比索引(数组和slice)慢很多
key必须是可以比较的,slice,func不可以
声明: var m map[int]string /m: = make(map[int]string)
赋值:m[1] = "ok"//key=1,value="ok"
删除: delete(m,1)
var m map[int]map[int]string
m=make(map[int]map[int]string)//只初始化外层map
m[1] = make(map[int]string)
m[1][1] = "ok"
a:= m[1][1]
输出a为ok
迭代
for range(类似for each)
一般形式:for i,v := range slice {输出i,v},相当于枚举,v是元素值,但是是拷贝,不能修改slice本身的元素值,但可以修改slice[i]
for k,v := range map{输出键值对,但都是对拷贝值的操作,map[k]才可以改变map中值}
map[k]
有一个传说中很牛逼的例子
func main(){
sm := make([]map[int]string,5)//以map为元素的slice
for _,v := range sm {
v= make(map[int]string,1)
v[1] = "OK"
fmt.Println(v)//打印map[1:ok]
}
fmt.Println(sm)//打印空map,因为v改变不影响slice
}
输出结果map[1:OK]map[1:OK]map[1:OK]map[1:OK]map[1:OK][map[] map[] map[] map[] map[]]
果然很牛逼的例子,有点绕
应用:可以通过对mapkey排序从而达到给map排序
把key转为int放在一个slice里然后sort.ints(s)
作业:把map中v,k对调,
解:for k,v := range m1{
m2[v] = k}
声明:如果参数类型一样(参数和返回值处type1=type2,可以只写最后一个;返回值可以不起名) func funcname (para1 type1, para2 type2) (returnvalue1 type1, returnvalue2 type2){
}
a := func(){
fmt.Println("Func A")
}
a()
a是后面那个函数的类型的变量,那个函数叫匿名函数
需要编程基础和对闭包的理解
闭包
另一个闭包例子,看懂一个就都能看懂了
返回一个匿名函数
for i:=0; i<3;i++{
defer func(){打印i}
}
//输出210,i作为参数传递进去
for i:=0; i<3;i++{
defer func(){
打印i
}() //这个括号是类似defer a()
}
//输出333,闭包,i一直作为引用
go中的异常处理机制,类似finaly--panic/recover 模式来处理,panic可以在任何地方引发,recovery只有在defer中才有效
panic(),执行的话程序会终止
作业:
先输出的是fs[i],然后用下一个for循环输出fs这个slice的值,i为外层for循环中i的引用地址,执行完第一个for循环,i=4,所以第二个for循环输出的都是=4
然后执行第一次第一个for循环中的第二个defer,第一个defer是值拷贝,所以值被修改了。。。其实并不是很懂┑( ̄Д  ̄)┍
meStruct := new(SmallSoho) //初始化一个空的结构体,返回指针
meStruct := &SmallSoho{} //同上
meStruct := SmallSoho{} //返回一个空结构体
meStruct := &SmallSoho{Name:"SmallSoho"} //也可以键值对这样来初始化
推荐: 对struct初始化的时候习惯加上取地址符eg:&person,方便后续操作
a := &struct {
Name string
Age int
}{
Name: "Joe",
Age : 19,
}
fmt.Println(a)
嵌套结构体(像继承),匿名结构
如果想设置sex,a := teacher{Name: "joe",Age:19,human:human{Sex:0}}
如果想修改
a.Name = "Joe2"
a.Sex = 100//注意,这里直接加sex就可以了
func(a A)print(){}
调用的时候 a.print
A类型的a是接收者
同级名称不能冲突
调用最先找到的方法
方法访问权限问题:
同一个包里:方法可以访问struct的私有和公有字段
作业:
接口
若某个具体类型实现了某个接口,则: 这个具体类型的值既可以当做该接口类型的值来使用, 也可以当做该具体类型的值
注解:指针方法集,如果传进来指针,可以调用reveicer是指针和不是指针的方法,如果传进来的是非指针方法集(拷贝的)不能调用指针方法解,所以接口的receiver不做自动转换,因为是拷贝的
(开始有点听不懂了,目前看代码阶段,估计了解几个基本方法就会跳过,以后再看)
reflect包中field 反射出结构信息
反射
Go语言实现了反射,反射就是动态运行时的状态。我们一般用到的包是reflect包。
使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。
这两种获取方式如下:
t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素
v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签
name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
动态调用方法
goroutine 超级线程池
每个实例4-5k,轻便
并发不是并行:并发是由切换时间来实现“同时”运行,并行是多核多线程
goroutine 通过通信来共享内存,而不是共享内存来通信
一个基本的并发??
//引入time包
func main(){
go Go()
time.sleep(2*time.second)//暂停两秒
}
//加一个东西,使函数和main可以通信
func Go(){
fmt.Println("gogogo")
}
判断channel是否关闭x, ok := <-ch //看ok的值
复杂点的channel
var m map[string] chan bool
// key--string, val--bool,这个map是channel
作业:
初始化的时候注意,不设置cap的话,设置的元素个数就是cap,超过cap就不是引用传递了,变成了拷贝
保险起见,加返回值,如图:
format里最好是拷贝const,不然有坑
引用传递
for range 启动goroutine
虽然看完了,但是接口复杂用法,reflect除typeof,valueof外其他函数,并发后半节,都有不太懂的地方,,以后再战, 先下手项目去啦(≧▽≦)/
最后提一点错误处理,go中强制返回所有err,然后自己决定怎么处理,项目中返回一个正常返回值加一个err的情况比比皆是。个人觉得算是go的优点。
goroutine替代原来的线程概念成为最小的调度单位。一旦运行goroutine时,先去当线程查找,如果线程阻塞了,则被分配到空闲的线程,如果没有空闲的线程,那么就会新建一个线程。
注意,当goroutine执行完毕后,线程不会回收推出,而是成为了空闲的线程。
原文:https://www.cnblogs.com/cookie1026/p/9149683.html