1、没有public protect private这种可访问范围的关键字,而是使用大小写来代替,首字符大写,则对包外可见;首字母小写,只有包内可见。
2、每行的结尾不强制要求加“;”,也建议不要加,除非在同一行上写多行的代码,才需要。
3、多重变量定义方式:
- var a int = 1
- a := 1
4、自动类型推测,a := "test",能自动推测出变量a的类型是string。
5、支持指针类型(跟C语言的指针差不多)。
6、有特殊的channel类型,倡导“不要共享内存来实现通信,而是用通信来实现内存共享”,channel就是通信的通道。
数组:
- 值类型,作为函数入参的时候,是copy一份数据,而不是传递原数组的引用。
- 数组初始化之后,长度是固定的,无法修改长度。
- 数组长度是Type的一种,[10]int和[20]int是不一样的类型。
切片(动态数组):
- 切片有长度len和容量cap的概念,长度是指已经被赋值过的最大下标+1,容量是指切片目前能容纳的最多元素个数(到达最大容量,继续追加数据,就会触发切换扩容,这又涉及到一个值得研究的问题:扩容是并发安全的么)。
- 引用类型,作为函数入参的时候,传递的是引用。
- 切片可以由数组或者另外一个切片产生,例如“s := arr[1:]”,这样切片s就会指向arr数组从下标1到结束的内容。当扩容的时候,就会脱离arr,指向新申请的地址。
- 切片可以通过内置的append方法追加内容
- 类似java的ArrayList。
声明数组的时候,[]内指定了长度或者...自动计算长度,就是数组;[]内没有内容就是切片。
参考文章:go语言中数组和切片的区别
8、接口:
go里的接口,不需要主动去实现,只要包含了接口定义的所有方法,就认为实现了这个接口。
如果定义一个没有任何方法的接口,则所有对象都实现了这个接口,这个接口类型的变量,能接受任何类型的值。
9、函数返回值可以有多个返回值,不需要像java一样,想返回多个值的时候,只能定义一个新的类,用这个类来存储值,方法返回这个类的对象。
10、go里的变量都有默认值,值类型的默认值是对应类型的0值,例如int32是0、string是"";引用类型的默认值是nil。
11、go里没有set,但是可以用Map来当Set使用。但是容易受0值的干扰。例如:
test_map := make(map[string]string)
a := test_map["test"]
这种情况,a的内容是"",无法判断是value就是"",还是没有test这个key
可以使用_, ok := test_map["test"],如果存在test的key,则ok=true,否则false。
删除key,用内置的delete方法delete(test_map, "test")。
12、常量定义关键字const,需要注意iota特殊常量~
可以看这个:Go 语言常量 | 菜鸟教程
13、if语句支持定义变量
if a := true; a {
// dosomething
}
14、switch
- case 可以有多个备选匹配值,例如:“case a==5, a==6,a==7”,当a是5、6、7里任意一个值的时候,都会匹配上
- go语言里的case语句,不需要在末尾加break,匹配成功后,就不会执行其他case
- 可以使用fallthrough,来实现匹配成功后,继续执行下一个case,但是不会判断case的条件
15、牛逼的select
有点像switch,实际效果像java的nio
- 每个 case 都必须是一个通信
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行,其他被忽略
- 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。否则:.
- 如果有 default 子句,则执行该语句
- 如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3n")
} else {
fmt.Printf("c3 is closedn")
}
default:
fmt.Printf("no communicationn")
}
}
16、defer(推迟),在函数体中使用defer语句,将在函数体结束的时候,执行defer的内容。适合用来实现资源的关闭,锁的释放
17、go使用的是协程,java使用的是线程
main方法运行在特殊的main goroutine,当main goroutine退出,其他gorountine也会退出。
如果运行go的机器是2核,则一般配置go启动两个2线程,相当于占用掉2个核。
使用goroutine的时候,就会创建一个协程,协程切换不涉及内核切换,都是在一个用户态中(还在同一个线程里,一个线程里的协程是串行的)。
线程的切换机制,是时间片用完了,就会切换。协程的切换时机有2种:
- 新的协程被创建
- 有协程的任务完成
gorountine相关runtime包:
- runtime.Gosched()让出运行权
- runtime.Goexit()退出goroutine,不会继续执行goroutine的代码,defer能触发
TODO:补上进程、线程和协程的差异
18、goto,go语言支持goto语句跳转到指定的label位置~自由又危险
19、go自带了依赖管理god.mod,不需要像java,需要额外依赖maven或者gradle
使用GoLand开发,默认是下载不了镜像的,要先去setting->Go->Go modules配置GOPROXY=https://goproxy.io
20、框架
- web框架—gin
- orm框架—gorm
21、Go默认的json序列化,时间默认的格式为RFC3339,即1993-01-01T20:08:23.000000028+08:00
但实际上我们一般接触更多的是1993-01-01 20:08:23
需要对time.Time进行一些包装:golang 自定义time.Time json输出格式 - 小风疏雨 - 博客园
二、待研究的内容1、自动的垃圾回收,可以去研究一下垃圾回收机制~
2、切片slice的扩容机制,是否并发下是安全的
3、go的Mutex包的实现原理
4、go的依赖管理
5、channel的实现原理
三、相关学习文档go的综合知识
GORM 中文文档
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)