目录标题
一、参考
二、基础语法
(一)行分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成
。
如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。
(二)注释
注释和Java的一致。注释规范(可以使用01笔记的注释工具控制)
- 单行 //
- 多行 /**/
// 单行
/**
* 多行
**/
(三)命名规范(* 影响封装性)
1、go默认命名规则
Go在命名时以字母a到Z或a到Z或下划线开头,后面跟着零或更多的字母、下划线和数字(0到9)。Go不允许在命名时中使用@、$和%等标点符号。Go是一种区分大小写的编程语言。因此,Manpower和manpower是两个不同的命名。
- 当命名(包括常量、变量、类型、函数名、结构字段等)以大写字母开头,如:Name,
这种以大写开头的命名形式,在go中默认是可以被其他包使用的(在java中是public的)
- 小写就是对其他包不可见的,
go默认将小写开头的变量、函数等默认为私有的,只能自己使用(对应java中的private)
2、包命名规则
.go文件 一般在哪个文件夹包名就叫什么,有mian函数的包名一定是main(使用小写,不要包含下划线)。
3、文件命名
.go文件 小写单词,并使用下划线分割单词。
4、结构体
使用驼峰命名法,根据封装类型选择首字母大写,还是小写。
5、接口
与结构体一致,当接口是当函数接口时,命名一般以er结尾
type Reader interface {
read(id int) int
}
6、变量命名规则
同样使用驼峰命名,根据封装需求,决定第一个字母大小写。
布尔类型,一般以Has、Is,Can等开头。
7、常量命名规则
全大写,以下划线分割单词
(四)关键词
25 个关键字或保留字
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
Go 语言还有 36 个预定义标识符:
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 |
(五)变量声明
//声明单个
var identifier type = 初始值
//声明多个
var a1 , a2 type = 初始值1, 初始值2
方式一:指定变量类型,如果没有初始化,则变量默认为零值。(跟java类似,直接指出变量类型)
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)
}
//输出
0 0 false ""
方式二:根据值自行判定变量类型。(跟js一致,由值决定变量类型)
package main
import "fmt"
func main() {
var name = "李华"
var age = 19
var sex = true
fmt.Println(name)
fmt.Println(age)
fmt.Println(sex)
}
//输出
李华
19
true
方式三:声明变量的同时赋予初始值,并且这个方式只能在函数(方法)中使用
package main
import "fmt"
func main() {
score := 98
name := "lihua"
fmt.Println(name)
fmt.Println(score)
}
//输出
lihua
98
score := 98 等价于 var score int score = 98
注意:以下写法是错误的
":="是用来声明和初始化变量的,赋值操作请使用"="
(六)多变量声明
package main
var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
//这种不带声明格式的只能在函数体中出现
//g, h := 123, "hello"
func main(){
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}
//输出
0 0 0 false 1 2 123 hello 123 hello
(七)常量
常量使用关键字 const 修饰
package main
const MAX = 16
func main(){
println(MAX)
}
输出16
常量也可以用作枚举
package main
const (
Red = 0
Green = 1
Blue = 2
)
func main(){
println(Red)
println(Green)
println(Blue)
println(Blue==2)
}
//输出
0
1
2
true
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次
(iota 可理解为 const 语句块中的行索引)。
(八)运算符
go 运算符与java基本一致
算数运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | 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 |
(九)go的数据类型
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:
|
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
注意:
func main() {
var a1 int64 = 1
//注意 int 的大小取决于本计算机(运行代码的机器是32位,还是64位)
var a2 int = 2
//调用公共函数Printf,通过%T能判断变量的数据类型
fmt.Printf("%T,%T",a1,a2)
}
三、条件分支
if else
package main
func main(){
high := 10
width:=20
println(high*width)
println((high+width)*2)
if high>width {
println((high-width)*2)
}else {
println("高小于宽")
}
}
//输出
200
60
高小于宽
switch
switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。匹配成功后就不会执行其他 case。如果我们需要执行后面的 case,可以使用 fallthrough 。加了fallthrough后,无论下一条case的判断是否成立(为true)都会执行
。
select
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
四、循环
package main
func main() {
//方式一
num := 0
for i := 0; i < 10; i++ {
num = num + i
}
println(num)
//方式二 等价于while
i := 0
for i < 10 {
i++
println(i)
}
//方式三,for each(增强for循环)
var n [10]int
for i := 0; i <10 ; i++ {
n[i] = i
}
for i2 := range n {
println(i2)
}
}
五、函数(方法)
go注释规范
注意:如果安装了Goanno 插件。可以直接通过快捷键生成标准注释。
//
// max
// @Description:获取最大值
// @param p1
// @param p2
// @return int 返回最大值
func max(p1 int, p2 int) int {
if p1>p2 {
return p1
}
return p2
}
go支持多返回值
,如下:
package main
func main() {
//直接打印值
println(square(4, 2))
//使用变量接收返回值
var squaresSum ,squaresDifference = square(4, 2)
println("平方和",squaresSum)
println("平方差",squaresDifference)
}
//
// square
// @Description: 根据传入的参数,计算他们的平方和,平方差
// @param a
// @param b
// @return int
// @return int
//
func square(a, b int) (int, int) {
return (a * a) + (b * b), (a * a) - (b * b)
}
六、函数传值和传引用
(一)传递值
传递是指在调用函数时将实际参数复制一份传递到函数
中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数
。
package main
func main() {
var a1 = 10
//值传递
test1(a1)
println(a1)
}
//
// test1
// @Description: 传值测试
// @param value
// @return int
//
func test1(value int) int {
value = value + 1
return value
}
输出
10
(二)引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中
,那么在函数中对参数所进行的修改,将影响到实际参数
。
引用传递指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:
package main
func main() {
var a1 = 10
//引用(指针)传递
test2(&a1)
println(a1)
}
//
// test2
// @Description: 传递引用类型(指针)
// @param value
// @return int
//
func test2(value *int) int {
var tmp = &value
println(tmp)
*value = *value+1;
return 0
}
输出
0xc000055f68
11
// & 号是获取地址的
// * 号是获取值的
// *tpye(*int) 是声明指针类型的,指针类型初始值为nil(空,没有指向任何存储空间)
七、传递函数类型的参数(函数式编程,回调函数)
一个简单的例子
package main
// 声明一个函数类型,在java中就是一个待实现的方法。
//作用是获取函数传入的参数的值
type getValue func(int) int
func main() {
//方式一:直接传入匿名的函数,(也就是没有名字的函数,只能用一次)。跟java里面的匿名子类差不多,比如线程中的Runnable接口里面的run方法(也叫做单方法接口)
testCallBack(2, func(x int) int {
//函数体的具体逻辑
println("函数真正执行一些业务,(在这里的业务是直接返回传入的参数x的值)")
return x
})
//方式二:传入一个具体的函数,这个具体的函数的类型必须与声明的一致(传入的参数是int类型,返回值也是int类型 )
testCallBack(1, callBack)
}
//
// testCallBack
// @Description: 函数式编程,参数是一个函数类型(将函数当作参数传入函数中,在回调(发布-订阅模式)用得比较多)
// @param x 一个整形参数
// @param f 一个函数体
//
func testCallBack(x int, f getValue) {
//上面传入了一个函数。我们在这个方法中(在合适的时候)可以调用这个函数。
println("一些其他业务")
//调用传入的函数,函数的声明在开头,也就是getValue func(int) int
v := f(x)
println(v)
}
func callBack(x int) int {
println("方式二,我是一个有名字(名字为:callBack)的回调函数。")
return x
}
debug分析一下回调函数的运行逻辑(体验函数式编程)
八、变量作用域(全局变量和局部)
变量作用域和java一致
Go 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑(就近原则
)
九、数组
一个冒泡排序的例子
// Package main
// @Description: 数组练习
package main
func main() {
//创建数组的方式
//方式一
var array1 = []int{6, 2, 13, 4, 28}
//方式二
//array2 := [3]int{1, 2, 3}
//方式三,如果不想数{}号有几个元素,可以用 ... 表示。编译器会根据元素个数自行推断数组的长度
//var array3 = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//迭代遍历,i为数组下标,i2为下标对应的值
for i, i2 := range array1 {
println(i)
println(i2)
}
//寻常遍历
println("---")
for i := 0; i < len(array1); i++ {
println(array1[i])
}
sort(array1)
for i := range array1 {
println(array1[i])
}
}
//
// sort
// @Description: 排序 注意传递数组类型的参数时。数组的大小与传递的大小必须一致
// (比如 传递的参数为:var array1 = [3]int{6, 2, 13, 4, 28} ,那么函数声明也为func sort(array [3]int),
// 如果为动态数组 var array1 = []int{6, 2, 13, 4, 28},那么声明为func sort(array []int),数组大小保存一致,也就是中括号保存一致)
// @param array
//
func sort(array []int) {
for i := 0; i < len(array)-1; i++ {
tag := false
for j := 0; j < len(array)-1-i; j++ {
if array[j] > array[j+1] {
swap(&array[j],&array[j+1])
//标志是否发生交换
tag = true
}
}
//一趟下来没有发生交换
if !tag {
break
}
}
}
//
// swap
// @Description: 交换两个值
// @param p1
// @param p2
//
func swap(p1 *int, p2 *int) {
tmp := 0
tmp = *p1
*p1 = *p2
*p2 = tmp
}
二维数组
package main
func main() {
//二维数组
var arrayTwo = [][]int{}
row1 := []int{1, 2, 3}
row2 := []int{4, 5, 6}
arrayTwo = append(arrayTwo, row1)
arrayTwo = append(arrayTwo, row2)
println(arrayTwo[0][0])
}
切片
使用 [start-index : end-index] 可以将原数组进行切片,同时可以赋值给新变量(注意:新变量元素指向的地址是原数组的地址。)
start-index: 从0开始
end-index: 不包括 end-index
比如 [0:2] 表示获取下标为 0、1的元素。
注意:如果不指定start-index和end-index的值,那么代表获取整个数组。
package main
func main() {
//切片
var array = []string{"北京","上海","深圳","广州"}
array1 := array[0:1]
for i := range array1 {
//改变新变量的元素的值
array1[i] = "**"
}
//注意:这里的切片后赋予的变量array1指向的地址还是原数组的地址。
for i := range array {
//改变新变量元素的值后,原数组的值也发生了改变
println(array[i])
}
}
切片后生成新的数组(新数组的值改变不影响旧数组)
仅仅是切片,切片后的数组的元素指向的地址还是旧数组的地址。为了避免这种情况,可以使用copy函数将切片完全拷贝到新数组中(深拷贝)
package main
func main() {
//切片
var array = []string{"北京","上海","深圳","广州"}
//深拷贝
var array2 = make([]string,len(array))
copy(array2,array)
//改变新数组元素的值,不影响旧数组
array2[0] = "88"
for i := range array2 {
println(array2[i])
}
//遍历旧数组
for i := range array {
println(array[i])
}
}