GO语言基础 | 青训营笔记

87 阅读9分钟

GO语言基础|青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

一、GO语言的特点

1.高性能、高并发

拥有和C++、Java媲美的性能,内含对高并发的支持,只需要使用标准库或者基于标准库开发的第三方库,就可以开发高并发应用程序。

2.语法简单、学习曲线平缓

下面通过实现一个http服务器来简单了解下GO

package main
​
import(
    "net/http"
)
​
func main(){
    http.Handle("/",http.FileServer(http.Dir("."))) //把“/”这个路由指向静态文件
    http.ListenAndServe(":8080",nil)//增添8080端口并启动服务器
}

3.拥有丰富的标准库

很多情况下不需要借用第三方库,就可以完成基础功能的开发,大大降低学习和使用成本,且有很大的稳定性。

4.完善的工具链

代码格式化、错误检查、帮助文档、包管理、代码补充提示、单元测试框架等。

5.静态链接

所有编译结构默认都是静态链接的,只需要拷贝编译后的可执行文件就可部署运行,部署非常方便快捷。

6.快速编译

拥有静态语言里几乎最快的编译速度。

7.跨平台

可以在各种操作系统下运行,也可用来开发IOS,Android软件,含有交叉编译功能,无需配置交叉编译环境。

8.垃圾回收

拥有垃圾回收功能,无需考虑内存的分配释放,可专注于业务逻辑。

二、GO语言基础语法

1.梦开始的地方------------Hello,World!

package main    //表示文件属于main包,也就是程序的入口import (
    "fmt"       //fmt包主要用来输入输出字符串格式化字符串
)
​
func main() {
    fmt.Println("Hello,World!") //调用fmt包内的Println打印hello,world
}
​

程序执行结果如下

1.png

2.变量与常量

  • 变量类型:整型int、无符号整型uint、浮点型float、布尔型boolean、复数complex、字符串string,GO中没有字符类型(char),而是使用byte和rune来代表字符,[]byte也可表示字符串。
  • 变量的声明方式
var e float64       //仅声明变量未赋值
var a = "hello"     //var+变量名=变量的值
var b,c int = 1,2   //var+变量名+变量类型=变量的值
f := float32(e)     //当左边的变量之前没有定义过时,可以使用:=
  • 常量的赋值方式
const a = 100
const b = "abcd"    //var改为const即可,可根据使用的上下文自动确定类型

需要注意的是,go语言中,变量定义之后,必须要使用,也可使用匿名变量,用下划线表示。

3.if else语句

if 9%2 == 0{                    //判断条件不需要用括号括起来
    fmt.Println("9 is even")
}else {
    fmt.Println("9 is odd")
}
if 条件{
    语句
}else{
    语句
}else{
    语句
}

4.for循环

  • 写一个死循环
for {
    fmt,println("hello")
}
  • 普通格式的for循环
for j := 1 ; j < 10 ; j++ {
    fmt.Println(j)
}                               //参考C语言的for循环
  • 另一种条件循环
i ;= 1
for i < 5{
    fmt.Println(i)
    i = i + 1
}
  • 使用break直接结束循环,使用continue后面的语句不再执行跳到下一次循环

5.switch分支结构

与C++、Java中的switch也是十分的相似

switch	a {
	case 1:
		语句	//值得注意的是,与C++不同,这里语句结束并不需要加上break,程序不会自动跑完剩下分支
	case 2:
		语句
	case 3:
		语句
    default:
    	语句
}

go语言的switch功能更加强大,可以使用任意的数据类型作为条件,也可以在switch后面不加判断条件,而是在case后面去写条件分支。

switch {
	case i < 1:
		语句
	default:
		语句
}

6.数组

  • 数组的声明方式
var a [5]int	//意为定义了一个长度为5的整型数组a
b := [5]int{1,2,3,4,5}	//定义的同时为数组赋值
a[3] = 1		//为数组的某一位赋值

由于数组长度固定,在GO中我们更多地使用切片而不是数组

7.切片

切片是一个可变长度的数组,拥有更多丰富的操作,以下是切片创建的方法以及一些操作

s := make([]int, 3)		//创建一个切片s,长度为3
s[0] = 1
s[1] = 2
s[2] = 3
fmt.Println("get:",s[2])		//输出3
fmt.Println("len:",len(s))		//输出3,为s的长度
s = append(s,"4")			//使用append追加元素
s = append(s,"5","6","7")
fmt.Println(s)			//输出结果为[1,2,3,4,5,6,7]

进行切片操作

fmt.Println(s[2:5])  //[3,4,5],左闭右开
fmt.Println(s[:5])	//[1,2,3,4,5]
fmt.Println(s[3:])	//[4,5,6,7]

8.map

定义一个map,类似于python中的字典

m := make(map[string]int)//string对应键,int对应值,后面接{“one”:1,“two”:2}可直接传入键值对
m["one"] = 1
m["two"] = 2
fmt.Println(m["unknow"])		//0
delete(m,"one")			//删除键值对
r,ok :=m["unknow"]		//后面接一个ok来看值是否存在
fmt.Println(r,ok)		//0 false

9.range

可以使用range快速遍历map,返回索引和值,如果不需要索引可用下划线忽略

for k,v :=range m{
    fmt.Println(k,v)	//"one" 1;"two" 2(不分前后顺序)
}
for v :=range m{
    fmt.Println("key",v)	//"key" 1;"key" 2(不分前后顺序)
}

10.函数

与C++十分相似,比较特殊的点在于变量类型是后置的

func add(a int,b int) int{
	return a + b
}

Golang的函数原生支持返回多个值,在实际业务逻辑中,函数不仅会返回值,还会返回一个布尔类型的变量,以表示错误信息

func exists(m map[string]string, k string)(v string, ok bool){
	v, ok = m[k]
	return v, ok
}					//后面括号括起来的表示返回值

11.指针

Golang也支持指针,但是相比C、C++,它支持的操作十分有限,只要作用是修改函数传入的参数。

func add(n *int){
	*n += 2
}

func main(){
    n := 5
    add(&n)
    mt.Println(n)	//结果为7,不使用指针的话结果仍然为5
}

12.结构体

type user struct{
    name string
    password string
}							//定义一个结构体user
func main(){
    a := user{name:"Liu",password:"123"}	//初始化一个结构体变量
    b := user{"Wang","342"}
    C := user{name:"Li"}		//对于为初始化的字段,就为空值
    c.password = "564"			//也可通过这个方法访问其中字段
    var d user					//直接定义一个结构体成员
}

在函数中,通过使用结构体的指针,来改变结构体的内容

func changePassword(u *user){
    u.password = "666"
}

13.结构体方法

定义方法的语法格式:* func (接收者变量 接收者类型) 方法名(参数列表) (返回值列表){ //方法体 }

type user struct{
    name string
    password string
}

func (u user) checkPassword(password string) bool{  //将结构体的传入参数放到了函数名前面的的括号
    return u.password == password 
}

func (u *user) resetPassword(password string) string{  //带指针的结构体方法可以对结构体进行修改
    u.password = password 
}

14.错误处理

GO语言使用一个单独的返回值处理错误信息,可以清晰地知道哪个函数返回了错误,能够用简单的if-else去处理错误。

func finfUser(users []user,name string)(v *user,err error){ //在返回参数的位置,加入一个error
    for _, u := range users{
        if u.name == name{
            return &u,nil
        }
    }
    return nil,errors.New("Not Found")		//通过errors.New返回错误信息
}

15.字符串操作

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,-1表示替换全部字符串,为n(n不等于-1)时替换n个字符串
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
fmt.Println(len("你好"))				//6

16.字符串格式化

type point struct{
    x, y int
}
p := point{1, 2}
fmt.Println(s, n)	//表示打印s和n
fmt.Printf("s=%v\n", s)		//%v表示打印的变量s
fmt.Printf("p=%+v\n", p)	//以键值对的形式进行打印,p={x;1 y:2}
fmt.Printf("p=%#v\n", p)	//先输出结构体名字值,再输出结构体(字段类型+字段的值),p=main.point{x:1,y:2}
fmt.Printf("%.2f\n", f)		//控制浮点数精度

17.Json处理

结构体中,字段名的第一个字母均为大写时,我们便可以使用json.Marshal去对其进行序列化,序列化的结果是一个byte数组,打印时需要将其转换为string类型,否则打印结果将是一些二进制数。

type yserInfo struct {
    Name string
    Age int json:"age"	//这样一来序列化后打印出的是age而不是Age
    Hobby []string
}
a := userInfo{Name:"Liu",Age:"19",Hobby: []string{"Golang","Java"}}	//userInfo为我们定义的结构体
buf, err := json.Marshal(a)		//序列化
if err != nil {
    panic(err)				//panic抛出异常
}
fmt.println(string(buf))

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
// v: 想要编码成json的结构体|map
// prefix: 前缀,通常就设置为""即可。
// indent:缩进,通常设置为制表符\t即可

err = json.Unmarshal(buf,&b)	//反序列化

18.时间处理

now := time.Now()		//获取当前时间
t := time.Date(2022,5,23,2,30,45,0,time,UTC)	//年,月,日,时,分,秒
fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute)
diff = t2.sub(t)	//表示两个时间段相减
t.format("2006-01-02 15:04:05")	//将时间格式化成字符串的样子,该字符串为官方指定
t3,err = time.Parse("2006-01-02 15:04:05","2002-3-13 04:34:12")	//同样使用该字符串,将一个字符串转换为时间
now.Unix()		//获取一个时间戳

19.字符串与数字之间的转换

相关的函数包含在strconv包之中

f,_ := strconv.ParseFloat("2.33",64)		//转换为64精度的float
f,_ := strconv.ParseInt("2123",1064)		//转换为整数--字符串,进制,位数
n,_ := strconv.Atoi("123")					//快速将字符串转为数字

20.进程信息

获取进程相关的一些信息,用到的包有os,os/exec

fmt.Println(os.Args)		//获取进程在执行的时候一些命令行参数
os.Getenv("PATH")			//获取环境变量
os.Setenv("AA","BB")		//设置环境变量
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()	//快速启动子进程

三、学习体会

GO语言,相对于C++和Java简化了不少语法,同时也引入了python中的切片操作,可以说是一门集几家之长的编程语言。 学习的过程中,发现GO语言的变量定义与其他几门语言有一个很显著的区别,那就是GO语言规定了,变量只要被定义了就一定要使用,否则就会报错。不过如果我们也可以使用匿名变量代替之,也就是说以“_”作为变量名。很多时候GO语言中调用的函数返回值并不止一个,还有一个包含错误信息值,当我们不需要某个返回值时,便可以使用匿名变量。