Go安装
GOPATH
:你写Go代码的工作区,保存你的Go代码的。go env
GOPATH/bin
添加到环境变量- 环境变量:当前目录下找不到相关命令时,到环境变量目录中寻找相关命令
go install
命令会把生成的二进制可执行文件拷贝到bin
中
Go命令
go build
:编译Go程序go build -o "xx.exe"
:编译成xx.exe
文件go run main.go
:像执行脚本一样执行mian.go
文件go install
:先编译后拷贝
Go语言文件基础语法
- 文件第一行:
package
关键字声明包名 - 单行注释:
//
- 多行注释:
/* */
- 如果要编译一个可执行文件,必须要有一个
main
包,和一个main
函数(入口函数,无参数和返回值) go
语言函数外的语句必须以关键字开头- 函数内部定义的变量必须使用
变量
- 三种声明方式:
var name type
var name = "zhangsan"
name := "zhangsan"
只能在函数内使用
- 匿名变量(哑元变量)
- 当有些数据必须用变量接收,但又不使用它时,使用
_
接收
- 当有些数据必须用变量接收,但又不使用它时,使用
常量
const pi = 3.1415926
const UserNotExistErr = 10000
iota
实现枚举iota
在const
关键字出现时将被重置为0const
中每新增一行常量声明,iota
累加1
nil
指没有分配内存
流程控制
if
var age = 19 if age >18{ fmt.Println("成年了") }else if age>7{ fmt.Println("上小学") }else{ fmt.Println("最快乐的时光") }
for
各种
for
循环for i:=0;i<10;i++{ fmt.Println(i) }
var i = 0 for ;i<10;i++{ fmt.Println(i) }
for{ fmt.Println("无限循环") }
//基于范围的for循环 s := "hello" fmt.Println(len(s)) //i是索引 for i,v:=range s{ fmt.Printf("第%d个字母是%c\n",i,v) }
switch
switch op{ case 1,2: //...(无break) case 2,3: //... case 4,5: //... default: //... }
基本数据类型
整型
无符号整型
uint8
,uint16
,uint32
,uint64
有符号整型
int8
,int16
,int32
,int64
int
具体是32位或64位取决于操作系统uintptr
表示指针其他进制数
八进制
var n = 0777
十六进制
var n = 0xff
浮点型
float32
float64
(默认)- 两种类型不能直接比较
布尔值
bool
true
和false
不能和其他类型转换
复数
complex128
complex64
字符串
string
使用双引号、反引号包裹,其中反引号支持多行字符串,且自动转义字符,不需要使用
\
字符串不能修改
如果要修改字符串,需要先将其转换成
[]rune
或[]byte
类型,完成后再转换成string
类型。无论哪种转换,都会重新分配内存,并复制字节数组。s2:="白萝卜" s3:=[]rune(s2)//把字符串强制转换乘一个rune切片 s3[0]='红' fmt.Println(string(s3))//把rune切片强制转换成字符串
go语言中字符串都是
utf-8
编码,一个常用汉字一般占3个字节字符串的常用操作
var str = fmt.Sprintf("%s%s",str1,str2)
byte(uint8)
和rune
(int32
)- 均属于类型别名
fmt占位符
%T
查看类型%v
查看变量的值,万能的%d
查看十进制%b
查看二进制%o
查看八进制%x
查看十六进制%s
查看字符串
运算符
- 算术运算符
+,-,*,/
- 逻辑运算符
&&,||,!
- 位运算符
>>,<<,|,^,&
- 赋值运算符
=,+=,-=
- 比较运算符
<,>,<=,>=,==
复合数据类型
数组(Array)
var ages [30]int
数组声明包含元素的类型和元素的个数,元素的个数(数组的长度)属于数据类型的一部分
数组是值类型(与之对应的是引用类型)
数组的初始化
var ages = [30]int{1,2,3,4,5}
var ages = [...]int{1,2,3,4,5,6,7,8}
var ages = [...]int{1:1,99:2}
指定索引
二维数组
var a1 [3][2]int
表示3个[2]int
类型组成的数组只有外层
[]
可以使用...
初始化
a1 = [3][2]int{ [2]int{1,2}, [2]int{3,4}, [2]int{5,6}, }
切片(slice)
声明切片
var s1 []int
,没有分配内存,即==nil
切片是引用类型
初始化
s1 = []int{1,2,3}
make
初始化,分配内存s1:=make([]string,2,4)
- 上面2是长度,4是容量(有容量即分配了内存)
切片与数组
- 切片的本质是对底层数组的封装,它包含三个元素:底层数组的指针(指向切片第一个元素),切片的长度和切片的容量。
- 常用操作
slice:=array[x:y]
切片不能直接比较
切片唯一合法的比较操作是和
nil
比较一个
nil
值的切片长度和容量都是0,但是一个长度和容量都是0的切片不一定是nil
var s1 []int //len(s1)=0;cap(s1)=0;s1==nil s2:=[]int{} //len(s2)=0;cap(s2)=0;s2!=nil s3:=make([]int,0) //len(s3)=0;cap(s3)=0;s3!=nil
切片的赋值拷贝
下面的代码演示了拷贝前后谱两个切片共享底层数组,对一个切片的修改会影响另一个切片的内容
func main(){ s1:=make([]int,3)//[0,0,0] s2:=s1 //将s1的值赋值给s2,两者共用一个底层数组 s2[0]=100 fmt.Println(s1) //[100,0,0] fmt.Println(s2) //[100,0,0] }
copy
函数copy
函数能迅速的将一个切片的数据复制到另外一个切片空间中copy(des,src)
func main(){ //copy复制切片 a:=[]int{1,2,3,4,5} c:=make([]int,5,5)//长度必须要足够 copy(c,a) fmt.Println(a)//[1,2,3,4,5] fmt.Println(c)//[1,2,3,4,5] c[0]=1000 fmt.Println(a)//[1,2,3,4,5] fmt.Println(c)//[1000,2,3,4,5] }
append
函数向
slice
尾部添加数组并返回新的slice
对象func main(){ s:=[]int{1,2} s=append(s,3)//若s未初始化,append可帮助其初始化 fmt.Println(s)//[1,2,3] }
切片的扩容策略
- 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容(newcap)量就是新申请的容量(cap)
- 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍
- 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,直到最终容量(newcap)大于或等于新申请容量(cap)
- 如果最终容量(cap)计算值溢出,则最终容量就是新申请容量
指针
- 两个符号
&
,*
Go
里面的指针不允许被操作修改(++,–),但可以被重新赋值
map
map
存储的是键值对的数据,也需要申请内存var m1 map[string]int
,未分配内存,不可使用- **
make
**初始化:m1 = make(map[string]int,30)
- 如果键值(key)不存在,则取回对应类型(value)的零值(与布尔值)
- 同时取出布尔值:
score,ok:=map1[key]
- 同时取出布尔值:
delete
函数delete(map1,key)
- 如果
key
不存在,则无操作
函数
Go
语言中函数传递的都是值func 函数名(参数)返回值{ 函数体 }
变长函数
变长函数被调用时可以有可变个参数
在参数列表最后的类型名称之前使用省略号
“...”
表示一个变长函数func sum(vals ...int){ total:=0 for _,val := range vals { //vals在函数体内是一个int类型的切片 total += val } return total }
当实参已经存在一个
slice
之后调用变长函数:在最后一个参数后面放一个省略号total = sum(values...)
函数进阶
高级函数:函数作为函数参数或返回值
func main(){ F(Hello) } func F(f func(string)){ //func(string)是f的类型 f("张三") } func Hello(name string){ fmt.Println("hello",name) }
func main(){ f:=F() fmt.Println(f(5,6)) } func F() func(int,int)int { f1:=func(x,y int) int { //函数内部只能定义匿名函数 return x+y } return f1 }
闭包
函数和其外部变量的引用
func bibao(f func(string),name string) func(){ return func() { f(name) } }
defer
延迟调用- 多用于处理资源的释放
内置函数
panic
recover
自定义类型和类型别名
自定义类型
定义了一个全新的类型,我们可以基于内置的基本类型定义,也可以通过
struct
定义type Myint int
通过
type
关键字,Myint
就是一种全新的类型,它具有int
的特性
类型别名(同一个类型)
type Yourint = int
结构体
结构体(值类型)
type name struct{ //content }
当结构体内部数据首字母大写时,对包外为可见状态
匿名结构体(多用于临时场景)
var a = struct{ //content x int y int }{10,20} //实例化
构造(结构体变量)函数
func newPerson(n string,i int) person { return person{ name: n, age: i, } }
方法和接收者
方法是有接收者的函数,接收者指的是哪个类型的变量可以调用这个函数
//接受者使用对应类型首字母小写 func (p person) dream(s string){ fmt.Printf("%s的梦想是%s",p.name,s) }
值接收者和指针接收者
使用指针接受者的情况
- 需要更改结构体的值时
- 结构体本身比较大,拷贝的内存开销比较大时
- 保持一致性,如果有一个方法使用了指针接收者,其他的方法也要使用指针接收者
结构体的嵌套
type addr struct{ province string city string } type student struct{ name string ad addr }
结构体的匿名字段
type addr struct{ province string city string } type student struct{ name string addr //匿名嵌套结构体 }
json
序列化与反序列化- 把
go
语言中结构体变量转换为json
格式的字符串 - 把
json
格式到的字符串转换为go
语言中能够识别的结构体变量
- 把
包(package)
包的定义:
package
关键字,包名通常和目录名一致,不能包含“_
”- 一个文件夹就是一个包
- 文件夹里都是
.go
文件
包的导入:
import
包导入路径:
$GOPATH/src
单行导入
多行导入
给导入的包起别名
匿名导入
包中标识符(变量名、函数名、结构体名、接口名、常量)的可见性:标识符首字母大写
init()
- 包导入的时候自动执行
- 一个包里只有一个
init()
init()
没有参数叶没有返回值叶不能调用它- 一般用于做一些初始化操作
接口(interface)
接口是一种类型,将实现同一种方法的所有类型进行汇总
type mover interface{ 方法的签名(参数)(返回值) }
type person struct{} type cat struct{} type dog struct{} //接口是一种特殊的类型,它规定了变量有哪些方法 type Speaker interface { speak() //只要实现了speak方法的变量都是speaker类型的变量 } func (c cat) speak() { fmt.Println("喵喵喵~") } func (d dog) speak() { fmt.Println("汪汪汪~") } func (p person) speak() { fmt.Println("嘤嘤嘤~") } /*发现问题,传入参数的类型不固定*/ func da(x Speaker) { //传入一个参数,传进来什么就打什么 x.speak() //挨打了就要叫 } /*在编程中,会遇到以下场景: 我不关心一个变量是什么类型,我只关心能调用它的什么方法*/ func main() { var c1 cat var d1 dog var p1 person da(c1) da(d1) da(p1) }
空接口
- 任意类型都实现了空接口(“
interface{}
”) - 用法
- 作为函数参数
map[string]interface{}
- 任意类型都实现了空接口(“
类型断言
只能是接口类型
x.(T)
,多用switch
来做类型断言func main(){ var a interface{}//定义一个空接口变量 a=100 //x.(T) if v1,ok := a.(int8);ok{ fmt.Println("猜对了",v1) return }else{ fmt.Println("none") } //switch switch v2:=a.(type){ case int8: fmt.Println("int8",v2) case int16: fmt.Println("int16",v2) case int: fmt.Println("int",v2) default: fmt.Println("none") } }
文件操作
打开文件和关闭文件
os.open
打开一个文件(只读文件),返回一个*file
和一个error
。对得到的文件实例调用close()
方法关闭文件func main() { fileObj, err := os.Open("./file.go") //os.Open与os.Close用来文件的打开和关闭 if err != nil { //常规语句,用来判断读取文件是否出错 fmt.Printf("open file failed, err=%v", err) return } //记得关闭文件 defer fileObj.Close() //读文件 //var tmp = make([]byte, 128) //指定读的长度 var tmp [128]byte for { n, err := fileObj.Read(tmp[:]) //fileObj.Read把内容读到参数tmp[:]中,返回读取长度与error if err != nil { fmt.Printf("read from file failed, err:%v", err) return } fmt.Printf("读了%d个字节\n", n) fmt.Println(string(tmp[:n])) //string()函数,使参数化为string类型 if n < 128 { return } } }
读文件
func (f *os.File)Read(b []byte)(n int,err error)
- 它接收一个字节切片,返回读取的字节数和可能出现的具体错误,读到文件末尾会返回
0
和io.EOF
- 它接收一个字节切片,返回读取的字节数和可能出现的具体错误,读到文件末尾会返回
bufio
利用缓冲区func readFromFilebyBufio() { fileObj, err := os.Open("./file_2.go") if err != nil { fmt.Printf("open file failed, err=%v", err) return } //记得关闭文件 defer fileObj.Close() //创建一个用来从文件中读内容的对象 reader := bufio.NewReader(fileObj) for { line, err := reader.ReadString('\n') //参数是结束符号,返回截止符号前的string与error if err == io.EOF { fmt.Println("文件读完") return } if err != nil { fmt.Printf("reader line failed,err:%v", err) return } fmt.Println(line) } } func main() { readFromFilebyBufio() }
ioutil
包的ReadFile
方法能够读取完整的文件,只需要将文件名作为参数传入func readFromFilebyIoutil() { //读取整个文件,且无需打开与关闭文件 ret, err := ioutil.ReadFile("./file_3.go") //返回[]byte类型与error if err != nil { fmt.Printf("read file failed, err:%v", err) return } fmt.Printf(string(ret)) } func main() { readFromFilebyIoutil() }
写文件
os.OpenFile(name string,flag int,perm FileMode)(*File,error)
Write
和WriteString
func main() { fileObj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { fmt.Printf("open file failed,err:%v", err) return } //write fileObj.Write([]byte("123456789\n")) //写入字节切片数据 //writestring fileObj.WriteString("987654321") //直接写入字符串数据 fileObj.Close() }
利用缓存
bufio
func writeDemo2() { fileObj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { fmt.Printf("open file failed,err:%v", err) return } defer fileObj.Close() //创建一个写的对象 wr := bufio.NewWriter(fileObj) wr.WriteString("hello沙河\n") //写到缓存中 wr.Flush() } func main() { writeDemo2() }
利用
ioutil
func main() { str := "hello 沙河" err := ioutil.WriteFile("./xx.txt", []byte(str), 0666) if err != nil { fmt.Println("write file failed, err:", err) return } }