主要特点
- 自动垃圾回收
- 丰富的内置类型
- 函数多返回值
- 错误处理
- 匿名函数和闭包
- 类型和接口
- 并发编程
- 发射
- 语言交互性
环境安装
windows
安装包地址:https://golang.org/dl/。或https://golang.google.cn/dl/。
windows上安装完后可以直接在cmd中运行go
Linux
sudo apt install golang-go
语言结构
-
包声明
-
引入包
-
函数
-
变量
-
语句&表达式
-
注释
package main
import "fmt" func main() { /* 这是我的第一个简单的程序 */ fmt.Println("Hello, World!") }
注:
- 需要注意的是 { 不能单独放在一行
- 在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。
数据类型
布尔型
布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
数字类型
uint8 uint16 uint32 uint64 int8 int16 int32 int64 float32 float32 complex64(32 位实数和虚数) complex128(64 位实数和虚数)
byte 类似 uint8
rune 类似 int32
uint 32 或 64 位
int 与 uint 一样大小
uintptr 无符号整型,用于存放一个指针
派生类型
(a) 指针类型(Pointer)
(b) 数组类型
© 结构化类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface)
(h) Map 类型
变量
声明变量的一般形式是使用 var 关键字: var identifier type
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
第一种,指定变量类型
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口
第二种,根据值自行判定变量类型。
var v_name = value
省略 var, 注意 := 左侧如果没有声明新的变量,就产生编译错误
格式: v_name := value
var intVal int
intVal :=1 // 这时候会产生编译错误
intVal,intVal1 := 1,2 // 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
实例
// 第一种写法
var num1 int = 1
var num2 int = 2
var num3 int = 3
或者
var num1 = 1
var num2 = 2
var num3 = 3
// 第二种写法
var num1,num2,num3 int = 1,2,3
或者
var num1, num2, num3 = 1,2,3
// 第三种写法
var (
num1 int = 1
num2 int = 2
num3 int = 3
)或
var (
num1 = 1
num2 = 2
num3 = 3
)
常量
格式:const identifier [type] = value
显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"
运算符
-
-
- / % ++ – == != < > >= <=
-
&& || ! & | ^ << >> = += -= *= /= %= <<= >>= &= ^= |=
条件语句
if
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 /
} else {
/ 在布尔表达式为 false 时执行 */
}
switch
switch var1 {
case val1:
…
case val2:
…
default:
…
}
select
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case /
default : / 可选 */
statement(s);
}
- 每个 case 都必须是一个通信
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行,其他被忽略。
- 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。 否则:如果有 default 子句,则执行该语句。如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值
循环语句
for
for init; condition; post { }
for condition { }
for { } //死循环
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
func main() {
var b int = 15
var a int
numbers := [6]int{1, 2, 3, 5}
/* for 循环 */
for a := 0; a < 10; a++ {
fmt.Printf("a 的值为: %d\n", a)
}
for a < b {
a++
fmt.Printf("a 的值为: %d\n", a)
}
for i,x:= range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
}
}
方法
func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) { 函数体 }
- 指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的 this 或者 self。由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的
- 非指针接收器时,Go 语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
函数
格式:
func function_name( [parameter list] ) [return_types] {
函数体
}
-
func:函数由 func 开始声明
-
function_name:函数名称,函数名和参数列表一起构成了函数签名
-
参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。
-
return_types:返回类型,有些功能不需要返回值,这种情况下 return_types 不是必须的。
func swap(x, y string) (string, string) {
return y, x
}func main() { a, b := swap("Mahesh", "Kumar") fmt.Println(a, b) } /* 函数返回两个数的最大值 */ func max(num1, num2 int) int { /* 定义局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
数组
格式:var variable_name [SIZE] variable_type
var balance [10]float32
初始化:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
var balance = […]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance[4] = 50.0
指针
格式:var var_name *var-type
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
结构体
格式:
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
}
枚举
在golang中并没有枚举类型,但可根据iota特性进行枚举类型的定义
详见iota使用
切片(Slice)
可以声明一个未指定大小的数组来定义切片:
var identifier []type
或使用make()函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
空(nil)切片:一个切片在未初始化之前默认为 nil,长度为 0
可变参数
声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号“…”,这表示该函数会接收任意数量的该类型参数。
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
sum函数返回任意个int型参数的和。在函数体中,vals被看作是类型为[] int的切片。sum可以接收任意数量的int型参数:
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
标准库
https://studygolang.com/pkgdoc
包引用
同一个包里面,不同文件之间,不需要 import,直接用就好。不同包的话,需要引用包,只能使用大写字母开头的方法 ,变量 等等,小写子母开头的只能包内使用。大写字母开头的变量,方法暴露给其他包用的,包内的话可以随便引用。
直接引用
import “fmt”
import “os”
将包一块引入
import (
"fmt"
"os"
)
相对路径
import "./model" // 当前文件同一目录的 model 目录
绝对路径
import "shorturl/model" // 加载 GOPATH/src/shorturl/model 模块
点操作
import(
. "fmt"
)
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名 fmt.Println( “我爱北京天安门” )可以省略的写成 Println( “我爱北京天安门” )。
别名操作
import(
f "fmt"
)
f.Println( "我爱北京天安门" )
下划线操作
import (
“database/sql”
_ “github.com/ziutek/mymysql/godrv”
)
下滑线 操作其实只是引入该包。当导入一个包时,它所有的 init() 函数就会被执行,但有些时候并非真的需要使用这些包,仅仅是希望它的 init() 函数被执行而已。这个时候就可以使用下滑线操作引用该包了。即使用下滑线操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其 init() 函数。
new()和make()区别
- new 的作用是初始化一个指向类型的指针(*T),make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。
func new(Type) *Type
func make(Type, size IntegerType) Type
下划线
忽略返回值
v1, v2, _ := function(...)
某个函数返回三个参数,但是我们只需要其中的两个,另外一个参数可以忽略
下划线在import中
让导入的包做初始化,而不使用包中其他功能
range
返回的第一个索引是连续的,可以看到第二个值都是整型数字
func rangeString() {
datas := "aAbB"
for k, d := range datas {
fmt.Printf("k_addr:%p, k_value:%v\nd_addr:%p, d_value:%v\n----\n", &k, k, &d, d)
}
}
k_addr:0xc420014148, k_value:0
d_addr:0xc420014150, d_value:97
k_addr:0xc420014148, k_value:1
d_addr:0xc420014150, d_value:65
k_addr:0xc420014148, k_value:2
d_addr:0xc420014150, d_value:98
k_addr:0xc420014148, k_value:3
d_addr:0xc420014150, d_value:66
type
定义别名
例如:type name string // name类型与string等价,就是将一个string类型起一个别名叫做name