第一行代码
vscode shift+alt+向下 当前行向下复制一行
package main
import "fmt"
func main() {
fmt.Println("Hello Albert")
}
命令行运行
装完Golang.msi后
go build -o albertmain.exe main.go //重命名编译
go build main.go //编译成main.exe
go 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 删除目录(带询问)
dir
cd ..
cd \ 回到根目录
echo filecontent > filename 内容写到文件中
copy originfilename distfilename 拷贝文件
move originfilename distfilename 移动文件(剪切文件)
del filename 删除文件
变量
//标准声明
var 变量名 变量类型 var strWorkItem string
//批量声明
var(
strWorkItem1 string
strWorkItem2 string
intWorkItem int
doubleWorkItem 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>
# iota
iota是golang的常量计数器,只能在常量的表达式中使用。<br />iota在const关键字出现时将被重置为0,const中每新增一行常量申明将使iota计数一次(iota可理解为const语句块中的行索引)。使用iota能简化定义,在定义枚举时很有用。
```go
//实现类似枚举的效果
const(
n1 = iota //实现类似枚举的效果 0
n2 //1
n3 //2
n4 //3
)
//跨行
const(
a1 = iota //0
a2 = 100
a3 = iota //2,上面新增了一行,计数加1,此处又加1为2
a4
)
//多个常量声明在同一行
const(
b1,b2 = iota+1,iota+2 //此处iota都是0,没有新增一行,b1=1,b2=2
b3,b4 = iota+1,iota+2 //b3=2,b4=3
)
//定义数量级
const(
_= iota
KB = 1<<(10*iota) //1左移10位,2^10
MB = 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 main
import "fmt"
func main() {
f1 := 1.725649
fmt.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>
# Slice
Slice是一个拥有相同类型元素的可变长度的序列。引用类型,只能和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 />**切片的容量是底层数组的容量(长度)**
```go
func main() {
//切片声明1
var stuList []string
//切片插值
stuList = append(stuList, "AlbertZhao", "AlbertChen")
stuList = append(stuList, "AlbertZhao", "AlbertChen")
//切片遍历,index为索引,可以用_来占位 v是元素,range后是遍历的对象
for index, v := range stuList {
fmt.Println(index, v)
}
//切片声明2
teaList := 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个元素AlbertChen
copytotalList = 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:= 18
fmt.Println(&n)
fmt.Println(*(&n))
//Go中空指针赋值问题
//var a *int //没有初始化,对应的地址为nil,*a毫无意义,如何避免new关键字
//*a = 100
//fmt.Println(*a)
//new函数申请一个内存地址
var a = new(int)
*a = 100
fmt.Println(*a)
new()
用于值类型变量申请空间
func main(){
a := new(int) //不可以写成 var a *int 然后*a = 100这样申请的是a是nil
*a = 100
fmt.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"] = 10
fmt.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 switch
else if必须跟在}后面
```go
//strings.Contains
var 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("不相等啊")
fallthrough
default:
fmt.Println("不相等")
}
函数声明及使用
package main
import(
"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 := 0
for _, 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 string
json:”nAme”SubName string
json:”subname”Age int Color string
json:”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不生效。