这是我参与「第五届青训营 」笔记创作活动的第1天
前段时间忙着写论文,青训营的课程没来得及总结,从这篇开始从GO基础开始总结。 我原本是使用Java进行开发的,趁青训营了解一些GO。
GO简介
Go是由Google出品的,具有以下特点
-
高性能、高并发。Java的并发是基于多线程的,线程属于内核态,开销大。go是基于协程的,协程属于用户态,轻量级线程。
-
语法简单、学习曲线平缓。接触过C或者C++的应该很容易上手
-
丰富的标准库,不需要额外导入一些第三方库,标准库有很高的稳定性保证。
-
完善的工具链,代码优化,内置单元测试等。
-
静态链接(java中有动态链接),只需要拷贝一个文件就可以直接运行,Java需要部署一个GRE环境才能运行。
-
快速编译,一秒钟左右快速编译。
-
跨平台(交叉编译,路由器,树莓派等等)
-
自动垃圾回收机制,(C++中没有垃圾回收机制)
-
多返回值函数(java的返回值只有一个)
-
错误处理等。(没有Java中try catch的繁琐,直接将error在方法中将异常作为返回值)
开发环境
首先需要下载安装Golang,类似Java中的JDK。安装及配置环境过程比较简单。下载地址go.dev/doc/install
编译工具有VSCode和GoLand。对于Java开发者而言,使用过IDEA,GoLand更容易上手。JetBrain全家桶破解版YYDS。
基本语法
1、Hello World
程序员接触的第一行代码“hello world”。
Go的执行入口是main包下的main函数。
package main
//导入工具包
import (
"fmt"
)
//主函数入口
func main() {
fmt.Println("hello world")
}
//错误示例:“ { ”必须和方法名在同一行
func main()
{
fmt.Println("hello world")
}
2、变量声明
变量声明由两种方式:
- var 变量名 类型 (和Java相反,把类型放到了后面。)
- 变量名 := 值 (声明并赋值,系统会自动推断值的类型)
func main() {
var a = "string"
var b, c int = 1, 2
var d = true
var e float64
f := float32(e)
g := a + "foo"
fmt.Println(a, b, c, d, e, f)
//输出
fmt.Println(g)
const s string = "constant"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
3、常量声明
常量是再程序运行过程中不可修改的值。对照Java中以final关键字修饰的常量。
package main
import (
"fmt"
"math"
)
func main() {
const a string = "constant"
const b = 500000000
const c = 3e20 / h
fmt.Println(a, b, c, math.Sin(b), math.Sin(c))
}
4、条件语句
Go不支持三目运算符?:
- if else
if 10%2 == 0 {
fmt.Println("偶数")
} else {
fmt.Println("奇数")
}
- switch
switch后可省略变量名
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
select只能用于通道操,channel在多线程中应用juejin.cn/post/719665…
5、循环语句
Go只支持for循环,不支持do while循环。for循环格式如下:
for 条件 {
循环体
}
package main
import "fmt"
func main() {
//1、省略条件
for {
fmt.Println("循环输出")
break //不加break是死循环
}
//2、条件循环
for j := 1; j < 10; j++ {
fmt.Println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
}
6、数组
数组是定长的,不可改变,所以实际开发中不常用。数组内的元素都是同一种类型。数组的创建方式如下:
- 先声明,在赋值
var a [5]int
a[1] = 100
a[2] = 200
复制代码
- 声明时直接赋值
b := [5]int{1, 2, 3, 4, 5}
复制代码
- 二维数组
var c [2][3]int
for i := 0; i < 2; i++{
for j := 0; j < 3; j++{
c[i][j] = i+j
}
}
7、切片
长度可变,可追加元素,实现动态数组,实现范围读取。
创建切片的函数是
make([]type, len, capacity)
参数分别对应:切片类型,初始化切片长度,指定容量(可选参数)
切片最大容量和切片长度
cap(切片)
len(切片)
- 声明切片并赋值
var slice1 []int
slice1 = make([]int, 5)
slice slice2 =[]int{1,2,3}
slice3 := []int{1,2,3}
复制代码
或者
slice4 := make([]int, 4)
slice4[0] = 0
slice4[1] = 1
slice4 = slice4.append(2)
slice4 = slice4.append(3)
//数组拷贝
slice5 := make(int[], len(slice4))
copy(slice5,slice4)
//切片截取赋值
slice6 := slice5[0:5]
复制代码
切片读取范围是前闭后开
slice[:] //整个数组引用
slice[startIndex:endIndex]
slice[startIndex:]
slice[:endIndex]
复制代码
8、函数
函数的格式如下,返回值可以是多个。
func 函数名(参数名 参数类型, 参数名 参数类型) 返回类型{
return 值
}
示例:
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
9、指针
指针也是一种变量,指向的是变量的地址。
函数参数是传递变量时,传的是值,而不是地址,修改不会影响变量。
函数参数是传递指针时,传递的是变量的地址,修改指针变量会影响值。要通过&进行传递。
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
10、结构体
结构体类似Java中的类,是对一系列变量的封装,是一系列带类型的字段集合
type 结构体名 struct{
字段名 字段类型
字段名 字段类型
...
}
type user struct {
name string
password string
}
func main() {
a := user{name: "张三", password: "1024"}
b := user{"李四", "1024"}
c := user{name: "王五"}
c.password = "1024"
var d user
d.name = "赵六"
d.password = "1024"
fmt.Println(a, b, c, d)
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
11、结构体方法
方法和函数的区别在于func后有无接收者。类似Java中的成员方法。
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
11、Map集合
Map是无序的键值对集合。也是通过make函数进行创建
m := make(map[string]int)
m["a"]=1
m["b"]=2
r,error = m["unknow"]
delete(m,"a")
12、接口
接口和struct一样,也是一种类型,用关键字interface定义。 接口中只有方法声明,没有实现也没有字段(JAVA中可以有static final的字段)。
type Animal interface {
Eat(string) string
}
接口是抽象出一些类的公共特点。
type Dog struct{}
func (dog Dog) Eat(food string) (emoij string) {
if food == "骨头" {
emoij = "汪汪"
} else {
emoij = "呜呜"
}
return
}
var animal Animal = Dog{}
food := "骨头"
fmt.Println(animal.Eat(food))
13、异常处理
go通过内置的error接口提供了简单的错误处理方式
type error interface {
Error() string
}
在方法返回时,返回多个参数,其中一个是error
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
14、并发
go关键字可以开启一个goroutine新的轻量级线程。
go 参数名(参数列表)
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
15、字符串操作
Go提供了strings工具包对字符串进行操作
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
}
json串
涉及到网络传输或者存储时,会用到json串和struct的转换。
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
字符串转换
Go提供了strconv工具进行字符串与数字类型之间的转换
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
时间处理
go提供了time类处理时间
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080
}
总结较为粗略,后续完善一下延申的知识。