这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
前情提要
由于博主主要学习语言为java,学习 GO 的过程中也将对比java的语法,达到快速上手的目的。
GO环境搭建
如果同学们没有科学上网,国外的下载链接太慢,这里推荐一个国内的下载地址Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com),windows版本选择go1.19.5.windows-amd64.msi即可
编译器这里推荐Goland => 其他版本 - GoLand (jetbrains.com)
- 试用30天
- 学生邮箱申请
- b站自搜破解
基础知识
GO 的优势
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
GO 语言结构
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
与java类似,不过多介绍
GO 数据类型
-
数字类型
intint有int8,int16,int32,int64,分别对应了java中的byte,short,int,long。float分为float 32,float64,分别对应了java中的float和double -
字符串类型
string -
布尔型
booltrue , false -
派生类型:
- 指针类型(Pointer)
- 数组类型
- 结构化类型(struct)
- Channel 类型
- 函数类型
- 切片类型
- 接口类型(interface)
- Map 类型
在 GO 中 ,nil可以代表下面这些类型的零值:
- 指针类型(包括unsafe中的)
- map类型
- slice类型
- function类型
- channel类型
- interface类型
快速入门
package main
// GO 的标准输入输出包
import "fmt"
// 主函数
func main() {
//注意不能有未使用的变量,GO 的检查比较严格
var a, b int = 0, 1
var s1 string = "hello,world!"
var c = 1
s2 := "小广播同学"
fmt.Println(s1+s2, a) //hello,world!小广播同学 0
fmt.Println(b == c) //true
}
- var a, b int = 0, 1 这句是声明多变量,结构是var + 变量名 + 数据类型 + 赋值
- var c = 1 根据值自行判定变量类型。类似js的类型声明
- s2 := "小广播同学" 与var c = 1 类似,是一种简写。
注意:如果 前面已经使用var声明过变量,再使用 := 会报错
GO 还可以在函数体外声明变量,第三种方法不能在函数体外使用
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝
条件判断if
与大多数语言不同,GO 的if语句无需再条件处添加括号,所以不能与其它语言一样能不写大括号。
package main
import "fmt"
func main() {
a, b := 1, 1
if a == b {
fmt.Println("a==b")
} else {
fmt.Println("a!=b")
}
}
循环for
GO 的循环只有一种==>for循环,下面是输出0-9的一个例子
package main
import "fmt"
func main() {
a := 10
for i := 0; i < a; i++ {
fmt.Println(i)
}
}
GO 也可以当成while使用
package main
import "fmt"
func main() {
a := 1
for a < 3 {
fmt.Println(a)
a++
}
}
循环体可以用break跳出循环,continue进入下次循环。
switch case
switch case语法也与大部分类似,但是再case后无需手动添加break
package main
import "fmt"
func main() {
a := 1
switch a {
case 0:
fmt.Println("///")
case 1:
fmt.Println("好好好") // 好好好
default:
fmt.Println("???")
}
}
数组
与c++和java都不同,这里给出两种初始化方法
package main
import "fmt"
func main() {
//第一种
a := [3]int{1, 2, 3}
fmt.Println(a)//[1 2 3]
//第二种
var b [5]int
b[4] = 8
fmt.Println(b[4], len(b))//8 5
}
切片slice
GO 比较常用的用法,比数组更加灵活,支持和python一样的切片,但不支持步长,也不支持负数索引
package main
import "fmt"
func main() {
a := make([]string, 3)
a[0] = "a"
a[1] = "b"
a[2] = "c"
b := append(a, "s")
fmt.Println(a) // [a b c]
fmt.Println(b) // [a b c s]
c := make([]string, 2)
//拷贝切片,第二个元素的值赋值给第一个,拷贝的长度为min( len(a), len(b) )
copy(c, b)
fmt.Println(c) // [a b]
fmt.Println(b[1:]) // [b c s]
fmt.Println(b[1:3]) // [b c]
fmt.Println(b[:4]) // [a b c s]
}
map
map是无序的,输出的顺序与插入顺序无关
package main
import "fmt"
func main() {
m := make(map[string]int) // 这里是string是键的类型,int是值的类型
m["one"] = 1 //one 是 键 , 1是值
m["two"] = 2
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["???"]) // 0
value, isExistKey := m["one"] // 第二个是bool类型,可用作判断key是否存在
fmt.Println(value, isExistKey) // 1 true
delete(m, "two") // 删除key为two的键
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3) // map[one:1 two:2] map[one:1 two:2]
}
循环for range
更便捷的for循环,可以用于数组(array)、切片(slice)、通道(channel)或集合(map)
package main
import "fmt"
var pow = []int{1, 2, 4, 8}
func main() {
// i 是索引位, v是索引位的值
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
m := make(map[int]float32)
m[1] = 1.0
m[2] = 2.0
// 读取 key 和 value
for key, value := range m {
fmt.Printf("key is: %d - value is: %f\n", key, value)
}
// 读取 key
for key := range m {
fmt.Printf("key is: %d\n", key)
}
// 读取 value
for _, value := range m {
fmt.Printf("value is: %f\n", value)
}
}
自定义函数
GO 的传递类型和返回类型都后置,函数可以返回多值
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("a", "b")
fmt.Println(a, b) // b a
}
指针
GO 语言中一切都是值传递,没有引用传递,那么如何修改传入函数的值,就用到了指针。指针相对 c 或 c++ 的操作有限 ,大多数只是用来修改值
package main
import "fmt"
func addReal(a *int) {
*a++
}
func addVirtual(a int) {
a++
}
func main() {
var a int = 10
fmt.Printf("变量的地址: %x\n", &a) // 变量的地址: c00001a0a8
addVirtual(a)
fmt.Println(a) // 10
addReal(&a)
fmt.Println(a) // 11
}
结构体
结构体就是java中的类,不过结构体是值类型,类是引用类型。他们的存储位置一个在栈上,一个在堆上。
结构体定义需要使用 type 和 struct 语句 。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。
结构体函数是在普通函数基础上,在名字前面加上结构体变量名和结构体名。
注意,如果需要在函数体内修改结构体对象,那么需要用到指针。
package main
import "fmt"
type People struct {
height float32
weight float32
name string
}
func checkName(people *People) {
people.name = "lisi"
}
func checkName2(people People) {
people.name = "lisi"
}
func (p *People) modifyName(name string) {
p.name = name
}
func main() {
p := People{height: 179.9}
p.weight = 141.5
p.name = "zhangsan"
checkName2(p)
fmt.Println(p) // {179.9 141.5 zhangsan}
checkName(&p)
fmt.Println(p) // {179.9 141.5 lisi}
fmt.Println(p.name) // zhangsan
}
错误处理
假设一个真实的例子,我们接受一个用户传递来的数据,需要从数据库中匹配,如果匹配不成功,可以返回nil,但是更好的做法是可以返回一个错误信息,通常通过枚举错误信息得出。在java里面就是抛出一个异常,在 GO 中就是返回一个error了,error可以填入一个错误信息用于提示。error存在的函数,调用相当与java的try,接下来的 if 相当于catch。以下是error的简单用法。
package main
import (
"errors"
"fmt"
)
type Users struct {
account string
password string
}
func (user Users) checkLogin(users map[string]string) error {
if users[user.account] != user.password {
return errors.New("用户名或密码不存在")
} else {
return nil
}
}
func main() {
var users = make(map[string]string)
users["lisi"] = "123456"
users["zhangsan"] = "654321"
user := Users{
account: "lisi",
password: "654321",
}
err := user.checkLogin(users)
if err != nil {
fmt.Println(err) // 用户名或密码不存在
}
}
字符串操作
- strings.Contains 字面意思,如果有子字符串则返回true,否则返回false
- strings.Count 返回子字符串的出现次数
- strings.HasPrefix 字符串是否以给定前缀开头,是则true,否则false
- strings.Index 返回给定字符串在要判断字符串的起始位置,从0开始
- strings.Join 用于字符串之间拼接,中间的连接符可以自定义,返回拼接后的字符串
- strings.Repeat 拼接指定次数字符串,返回拼接好的字符串
- strings.Replace 替换指定子字符串
- strings.Split 以给定分割符,将原字符串分割成一个数组
- strings.ToLower 全部小写
- strings.ToUpper 全部大写
字符串格式化
任意变量都可以用 %v 格式化打出
package main
import (
"fmt"
)
func main() {
s := "sss"
fmt.Printf("s=%v", s) //s=sss
}
需要注意fmt.Scanf处理输入时是需要具体的类型的
例如: fmt.Scanf("%i%s", &a, &b) , %i 代表int类型,%s代表sting类型
JSON处理
如果结构体中的字段名开头的第一个字母是大写,即公开字段,类似java中加了 public 的变量,那么直接用json.Marshal(结构体变量) ,json.Marshal 返回两个值(buf,err),buf是json字符串,可以用string(buf)转为人能看懂的字符串,err是错误信息,以防调用者出错。buf 还可以用json.Unmarshal反序列到一个空变量里,这样的变量是一个对象,会带有些除属性外的基本信息。
数字处理
我们可以用 strconv.Atoi 把一个十进制字符串转成数字。可以用 strconv.itoA 把数字转成字符串。 如果输入不合法,那么这些函数都会返回error
简单使用:
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123456"
num, err := strconv.Atoi(s)
if err == nil {
fmt.Println(num)
}
}
进程信息
- os.Args 获得程序启动得到的命令行参数
- os.Getenv 可以得到对应环境变量的值,如os.Getenv("PATH") 获得环境变量PATH的参数
- os.Setenv 可以设置一个环境变量
基础语法总结
至此,go的基础语法就完结了,通过看文章或视频并不能印在脑海,我还是建议大家手动敲一遍,不会的情况可以多百度。若有理解不了或者需要讲解的地方也可以私信我,我高强度冲浪。