Go简介
Go的基本介绍
特性
Go语言保证了既能到达静态编译语言的安全和性能,又达到了动态语言开发维护的高效率,使用一个表达式来形容Go语言:Go =C+ Python,说明Go语言既有c静态语言程
序的运行速度,又能达到Python动态语言的快速开发。
从c语言中继承了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指
针等等,也保留了和c语言一样的编保执行方式及弱化的指针。引入包的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在。
垃圾回收机制,内存自动回收,不需开发人员管理
天然并发
- 从语言层面支持并发,实现简单
- goroutine,轻量级线程,可实现大并发处理,高效利用多核。
- 基于CPS并发模型(Communicating Sequential Processes )实现
吸收了管道通信机制,形成Go语言特有的管道channel,通过管道channel,可以实现不同的goroute之间的相互通信。
函数返回多个值
新的创新:比如切片、延时执行defer等
程序开发
1 | //hello.go |
hello.go
所在的包是main,在go中,文件必须归属一个包。
在命令行可以通过build命令进行编译,在main包下执行下面命令可以得到一个执行文件
1 | go build hello.go |
通过run也可以直接运行源码程序
1 | go run hello.go |
2种编译方式区别
如果我们先编译生成了可执行文件,那么我们可以将该可执行文件拷贝到没有go开发环境的机器上,仍然可以运行
如果我们是直接go fun go
源代码,那么如果要在另外一个机器上这么运行,也需要go开发环境,否则无法执行。
在编译时,编译器会将程序运行依赖的库文件包含在可执行文件中,所以,可执行文件变大了很多。
go语言提供了大量的标准库:https://studygolang.com/pkgdoc
这些标准库在go语言的src
目录下
GO语法
变量
变量的定义
指定变量类型,声明后若不赋值,使用默认值
1
var i int
根据值自行判定变量类型(类型推导)
1
var num = 10.11
省略var,注意.
:=
左侧的变量不应该是已经声明过的,否则会导致编译错误1
2//var string name = ""
name := ""
一次性声明多个变量
1 | var n1, n2 ,n3 int |
数据类型
基本数据类型
1 | 1.数值型 |
字符变量
Go的字符串是由字节组成,对于ascii码的值可以保存到byte,
大于255可以保存到int
类型.
go语言字符使用utf-8
,在Go中字符是一个整数,直接输出是该字符的码值
1 | package main |
字符串
Golang的字符串是不可变的
字符串的表示形式
- 双引号,识别转义。
- 反引号,原生形式输出。
派生/复杂数据类型
1 | 1.指针(Pointer) |
基本数据类型转换
需要显示转换
1 | var i int32 = 100 |
基本数据类型和string的转换
1 | var num int = 90 |
还可以使用strconv
包的函数
1 | str = strconv.FormatInt(int64(num3), 10); //把原类型转换为int64,以10进制转换 |
指针
- 基本数据类型,变量存的就是值,也叫值类型
- 获取变量的地址,用&,比如:
var nim int
,获取num的地址:&num
- 指针类型,变量存的是一个地址,这个地址指向的空间存的才是值比如:
var ptr *int = &num
- 获取指针类型所指向的值,使用:
*
,比如:var ptr int
,使用ptr获取p指向的
使用细节
值类型:基本数据类型int系列, float系列, bool, string、数组和结构体struct
变量直接存储值,内存通常在栈中分配
引用类型:指针、slice切片、map、管道chan、interface等都是引用类型
变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
标识符命名
- 保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,不要和标准库不要冲突fmt。
- 变量名、函数名、常量名:采用驼峰法。
- 如果变量名、函数名、常量名首字母大写,则可以被其他的包访间;如果首字母小则只能在本包中使用(注:可以简单的理解成,首字母大写是公有的,首字母小写私有的)
模块化支持
1 | go env -w GO111MODULE=off |
包
go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的
- 区分相同名字的函数、
- 变量等标识符当程序文件很多时,可以很好的管理项目
- 控制函数、变量等访问范围,即作用域
在给一个文件打包时,该包对应一个文件夹,比如这里的utils文件夫对应的包名就是utils,文件的包名通常和文件所在的文件夹名一致,—般为小写字母。
当一个文件要使用其它包函数或变量时,需要先引入对应的包
1
2
3
4
5
6import"包名"
import (
"包名"
"包名"
)package指令在文件第一行,然后是import指令。
在import包时,路径从$GOPATH的 src下开始,不用带src ,编译器会自动从src下开始引入
为了让其它包的文件,可以访问到本包的函数,则该函数名的首字母需要大写,类似其它
语言的public ,这样才能跨包访问。主访问其它包函数时,其语法是包名.函数名。
如果包名较长,Go支持给包取别名,注意细节。取别名后,原来的包名就不能使用了
1
2
3import (
别名 "包名"
)同一个包下不能有相同的函数名。
函数
简介
函数的形参列表可以是多个,返回值列表也可以是多个。
形参列表和返回值列表的数据类型可以是值类型和引用类型。
函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似public ,首字母小写,只能被本包文件使用,其它包文件不能使用,类似private.
函数中的变量是局部的,函数外不生效
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用
Go函数不支持重载。空接口
在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用!
为了简化数据类型定义,Go支持自定义数据类型
1
2
3//基本语法:type自定义数据类型名数据类型理解:相当于一个别名
type mylnt int //这时mylnt就等价int来使用了.
type mySum func (int, int) int//这时mySum就等价一个函数类型func(int, int) in支持对函数返回值命名
1
2
3
4
5func cal(n1 int,n2 int)(sum int, sub int){
sum = n1+n2
sub = n1-n2
return
}Go支持可变参数
1
2
3
4
5
6//支持0到多个参数
func sum(args... int) sum int {}
//支持1到多个参数
func sum(n1 int, args... int) sum int {
}args是slice,通过args[index]可以访间到各个值。
init函数
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。
如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义->init函数->main函数
init函数最主要的作用,就是完成一些初始化的工作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import (
"fmt"
)
var age = test()
func test() int {
fmt.Println("test")
return 90
}
func init(){
fmt.Println("init")
}
func main() {
fmt.Println("main")
}
test
init
main如果main.go和util.go定义了变量和init
首先执行util.go的变量定义和init函数,然后执行main的变量定义和main函数。
闭包
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
1 | //累加器 |
返回的是一个匿名函数,但是这个匿名函数引用到函数外的n ,因此这个匿名函数就和n形成一个整体,构成闭包。
当我们反复的调用f函数时,因为n是初始化一次,因此每调用一次就进行累计。
我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。l
1 | 编写一个函数makeSuffix(suffix string)可以接收一个文件后缀名(比如.jpg),并返回一个闭包 |
1 | func makeSuffix(suffix string) func (string) string{ |
返回的匿名函数和makeSuffix (suffix string)的suffix变量组合成一个闭包,因为返回的函数引用到suffix这个变量
我们体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入后缀名,比如 .jpg ,而闭包因为可以保留上次引用的某个值,所以我们传入—次就可以反复使用。
defer
在函数中,程序员经常需要创建资源(比如,数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。
当执行到defer时,暂时不执行,会将defer后面的语句(包括数据)压入到独立的栈(defer栈)。当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
1 | func sum(n1 int, n2 int)int { |
变量作用域
- 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部。
- 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效。
- 如果变量是在一个代码块,比如 for / if中,那么这个变量的的作用域就在该代码块
常用函数
string
1 | package main |
时间
1 | func usedTime(){ |
时间常量
1 | const ( |
获取unix时间戳
1 | now.Unix(),nowUnixNano() |
内置函数
Golang 设计者为了编程方便,提供了一些函数,这些函数可以直接使用,我们称为Go的内置函数。文档: https://studygolang.com/pkgdoc
-> builtin
make:用来分配内存,主要用来分配引用类型,比如channel、map、slice。
错误处理
在默认情况下,当发生错误后(panic)程序就会退出(崩溃.)I
如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行。
Go语言追求简洁优雅,所以,Go语言不支持传统的try…catch…finally这种处理。
Go中引入的处理方式为: defer,panic, recover
这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
1 | func error(){ |
自定义错误
使用errors.New
和 panic内置函数。
1) errors.New(“错误说明””),会返回一个error类型的值,表示一个错误
2) panic内置函数,接收一个interface{}
类型的值(也就是任何值了)作为参数。可以接收error类型的变量,输出错误信息,并退出程序.
1 | func readConf(name string)(err error){ |