1.Go语言
Go语言的特点:
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2.入门
2.1 环境-安装Golang
2.2 Go的基本语法 -- HelloWorld
package main // 入口
import (
"fmt" //fmt包,用于输出
)
func main() {// main 方法
fmt.Println("hello world")
}
2.2 Go的基础语法 -- 变量
Go语言是一门强类型语言,每一个变量都拥有对应的变量类型
Go语言中,声明变量有两种方式:
- 显式声明:
var 变量名 = 值,Go语言会进行类型推断,无需加变量类型.
var 变量名 变量类型 = 值,但是如果想要声明变量类型,也可以采取这种方式
变量名 := 值,这种声明方法也可以进行声明以及赋值
- 常量
const 常量名 (类型) = 值<括号内可省>
Example Codes:
package main
import (
"fmt"
"math"
)
func main() {
var a = "initial"
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 = "contant"
const h = 50000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
2.3 Go的基础语法 -- if else
Go中的if-else语法与C类似,但是表达式不需要用
()囊括,且if-else语句不可省略{}
Example Codes:
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
2.4 Go的基础语法 -- 循环
Go里面的循环只有for循环一种,如果for里面什么都不写,就是死循环,也可以使用C型循环,Go里面的循环也支持break与continue语句使用
Example Codes:
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println("loop")
break
}
for j := 7; j < 9; j++ {
println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
println(n)
}
for i <= 3 {
println(i)
i += 1
}
}
2.5 Go的基础语法 -- switch
Go里面的switch结构和Java类似,只是识别的变量不需要加括号,并且对于每个case,语句结束后会直接结束switch循环,无需加break等语句,且case支持多变量并联。
并且在Go里面的switch语句中,可以识别任意类型的变量,甚至于结构体等等,或者可以在switch语句后不加表达式,直接当作if-else语句使用
Example Codes:
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
println("one")
case 2:
println("two")
case 3:
println("three")
case 4, 5:
print("four or five")
default:
print("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println(t, "It's before noon")
default:
fmt.Println(t, "It's after noon")
}
}
2.6 Go的基础语法 -- 数组
Go里面的数组是定长的,但是在实际开发中很少用数组,一般都是用可变长的切片
Example Codes:
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println(a[4], len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Print("2d: ", twoD)
}
切片
切片是一种可变长的数组,并且切片中值的添加使用
append()方法,并且添加后的结果必须赋值给原切片。不仅如此,如果是同长的切片,可以使用copy方法直接将切片内容进行拷贝。
切片还支持截取数组内容,可以通过三种方式截取
Example Codes:
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2])
fmt.Println("len:", len(s))
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s)
c := make([]string, len(s))
copy(c, s)
fmt.Println(c)
fmt.Println(s[2:5])
fmt.Println(s[:5])
fmt.Println(s[2:])
good := []string{"g", "o", "o", "d"}
fmt.Println(good)
}
- Section[2:5] 即截取切片
从第2个元素元素开始(但不包括)到第5个元素(包括),对应下标(2,3,4)- Section[:5] 截取到第五个元素,对应下标
(0,1,2,3,4)- Section[2:]
从二个元素开始截取(但不包括)对应下标(2,3,4,...,len(Section)-1)
map
map 是我们在开发过程中使用最多的数据类型。
对应创建语法 :
map_name := make(map [key_type] value_type)map_name := map [key_type] key_type{ key1 : value1 , key2 : value2}var map_name := map [key_type] key_type{ key1 : value1 , key2 : value2}在完成创建之后,可以通过
map_name[key] = value的方式对每一个key进行赋值map读取:
- 读: 我们通过
map_name[key]的方式取得value- 写: 我们通过
delete(map名,key)的方式删除 K-V键值对map遍历: 对map进行遍历的时候,不会按照字母顺序,也不会按照插入顺序输出,而是一个偏随机的顺序
Example Codes:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)
fmt.Println(len(m))
fmt.Println(m["one"])
fmt.Println(m["unknown"])
r, ok := m["unknown"]
fmt.Println(r, ok)
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)
}
range
对于切片和map,我们在进行遍历时会遭遇一些困难和不便,Go提供的range可以让我们快速的遍历map和section。
语法:
- 对于数组和切片:
for i,num : = range array{ code... }其中
i对应的是数组/切片的索引,num对应的是索引所代表的值
- 对于map:
for k,v := range map{ code... }其中
k对应的是map的key,v对应的是map的value
Example Codes:
package main
import (
"fmt"
)
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num)
}
}
fmt.Println(sum)
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v)
}
for k := range m {
fmt.Println("key", k)
}
}
2.8 Go的基础语法 -- 函数
函数的结构:
func func_name(param1 param1_type , param2 param2_type ,...) (return_value1 return_type,return_value2, return_type...) { code... return value1,value2,... }Go语言的函数是可以返回多个值的,并且在实际开发中,函数一般都是返回的多值。
Example Codes:
package main
import "fmt"
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
}
func main() {
res := add(1, 2) //3
fmt.Println(res)
v, ok := exists(map[string]string{"a": "A"}, "a") //A true
fmt.Println(v, ok)
}
2.9 Go的基础语法 -- 指针
Go语言里的指针的功能非常有限,主要用于对变量进行修改。
Example Codes:
package main
import "fmt"
func main() {
n := 5
add2(n)
fmt.Println(n)//5
add2ptr(&n)
fmt.Println(n)//7
}
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
2.10 Go的基础语法 -- 结构体
在Go里面,结构体是变量的集合,并且在对结构体进行赋值时,可以指定赋值的变量名,也可以不指定<按顺序赋值>,如果未赋值,则置为空值或0,取值时采用
结构体名.属性获取对应值。也可以通过传入结构体指针的方式修改结构体,可以防止大型结构体拷贝的开销
Example Codes:
package main
import "fmt"
type user struct {
name string
password string
status bool
}
func main() {
a := user{name: "NanCheng", password: "123456", status: true}
b := user{"NanCheng", "123456", true}
c := user{name: "NanCheng", password: "123456"}
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
/*
{NanCheng 123456 true}
{NanCheng 123456 true}
{NanCheng 123456 false}
*/
c.status = false
var d user
d.name = "shabi"
d.password = "1024"
fmt.Println(checkPassword(a, "haha"))
fmt.Println(checkPassword2(&a, "haha"))
/*
false
false
*/
}
func checkPassword(u user, pwd string) bool {
return u.password == pwd
}
func checkPassword2(u *user, pwd string) bool {
return u.password == pwd
}
2.11 Go的基础语法 -- 结构体方法
我们可以为结构体指定拥有的方法,十分类似于Java内类的成员方法等。
结构体方法的声明规则:
func (p Struct_Name) Function_Name(Param1 ParamType,...) (return_Value return_type, ...){ code... }我们使用结构体方法也很简单,就像引用属性一样引用方法即可。
而在声明结构体方法时,对结构体的引入可以选择带指针和不带指针,这样可以选择对结构体的内容进行修改/不修改。
Example Codes:
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword( pwd string) bool {
return u.password == pwd
}
func (u *user) restPassword( pwd string) {
u.password = pwd
}
func main() {
a := user{name: "NanCheng", password: "123456"}
a.restPassword("2048")
fmt.Println(a.checkPassword("2048"))
/*
true
*/
}
2.12 Go的基础语法 -- 异常处理
我们可以在函数的返回值添加一个
error类型的返回值,如此就标志着该函数可能会返回错误信息。 这种处理方式可以使得我们在处理错误信息上能够明确的知道哪个函数出现了错误,并且能够对错误信息进行清晰的处理。而接收这类函数的返回值,也要添加一个error变量来对错误信息进行处理。
Example Codes:
package main
import (
"errors"
"fmt"
)
type user struct {
name string
pwd 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 that User")
}
func main() {
u, err := findUser([]user{{"NanCheng", "1024"}}, "NanCheng")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) //wang
if u, err := findUser([]user{{"wang", "1234"}}, "li"); err != nil {
fmt.Println(err) //not found that User
return
} else {
fmt.Println(u.name)
}
}
2.13 Go的基础语法 -- 字符串操作
Go语言里的strings标准库里有许多方法对字符串进行操作
| 方法名 | 作用 |
|---|---|
bool Contains(string p1,string p2) | 判断p1里是否包含p2 |
int Count(string p1,string p2) | 寻找p2在p1内出现的次数 |
bool HasPrefix(string p1,string p2) | 判断p1是否以p2开头 |
bool HasSuffix(stirng p1,string p2) | 判断p1是否以p2结尾 |
int Index(string p1,string p2) | 寻找p2在p1内第一次出现的下标 |
string Join([]string p1,string p2) | 连接多个字符串并返回 |
string Repeat(string p1,int p2) | 重复p1字符串p2次并且返回 |
string Replace(string p1,string p2,string p33,int p4) | 返回将p1中前p4个p2字串都替换为p3的新字符串,如果p4为-1,则替换所有 |
[]string Split(string p1,string p2) | 将p1通过正则表达式p2进行分解 |
string ToLower(string p1) | 返回p1的全体小写 |
string ToUpper(string p1) | 返回p1的全体大写 |
int len(string p1) | 返回p1的长度(如果是中文,则会对应多个字符) |
Example Codes:
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, "lo")) //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
}
2.13 Go的基础语法 -- 字符串格式化
Go里面的字符串输出,我们不需要根据类型来选择
%d、%s这种,直接使用%v就可以将值直接打印出,%+v为值的详细信息,%#v为更详细的信息,也可以使用%.2f来打印保留两位小数的浮点数
Example Codes:
package main
import "fmt"
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) //hello 123
fmt.Println(p) //{1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p = {1 2}
fmt.Printf("p=%+v\n", p) // p = {x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1 y:2}
f := 3.131231312
fmt.Println(f) //3.131231312
fmt.Printf("%.2f\n", f) //3.13
}
2.14 Go的基础语法 -- JSON处理
Go里面的JSON操作,对于结构体的属性而言,我们什么都不需要做,只需要保证结构体的属性的第一个字母是大写,然后使用json标准库里的
Marshal方法即可将结构体的数据序列化,打印时,需要使用string()进行字符串转换,否则打印出的是一些16进制的编码。也可以将序列化的JSON数据通过json库里的Unmarshal()进行转化,将数据转换为结构体。
Example Codes:
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string `json:"name"`
Age int `json:"age"`
Hobby []string `json:"hobby"`
}
func main() {
a := userInfo{Name: "NanCheng", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf)
fmt.Println(string(buf))
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)
}
2.15 Go的基础语法 -- 时间处理
我们Go关于时间,有以下几种方法:
time.Now()方法可以获得当前时间time.Date()方法可以以带时区的方式构造一个时间,构造出的时间可以通过t.Year()、t.Mouth()、t.Day()、t.Hour()、t.Minute()等方法获取详细信息。t.Format(string format)可以根据format的格式来格式化时间到字符串,其中format可以是任意一个时间t.Sub(otherT)可以为两个时间做减法time.Parse(string timeFormat,string timeValue)通过TimeFormat模板从字符串timeValue中识别出时间对象time.Unix()获取时间戳
Example Codes:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
t := time.Date(2023, 5, 13, 20, 24, 14, 0, time.UTC)
t2 := time.Date(2023, 5, 13, 23, 12, 14, 0, time.UTC)
fmt.Println(t)
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
fmt.Println(t.Format("2006-01-02 15:04:05"))
diff := t2.Sub(t)
fmt.Println(diff)
fmt.Println(diff.Minutes(), diff.Seconds())
t3, err := time.Parse("2006-01-02 15:04:05", "2023-05-13 20:24:14")
if err != nil {
panic(err)
}
fmt.Println(t3 == t)
fmt.Println(now.Unix())
}
2.15 Go的基础语法 -- 数字解析
Go 的strconv提供了许多方法来转换数字与字符串
(float error) strconv.ParseFloat(string p1,int p2)将p1转换为浮点型数据,p2为位数(int error) strconv.ParseInt(string p1,int p2,int p3)将p1转换为p2进制的int型数据,p3为p1的最大位数<当p3为0时,自动识别>(int error) strconv.Atoi(string p1)快速的将p1转换为int型数据,但是如果有字符就会报错
Example Codes:
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f)
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n)
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n)
n2, _ := strconv.Atoi("123")
fmt.Println(n2)
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err)
}
2.16 Go的基础语法 -- 进程信息
在Go里,我们可以通过
os.Args来获取进程在执行时的一些命令行参数。 也可以通过os.Getenv(string env_name)和os.Setenv(string env_name,string env_value)来获取和设置环境变量。并且可以使用exec.Command()来启动子进程,并且获取其输入输出
Example Codes:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
fmt.Println("进程参数:", os.Args)
fmt.Println(os.Getenv("PATH"))
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf))
}