第一行代码
vscode shift+alt+向下 当前行向下复制一行
package mainimport "fmt"func main() {fmt.Println("Hello Albert")}
命令行运行
装完Golang.msi后
go build -o albertmain.exe main.go //重命名编译go build main.go //编译成main.exego run main.go //在后台编译,直接运行出结果
代码规范
- 官方推荐使用行注释
- tab向右,shift+tab向左
目录结构
GoPath—src—网站域名—作者/机构—项目名—模块
Dos常用指令
Dos:Disk Operating System磁盘操作系统md pathname1 pathname2 pathname3 ... 创建目录rd pathname1 pathname2 删除空目录rd /q/s pathname 删除目录(不带询问)rd /s pathname 删除目录(带询问)dircd ..cd \ 回到根目录echo filecontent > filename 内容写到文件中copy originfilename distfilename 拷贝文件move originfilename distfilename 移动文件(剪切文件)del filename 删除文件
变量
//标准声明var 变量名 变量类型 var strWorkItem string//批量声明var(strWorkItem1 stringstrWorkItem2 stringintWorkItem intdoubleWorkItem double)//类型推导var stringCityName = "NanJing"//短变量声明 := 只能在函数中使用stringName := "HelloGod"//匿名变量 一个下划线_x,_ := Foo() 匿名变量常用于占位,表示忽略值Func Foo()(int,string){}
func DeclareVariable() {//批量声明变量var(name string = "Albert"age int = 25)fmt.Println(name)fmt.Println(age)}
常量
```go const pi = 3.1415926
//批量声明常量时,如果没有赋值,默认和上一行一致 const( pi2 = 3.14 pi3 = 3.1415 )
<a name="BiqI4"></a># iotaiota是golang的常量计数器,只能在常量的表达式中使用。<br />iota在const关键字出现时将被重置为0,const中每新增一行常量申明将使iota计数一次(iota可理解为const语句块中的行索引)。使用iota能简化定义,在定义枚举时很有用。```go//实现类似枚举的效果const(n1 = iota //实现类似枚举的效果 0n2 //1n3 //2n4 //3)//跨行const(a1 = iota //0a2 = 100a3 = iota //2,上面新增了一行,计数加1,此处又加1为2a4)//多个常量声明在同一行const(b1,b2 = iota+1,iota+2 //此处iota都是0,没有新增一行,b1=1,b2=2b3,b4 = iota+1,iota+2 //b3=2,b4=3)//定义数量级const(_= iotaKB = 1<<(10*iota) //1左移10位,2^10MB = 1<<(10*iota)GB = 1<<(10*iota)TB = 1<<(10*iota)PB = 1<<(10*iota))
整型
在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int和uint。
浮点型
默认Golang中的小数都是float64类型
//PrintIn自带换行,但是不支持格式化输出字符串
//Print直接输出内容,Printf格式化输出字符串,Println会输出内容的结尾添加一个换行符
%T是查看类型 %v能查看默认值
package mainimport "fmt"func main() {f1 := 1.725649fmt.Printf("此是f1的类型%T",f1)//fmt.Println("xxx is %f",f1)//PrintIn自带换行,但是不支持格式化输出字符串//Print直接输出内容,Printf格式化输出字符串,Println会输出内容的结尾添加一个换行符fmt.Printf("xxx is %f",f1)}
复数
complex64和complex128
complex64的实部和虚部为32位,complex128的实部和虚部为64位。
fmt总结
%T 查看类型
%v 查看值
%d 查看十进制
%b 查看二进制
%o 查看八进制
%x 查看十六进制
%s 字符串
%#v 会加一个描述符””
字符串

内部实现utf-8编码,支持中文。
格式化输出
s1 := `世情薄人情恶雨送黄昏花易落`fmt.Println(s1)fmt.Printf("s1的数据类型是%T",s1)
字符串常用函数

- 字符串遍历 同时处理有中文的问题 r:= []rune(str) ```go package main
import ( “fmt” )
func main() { str := “Hello世界” r := []rune(str) for i := 0; i < len(r); i++ { fmt.Printf(“%c\n”,r[i]) } }
- 字符串转整数: n,err := strconv.Atoi("12")- 整数转字符串: str := strconv.Itoa(1234)- 字符串转byte[]: var bytes = []byte("hello go") byte转字符串 var str = string([]byte)- 十进制转其他进制 str = strconv.FormatInt(121454,2) //2-->8,16- 查找子串是否在指定的字符串中 strings.Contains("seafood"."foo) //true- 统计一个字符串有几个指定的子串 strings.Count("cecheese","e") //4- 不区分大小写的字符串比较(==是区分字母大小写的) string.EqualFold("abc","Abc") //true- 返回子串在字符串第一次出现的index值,如果没有就返回-1 strings.Index("NLT_abc","abc") //4- 返回子串在字符串最后一次出现的index,如果没有就返回-1 strings.LastIndex("go golang","go") //3- 将指定的子串替换 strings.Replace("go go hello","go","golang",n) n可以指定你希望替换几个,如果n=-1表示全部替换```go//替换字符串中str2:= "go go go to home"tempStr2 := strings.Replace(str2, "go", "golang", 2)println(tempStr2)
- 按照指定的某个字符,分割字符串 strings.Split(“Hello,wrod”,”,”)
- 将字符串的字母大小写转换 strings.ToLower(str) strings.ToUpper(str)
将字符串左右两边的空格去掉 strings.TrimSpace(“ hello albert “) TrimLeft TrimRight
//删除字符串首尾空格str3 := " hello albert "tempStr3 := strings.TrimSpace(str3)//%q可以用双引号将字符串引起来fmt.Printf("I'm tempStr3:%q", tempStr3)
将字符串左右两边指定的字符去掉 strings.Trim(str,” xxx!”)去掉” “和xxx和!
- 判断字符串是否以指定字符串开口/结束 strings.HasPrefix(str,”xxx”) strings.HasStufix(str,”xxx”) ```go //判断字符串是否以指定开头或结尾 str4:= “This is free city” fmt.Println(strings.HasPrefix(str4, “This”)) fmt.Println(strings.HasSuffix(str4, “city”))
<a name="hSPcW"></a># 数组-值类型1. 声明方式: var arrName [len]T (var arrAge [3]int)1. 数组初始化- var testArr [3]int 默认值- var numArr = [3]int{1,2}- var cityArr = [...]string{"shanghai","beijing"}- var stuArr = [...]int{1:3,3:5}和C#不同点C#的引用类型:类、接口、委托、预定义类型(object&string&dynamic)、array 值类型:简单类型、枚举、结构体<a name="K9GgO"></a># SliceSlice是一个拥有相同类型元素的可变长度的序列。引用类型,只能和nil(null)比较<br />声明 var name []T<br />**切片表达式左闭右开**<br />a := [5]int{1,2,3,4,5} 等价于 var a = [5]int{1,2,3,4,5}<br />s := a[1:3] [1,3)索引的值被拿到切片s中<br />fmt.Println(len(s),cap(s)) len(s):2 cap(s) <br />s5 := a[:4] 1,2,3 -->[0:4) <br />**切片的容量是底层数组的容量(长度)**```gofunc main() {//切片声明1var stuList []string//切片插值stuList = append(stuList, "AlbertZhao", "AlbertChen")stuList = append(stuList, "AlbertZhao", "AlbertChen")//切片遍历,index为索引,可以用_来占位 v是元素,range后是遍历的对象for index, v := range stuList {fmt.Println(index, v)}//切片声明2teaList := make([]string, 3, 10)for _, v := range teaList {fmt.Println(v)}fmt.Println(len(teaList))//连接切片totalList := append(stuList, teaList...)fmt.Println(len(totalList))fmt.Println("===========")//切片的赋值拷贝,copy(dst,src []Type) 会拷贝源目标的最小长度到目的的最小长度,最小长度由dst决定//min(len(dst),len(src))copytotalList := make([]string, 5, 10)copy(copytotalList, totalList)fmt.Println(totalList)fmt.Println(copytotalList)for _, v := range copytotalList {fmt.Println(v)}//从切片中删除元素 删除切片index元素,a = append(a[:index],a[index+1:]...)//删除copytotalList第1个元素AlbertChencopytotalList = append(copytotalList[:1], copytotalList[2:]...)fmt.Println(copytotalList)//排序,排序这种长度不定的数组要先转换为切片再进行排序var arrAge = [...]int{3, 7, 5, 6, 9, 5, 4, 1}sort.Ints(arrAge[:])fmt.Println(arrAge)for i := 0; i < len(arrAge); i++ {fmt.Println(arrAge[i])}}
指针
Go语言中不存在指针操作,只需要记住两个符号:
- & 取地址
根据地址取值
//关于Go中,指针只起到取地址和取值两个功能,前者&,后者*n:= 18fmt.Println(&n)fmt.Println(*(&n))//Go中空指针赋值问题//var a *int //没有初始化,对应的地址为nil,*a毫无意义,如何避免new关键字//*a = 100//fmt.Println(*a)//new函数申请一个内存地址var a = new(int)*a = 100fmt.Println(*a)
new()
用于值类型变量申请空间
func main(){a := new(int) //不可以写成 var a *int 然后*a = 100这样申请的是a是nil*a = 100fmt.PrintIn(*a) //100输出结果}
make()
用于引用类型的空间申请 slice\map\chan的内存创建
func main(){//make也是用于内存分配,区别于new,只用于slice、map以及chan的内存创建,而且它返回的类型就是//这三个类型本身,而不是他们的指针类型,因为这三种类型是引用类型,所以没必要返回他们的指针。//make函数是无法替代的,在初始化那三个类型,然后才可以对其进行操作。sourceSlice := make([]string,5,10)sourceSlice[0] = "ClaireLi"fmt.Println(sourceSlice)sourceMap := make(map[string]int,10)sourceMap["ClaireLi"] = 10fmt.Println(sourceMap)}
map
Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。C#中即是字典。
定义:map[KeyType]ValueType map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:make(map[KeyType]ValueType, [cap])
var 元素为map类型的切片 var mapSlice = make([]map[string]string,3) ```go //声明map字典 scoreMap := make(map[string]int, 10) //最好预估一个容量,避免在程序运行期间再动态扩容 scoreMap[“小明”] = 100 scoreMap[“张三”] = 99 fmt.Println(scoreMap) fmt.Println(scoreMap[“小明”])//map的初始化,声明时填充元素 userInfo := map[string]string{“username”:”AlbertZhao”,”password”:”albertzhao”} fmt.Println(userInfo) fmt.Println(userInfo[“username”])
//判断键值是否存在 value,ok :=userInfo[“username”] //ok是golang中约定的 if ok { fmt.Println(“键值存在”) fmt.Println(value) }
//map的遍历 for key, value := range userInfo { fmt.Printf(“键为%v,值为%v\n”,key,value) } //只取map中的key值 for key := range userInfo { fmt.Printf(“键为%v\n”,key) } //删除map中的元素 delete(map,key) //即使key不存在也不会报错 delete(userInfo,”username”) fmt.Println(userInfo)
//对字典进行排序 dicStuManager := make(map[string]int,6) dicStuManager[“AlbertZhao”] = 15 dicStuManager[“AlbertLi”] = 14 dicStuManager[“AlbertChen”] = 13 dicStuManager[“AlbertHuang”] = 12
listStuNames := make([]string,10,20) for key := range dicStuManager { listStuNames = append(listStuNames, key) }
sort.Strings(listStuNames) for _, key := range listStuNames { fmt.Println(key,dicStuManager[key]) }
//写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。 words := “how do you do” splits := strings.Split(words, “ “) dicWords := make(map[string]int,5) for , v := range splits { ,ok := dicWords[v] if !ok {
dicWords[v] = 1
}else{
dicWords[v]++
} } fmt.Println(dicWords)
<a name="VjN8v"></a># 包go的每一个文件都属于一个包,go是以包的形式来管理文件和项目目录结构的<br />文件包名通常和文件所在的文件夹名一致,一般为小写字母。 <br />在import包时,路径从$GOPATH的src下开始(不使用go mod inti 统御名称)<br />go mod init xxx <br />go mod tidy<br />import("<br />别名 "路径"<br />)<br />main包只能有一个,可执行程序。<br /><a name="xS1aJ"></a># 函数<a name="td0vT"></a>## 判断if switchelse if必须跟在}后面```go//strings.Containsvar cmd = "git commit"if strings.Contains(cmd, "commit") {fmt.Println("包含-commit")} else if strings.Contains(cmd, "-m") {fmt.Println("包含-m")} else {fmt.Println("不包含")}switch cmd {case "git commit -m":fmt.Println("相等")case "git commit":fmt.Println("不相等啊")fallthroughdefault:fmt.Println("不相等")}
函数声明及使用
package mainimport("fmt")func main() {fmt.Println(sum(6,7))}func sum(x int,y int)(ref int) {return x+y}//可变参数:Go语言中的可变参数通过在参数名后加...来标识。注意:可变参数通常要作为函数的最后一个参数。func intSum2(x ...int) int {fmt.Println(x) //x是一个切片sum := 0for _, v := range x {sum = sum + v}return sum}ret1 := intSum2()ret2 := intSum2(10)ret3 := intSum2(10, 20)ret4 := intSum2(10, 20, 30)fmt.Println(ret1, ret2, ret3, ret4) //0 10 30 60
defer
由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:
函数参数的传递方式
- 基本数据类型和数组默认都是值传递,即进行值拷贝。在函数内修改,也不会影响到原来的值。
- 如果希望函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针指针的方式操作变量。从效果上看类似引用。
- Go函数不支持重载。
委托
sensor := 函数A
sensor()执行A
sensor = 函数B
sensor()执行B
闭包
可以这样裂解:闭包是类,函数是操作,n是字段。函数和它使用到的n构成闭包。这个对象并没有被销毁,而是一直累加。
一直在调用匿名函数(相当于get;set;方法)
闭包的最佳实践
时间和日期函数-time包
- time.Time类型 用于表示时间 ```go func ShowTime() { //打印当前时间 now := time.Now() fmt.Printf(“now is %v, now type is %T”, now,now) }
//now is 2021-11-22 16:49:00.5368526 +0800 CST m=+0.001499601, now type is time.Time
- 格式化日期和时间:dataStr := fmt.Sprintf("xxx") Sprintf直接拿到打印的字符串<br />fmt.Printf(now.Format("2006/01/02 15:04:05"))- 休眠time.Sleep(100*time.Millisecond)```go//休眠打印for i := 0; i < 10; i++ {println(i)time.Sleep(time.Second)}
- 获取当前Unix时间戳和Unixnao时间戳(可以获取随机数字)
错误处理
go中错误处理的方式: defer panic recover
defer+recover错误异常处理
defer func(){err := recover()if err!=nil{printIn(err)//这里可以将错误信息发送给管理员}}//if前也可以有语句defer func(){if err := recover();err!=nil{printIn(err)//这里可以将错误信息发送给管理员}}
自定义错误
Go中程序中,也支持自定义错误,使用errors.New和panic内置函数。
- errors.New(“错误说明’),会返回一个error类型的值,表示一个错误
- panic内置函数,接收一个interface{}类型的值(也就是任何值)作为参数。可以接收error类型的变量,输出错误信息,并退出程序。
-
面向对象编程(OOP)
Golang支持面向对象编程特性
- Golang中没有class,结构体struct和其他语言的类class有同等的地位
- Golang面向对象去掉了传统OOP语言的继承、方法重载、构造函数和析构函数、隐藏this指针等等
- Golang中实现方式和其他OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名字段来实现
-
结构体-值类型
值拷贝,复制不影响原先变量

声明:
type Person struct{
} var person Person
- var person Person = Person{}
- var person Person = new(Person) (person).Name = “Albert” person.Name = “Json”
- var person *Person = &Person{} person是一个指针,取地址找到结构体
使用注意事项及细节:
- 结构体的所有字段
- struct的每一个字段上,都可以写一个tag,该tag可以通过反射机制获取,常见的场景就是序列化和反序列化。
``go //定义一个Cat结构体 //打Tag进行小写的转换 type Cat struct{ Name stringjson:”nAme”SubName stringjson:”subname”Age int Color stringjson:”color”` }
// //创建Cat变量 var catOne Cat; catOne.Name = “Xiaochen” catOne.SubName = “XiaochenMiao” catOne.Color = “White” catOne.Age = 2 fmt.Println(catOne)
data,err := json.Marshal(catOne)
if err!=nil{
defer func(){
err2 := recover()
if err2!=nil{
fmt.Println(err2)
//这里可以将错误信息发送给管理员
}
}()
fmt.Println("json encoding err ",err)
}
fmt.Println("Json后的数据",string(data)) //这里的data是byte[]类型
<a name="G97ZW"></a>
## 结构体方法
表示A结构体有一方法 Test()
```go
type Student struct{
Name string
Age int
}
//结构体方法 func (a *A) Test(xxx Type) (xxx Type){}
// *A的好处是不会进行值拷贝,而是地址
func (stu Student)PrintStudentInfo(){
stu.Name = "AlbertZhao"
stu.Age = 25
println(stu.Name+"-"+strconv.Itoa(stu.Age))
}
工厂模式
Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
类比C#中字段private 属性get set方法public
type student struct{
Name string
Score float64
}
//函数,构造函数
func NewStudent(n string,s float64) *student{
return &student{
Name:n,
Score:s
}
}
//结构体字段方法
func (s *student)GetScore() float64{
return s.score
}
继承、封装、多态



多重继承:如果同名,需要 c.A.Name c.B.Name单独赋值
接口Interface
接口的最佳实践
实现对Hero结构体切片的排序 sort.Sort(data interface)
//声明Hero结构体
type Hero struct{
Name string
Age int
}
//声明一个Hero结构体切片类型
type HeroSlice []Hero
//实现接口Interface
func (hs HeroSlice)Len() int{
return len(hs)
}
func (hs HeroSlice)Less(i,j int) bool{
return hs[i].Age <hs[j].Age
}
func (hs HeroSlice)Swap(i,j int){
//hs[i],hs[j] = hs[j]:hs[i]这句话等价下面的
temp := hs[i]
hs[i] = hs[j]
hs[j] = temp
}
断言

type Point struct{
x int
y int
}
var a interface{}
var point Point = Point{1,2}
a = point
var b Point
b,ok = a.(Point) //断言
if ok == true
文件操作 os.File
文件常规操作
带缓冲区的方式读取
package main
import (
"io"
"bufio"
"fmt"
"os"
)
func main() {
//打开一个文件
file, err := os.Open("F:\\Repo\\GetStartedWithGolang\\Day1124\\InterfacePractise\\main.go")
if err != nil {
fmt.Println("open file err=", err)
}
fmt.Println(file)
//当函数退出时,要及时关闭file,否则会有内存泄漏
defer file.Close()
//创建一个*Reader,带缓冲的,读一部分处理一部分缓冲的好处,可以处理比较大的文件
//默认缓冲大小是4096
reader := bufio.NewReader(file)
for{
str,err := reader.ReadString('\n') //每次读取一行,读到一个换行就结束
//io.EOF表示文件末尾
if err == io.EOF{
fmt.Println("已经读到文件末尾啦,我跳出去啦")
break;
}
//输出内容
fmt.Print(str)
}
fmt.Println("文件读取结束....")
}
小文件一次性读取(使用ioutil一次将整个文件读入到内存中),适用于文件不大的情况。(ioutil.ReadFile)
//小文件读取
func ReadFileOne(path string){
//一次性读取
content,err := ioutil.ReadFile(path) //content []byte
if err!=nil{
fmt.Println("Read file err=",err)
}
fmt.Println(string(content)) //[]byte转string string转[]byte []byte(string)
}
写文件
FileMode在Linux和Unix下使用,Windows不生效。




