Java转Go后端开发Landing记录
基础语法
发现一个特别好的项目,知识点总结的详细到位 GitHub - golang101/golang101: Go语言101 : 一本侧重于Go语言语法和语义的编程解释和指导书
新知识点
类型
除了常见的各种类型
还有func()类型, 表示一个函数
变量
Go 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
Golang命名规范 - Go语言中文网 - Golang中文社区
Go语言中的字符串字面量使用 双引号 或 反引号 来创建
- 双引号用来创建 可解析的字符串字面量(支持转义,但不能用来引用多行);
- 反引号用来创建 原生的字符串字面量,这些字符串可能由多行组成(不支持任何转义序列),原生的字符串字面量多用于书写多行消息、HTML以及正则表达式
常量
常量表达式中,函数必须是内置函数,否则编译不过
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
分支语句
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。
switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
注意:Go 没有三目运算符,所以不支持?:形式的条件判断。
循环
-
for语句3种形式
for init; condition; post { }
for condition { }
for { }
-
range
For-each range
循环 -
在多重循环中,可以用标号 label 标出想 break 的循环。
在多重循环中,可以用标号 label 标出想 continue 的循环。 -
Go 语言的 goto 语句可以无条件地转移到过程中指定的行。
导出名
在 Go 中,如果一个名字以大写字母开头,那么它就是已导出的。例如,Pizza 就是个已导出名,Pi 也同样,它导出自 math 包。
感觉类似于Java里面的public和private的区别
函数
func function_name( [parameter list] ) [return_types] {
函数体
}
-
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
-
参数省略: 当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略。
- 函数作为另一个函数的实参: 参数传递实现回调等
- 闭包: Go 语言支持匿名函数,可作为闭包。匿名函数是一个”内联”语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
- 方法: 类似于Java类方法
多值返回
函数可以返回任意数量的返回值。
命名返回值
return结尾
直接返回所有的入参
defer推迟
在return之前执行, 所有defer放入栈里, 后进先出执行
函数的闭包
- 有点类似Java里面的匿名实现类的对象,
- 最外层函数的变量, 类似于Java的对象属性,
- 最外层函数的变量,在内层函数中是可见的, 且生命周期是外层函数变量的声明周期相同
双赋值
var a,b = map[key]
数组
var variable_name [SIZE] variable_type
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
- 多维数组
- 向函数传递数组
切片的切片, Slice
指针
- 声明指针
var var_name *var-type
- var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针.
- 当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。
- 指针数组
- 指向指针的指针: 只能套娃一层
- 向函数传递指针参数: 类似Java的引用传递
错误处理 panic/recover
方法接受者
就类似Java里面,这个方法是属于哪个类的
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
接口
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
- Interface{} 空方法的接口可以代表任何类型
- 鸭子类型(Duck Typing)
“如果它走路像鸭子,并且像鸭子一样嘎嘎叫,那它一定就是鸭子”。在 Go 中就是这样的:无需定义某种结构体是否实现了给定的接口,只要这个结构体具有与给定接口相同的方法签名,那它就是实现了这个接口。这非常有用,作为代码库的调用端,你可以定义外部库结构体所需的任意接口。而在 Java 中,对象必须显式声明实现了哪些接口。
Channel 管道 通道
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
- select
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
协程 Goroutine
先看第一个,讲明白了协程是什么
内存布局
存在内存对齐机制, 同样的结构体可能由于字段顺序导致内存占用差别较大
对象复用机制
sync.Pool()
GO的GC
- 待学习
反射
- 待学习
模块 Go Modules
https://segmentfault.com/a/1190000021854441
golang 提供了 go mod命令来管理包
go.sum 文件来记录 dependency tree
依赖管理
依赖管理不断演变中
命令行工具
检测执行竟态条件go run -race xxx.go
检查单元测试代码覆盖率
协程和延迟调用的实参的估值时刻
一个协程调用或者延迟调用的实参是在此调用发生时被估值的。更具体地说,
- 对于一个延迟函数调用,它的实参是在此调用被推入延迟调用堆栈的时候被估值的。
- 对于一个协程调用,它的实参是在此协程被创建的时候估值的。
- 一个匿名函数体内的表达式是在此函数被执行的时候才会被逐个估值的,不管此函数是被普通调用还是延迟/协程调用。
实战开发-比Java好的
- 创建结构体类型的变量, 内置语法支持设置其中字段, 无需像Java一样手写构造函数, 或者调用一堆set方法
- 内置单元测试支持, 约定大于配置的方式指定测试文件名/方法名, 执行命令即可直接运行测试, 还可以检测覆盖率
- 很多场景都有约定大于配置的思想, init方法, handler.go, 单元测试, tag,
- 内置简洁web服务支持
- 内置包管理, 依赖更新基于git仓库, 秒级更新依赖, 开发效率超高, 比maven效率提高太多
- 函数多值返回, 无需再为了返回多个字段, 封装Java类
- 变量默认零值, string是空字符串, struct/map/pointer是nil
- 空集合range遍历, collection[index]操作, 均无需判nil
- 字符串判断相等也可以用==, 统一符合人的直觉, 字符串零值是””而不是null
- 拒绝循环引用, 编译检查发现了会报错: import cycle not allowed, 保持结构可维护性
- 异常处理主要依靠error返回值, 类似于Java的检查型异常,Java项目开发中容易catch在一大段代码中,不明确是哪里抛出的, 而go的panic相当于Java的运行时错误, 作为兜底的异常处理机制, 是要避免出现的
- switch语法更加灵活, 还可以不指定变量, 当做if/else分支使用
- for和if等分支语句前,可以增加赋值语句
- 协程, 最大程度利用CPU, 状态简单,协程切换负担小
- defer延迟函数调用, 依靠单独的延迟调用栈实现
- 没有隐式类型转换, 当发生64位数值向32位数值转换时, 开发人员需要明确有没有溢出风险
- 编译非常快, 开发效率很高, 秒级启动服务, 不像spring服务启动要加载很久
和Java相比没有的
- 继承/多态, go不是面向对象语言
- 泛型, 据说官方计划会支持
- 注解, Java的注释还是特别好用的, go的
- 三目运算符, 其实可有可无
- go的命名规则推崇缩写, 基本是java禁止的命名规则, 比如Request的变量命名r, 一开始项目代码看得我相当懵逼, 都是这样的命名strconv.Itoa(),其实是string convert integer to ASCII string, 这谁能看出来
- 不像Java一样,重名类较少,直接搜索类名即可定位代码, go项目存在很多重名文件比如handler.go存在几十个, 搜索定位代码时, 比较不方便, 可以在goland用symbol定位, 可以定位到特定方法名变量名, 但整体定位代码速度还是不如Java项目快
- 还没发现类似Java对集合排序传入一个Comparetor的方便的方式
其他一些了解的框架
- Ginex: web服务框架
- Hertz: web服务框架
- Kite: RPC框架
- Gorm: ORM框架
《GORM 中文文档 v1》| Go 技术论坛
《GORM 中文文档v2》 | Go 技术论坛 - goredis: redis操作框架
GitHub - astaxie/goredis: A simple, powerful Redis client for Go - consul: Consul 使用手册 | 一个梦
- confd: confd的安装与使用_菲宇运维-CSDN博客_confd
- dbuf: GitHub - zieckey/dbuf: A simple library to manage resources which can be upgraded gracefully
- etcd: 分布式一致性kv存储系统
开发规范
- 为什么go变量名那么喜欢缩写?, 例如response , 只写一个r, context写ctx之类的, 其实全看语言作者的个人爱好
- 没有用go module的项目,必须放在GOPATH下才能编译过
环境搭建
跟着公司的新人攻略走一遍
上手项目
拿一个简单项目练手, 本地启动成功, 发送请求查看日志
网友评论