golang概述
-
golang官网
https://golang.google.cn/ -
API文档
官网页面 => Packages => 输入要搜索的函数或者包(例如fmt包) => Index栏中找 => Example查看代码示例 -
golang的特点
go语言一个文件要归属一个包
内存自动回收机制,不需要像C一样malloc之后还得free
天然并发,go就出名在高并发的支持上
函数可以返回多个值
程序执行入口为main函数
多条语句不能写在同一行
golang中数据类型不能自动转换
不支持默认值参数,不支持函数重载
- golang源码的编译
编译生成exe文件:go build hello.go
然后执行hello.exe
以脚本方式运行go源文件:go run hello.go
注释
//单行注释
/*
多行注释
*/
输入输出
func main() {
fmt.Println() //打印并换行
fmt.Printf() //格式化输出
fmt.Print() //打印输出
fmt.Scanln() //输入
fmt.Scanf() //格式化输入
fmt.Scan() //输入
}
变量
定义变量
go变量定义规范使用小驼峰
- 变量定义语法1
var varName dataType = value
示例
var name string = "hello"
var age int = 10
- 变量定义语法2
var(
name string
age int
)
- 变量定义语法3
var a, b int //定义int类型的a和b
如果声明时指定变量类型,但不赋值,使用默认值,如int的默认值是0;string默认是空字符串;布尔默认是false;切片、函数、指针默认是nil
变量初始化
:=
是声明并赋值,并且系统自动推断类型,根据值自行判定变量类型。
语法示例
name := "hello"
fmt.Printf("%T", name)
也可以直接直接var a = 1
来简化var a int = 1
命名规则
如果变量名、函数名、常量名首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用( 首字母大写是公开的,首字母小写是私有的 ),在Golang中没有public、private等关键字。
匿名变量
匿名变量使用_
,被称为空白标识符,可以给他赋值,但是任何付给这个标识符的值都会被抛弃,因为这些值在后续不再使用。
代码示例
func test()(int, int){
return 1, 2
}
func main() {
a, _ = test()
_, b = test()
}
变量的作用域
- 局部变量
函数体内声明,在函数体内有同名全局变量和局部变量时,起作用的是局部变量
- 全局变量
函数体外声明
常量
const url string = "www.baidu.com" //显示
const url2 = "www.google.com" //隐式
iota
常量计数器
iota在const关键字出现时被重置为0,const中每增加一行常量声明就计数一次。
func main() {
const (
a = iota
b = iota
c //iota后默认自增
d = 10
e //默认使用上一个值
f = iota //第5行
g //递增
)
fmt.Println(a, b, c, d, e, f, g)
}
0 1 2 10 10 5 6
基本数据类型
bool类型
func main() {
var flag1 bool = true
var flag2 bool
fmt.Println(flag1, flag2)
}
//output
true false
数字类型
uint8, uint16, uint32, uint64
int8, int16, int32, int64
byte = uint8
rune = int32
uint 32位或64位
int与uint一样大小
uintptr无符号整型,用于存放一个指针
代码示例
var age int = 18
var pi float64 = 3.14
字符串
字符串末尾不包含null字符,底层实现是个二元数据结构,一个指针,一个长度。
go字符串字节使用utf-8编码标识的unicode文本
func main() {
//字符串、字符
var str string = "hello,world"
ch := 'a'
str1 := "a"
fmt.Printf("%T, %s\n", str, str)
fmt.Printf("%T, %d\n", ch, ch)
fmt.Printf("%T, %s\n", str1, str1)
//字符串拼接
var str2 = str + str1
fmt.Println(str2)
}
//output
string, hello,world
int32, 97
string, a
hello,worlda
数据类型转换
var a int = 1
var b float64 = float64(a)
var c int = int(b)
运算符
- 算数运算符
+ - * / % ++ --
- 关系运算符
== != > < >= <=
- 逻辑运算符
&& || !
- 位运算符
& | ^ << >> &^
a &^ b
对于b上的每位的值,如果是0则取a对应位的数值,如果为1则取0
- 赋值运算符
= += -= *= /= %= <<= >>= &= ^= |=
流程控制
if
if后面可以带一个简单的初始化语句,以分号分割。示例:返回x,y,z最小值。
if x:=f(); x<y {
return x
} else if x>z {
return z
} else {
return y
}
switch
switch后面可以带一个简单的初始化语句,通过fallthrough语句强制执行下一个case语句。
func main() {
switch i := "y"; i {
case "y", "Y":
fmt.Println("yes")
case "n", "N":
fmt.Println("no")
default:
fmt.Println("err")
}
}
//不需要break
//output
yes
func main() {
switch i := 1; i {
case 1:
fmt.Println("yes")
fallthrough
case 2, 3: //直接穿透下一个case
fmt.Println("no")
case 4:
if i == 1 {
break //终止穿透
}
default:
fmt.Println("err")
}
}
//output
yes
no
for
for init; condition; post {
}
for condition {
} //相当于while(condition)
for {
} //死循环
for key, value := range mapVar {
}
for index, value := range arrayVar {
}
for _, value := range arrayVar {
}
for index, value := range slice {
}
for value := range channelVar {
}
代码示例1
func main() {
var str string = "hello"
for i := 0; i < len(str); i++ {
fmt.Printf("%c, ", str[i])
fmt.Println(str[i])
}
}
代码示例2
func main() {
var str string = "hello"
for i, v := range str {
fmt.Print(i)
fmt.Printf("%c\n", v)
}
}
函数
函数定义和调用
func funcName(param-list)(return-list) {
...
}
func func1() {
fmt.Println("func1")
}
//单返回值
func func2(a, b int) int {
var c int = a + b
return c
}
//多返回值
func func3(a, b int) (int, int) {
return b, a
}
func main() {
func1()
var c int = func2(1, 2)
fmt.Println(c)
var a, b int = func3(1, 2)
fmt.Println(a, b)
}
可变参数
支持不定参数
所有不定参数的类型必须相同,不定参数必须是函数的最后一个参数,
不定参数名在函数体中相当于切片,对切片的操作同样适合于对不定参数的操作。
func sum(arr ...int) (sum int) {
for _, v := range arr {
sum += v
}
return
}
func main() {
s := []int{
1, 2, 3, 4}
var res int = sum(s...)
fmt.Println(res)
}
值传递和引用传递
值类型的数据:int, string, bool, float64, array
引用类型的数据:slice, map, chan
代码示例
// 值传递
func update1(arr [4]int) {
arr[0] = 10
}
// 引用传递
func update2(s []int) {
s[0] = 10
}
func main() {
//值传递
arr := [4]int{
1, 2}
update1(arr)
fmt.Println(arr)
//引用传递
s := []int{
1, 2}
update2(s)
fmt.Println(s)
}
函数类型
func add(a, b int) (c int) {
c = a + b
return
}
fmt.Printf("%T", add) // func(int, int) int
可以用type定义一个函数类型
type funcVar func(int, int) int
说明:函数类型和函数名都是指针变量。
代码示例
func add(a, b int) (c int) {
c = a + b
return
}
func main() {
type fp func(int, int) int
var myFp fp = add
c := myFp(1, 2)
fmt.Println(c)
}
defer
defer语句声明的函数,是注册延迟调用,采用先进后出的方式调用,
通常可以用在错误判断的后面,比如打开一个文件下一行跟着defer的关闭函数,
可以防止忘记编写关闭文件的函数
执行defer时参数就已经传递进函数了
func main() {
defer func() {
println("1")
}() //注意要直接调用
defer func() {
println("2")
}()
}
//output
2
1
匿名函数
不支持命名函数的嵌套定义,但支持嵌套匿名函数
代码示例
func main() {
c := func(a, b int) int {
return a + b
}(1, 2)
fmt.Println(c)
}
回调参数
func operator(a, b int, fp func(int, int) int) int {
c := fp(a, b)
return c
}
func add(a, b int) int {
return a + b
}
func main() {
r := operator(1, 2, add)
fmt.Println(r)
}
闭包
一个外层函数中有内层函数,内层函数中会操作外层函数的局部变量,并且外层函数的返回值就是这个内层函数。这个内层函数和外层函数的局部变量统称为闭包结构
闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
代码示例
//返回值为func() int类型
func increment() func() int {
i := 0 //局部变量
fun := func() int {
i++
return i
}
return fun
}
func main() {
r1 := increment()
fmt.Println(r1())
fmt.Println(r1())
r2 := increment()
fmt.Println(r2())
fmt.Println(r1()) //r1外层的局部变量没有被销毁
}
//output
1
2
1
3
异常、错误
异常
panic用来主动抛出异常,recover用来捕获异常。
发生panic后,程序会从调用panic的函数位置或发生panic的位置立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,知道被recover捕获或运行到最外层函数退出。
defer里面的panic能被后续执行的defer捕获。recover()可以和defer一起使用,但是recover()只有在defer后面的函数体内被直接调用才能捕获panic来终止异常,否则继续向外传递。
defer func(){
recover()
}()
连续多个panic的场景只能出现在延迟调用里面,但只有最后一次panic能被捕获,示例代码如下,输出panic 1。
package main
import "fmt"
func main() {
defer func(){
err := recover()
if err != nil {
fmt.Println(err)
}
}()
defer func(){
panic("panic 1")
}()
defer func(){
panic("panic 2")
}()
panic("panic 3")
}
错误
任何类型只要实现了Error() string方法,都可以传递error接口类型变量,go语言经典的错误处理方式是将error作为函数最后一个返回值,在调用函数时,通过检测其返回的error值是否为nil来进行错误处理。
文章评论